Merge "Refresh cached user array upon lockscreen settings changed" into nyc-dev
diff --git a/Android.mk b/Android.mk
index 59b2a46..ae5d67e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -197,7 +197,6 @@
core/java/android/net/ICaptivePortal.aidl \
core/java/android/net/IConnectivityManager.aidl \
core/java/android/net/IConnectivityMetricsLogger.aidl \
- core/java/android/net/IConnectivityMetricsLoggerSubscriber.aidl \
core/java/android/net/IEthernetManager.aidl \
core/java/android/net/IEthernetServiceListener.aidl \
core/java/android/net/INetworkManagementEventObserver.aidl \
@@ -226,6 +225,7 @@
core/java/android/os/INetworkManagementService.aidl \
core/java/android/os/IPermissionController.aidl \
core/java/android/os/IProcessInfoService.aidl \
+ core/java/android/os/IProgressListener.aidl \
core/java/android/os/IPowerManager.aidl \
core/java/android/os/IRecoverySystem.aidl \
core/java/android/os/IRecoverySystemProgressListener.aidl \
@@ -244,16 +244,21 @@
core/java/android/service/notification/IConditionListener.aidl \
core/java/android/service/notification/IConditionProvider.aidl \
core/java/android/service/vr/IVrListener.aidl \
+ core/java/android/service/vr/IVrManager.aidl \
+ core/java/android/service/vr/IVrStateCallbacks.aidl \
core/java/android/print/ILayoutResultCallback.aidl \
core/java/android/print/IPrinterDiscoveryObserver.aidl \
core/java/android/print/IPrintDocumentAdapter.aidl \
core/java/android/print/IPrintDocumentAdapterObserver.aidl \
core/java/android/print/IPrintJobStateChangeListener.aidl \
core/java/android/print/IPrintServicesChangeListener.aidl \
+ core/java/android/printservice/recommendation/IRecommendationsChangeListener.aidl \
core/java/android/print/IPrintManager.aidl \
core/java/android/print/IPrintSpooler.aidl \
core/java/android/print/IPrintSpoolerCallbacks.aidl \
core/java/android/print/IPrintSpoolerClient.aidl \
+ core/java/android/printservice/recommendation/IRecommendationServiceCallbacks.aidl \
+ core/java/android/printservice/recommendation/IRecommendationService.aidl \
core/java/android/print/IWriteResultCallback.aidl \
core/java/android/printservice/IPrintService.aidl \
core/java/android/printservice/IPrintServiceClient.aidl \
@@ -299,7 +304,6 @@
core/java/com/android/internal/app/IAssistScreenshotReceiver.aidl \
core/java/com/android/internal/app/IBatteryStats.aidl \
core/java/com/android/internal/app/IEphemeralResolver.aidl \
- core/java/com/android/internal/app/IProcessStats.aidl \
core/java/com/android/internal/app/ISoundTriggerService.aidl \
core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl \
core/java/com/android/internal/app/IVoiceInteractionSessionShowCallback.aidl \
@@ -307,6 +311,7 @@
core/java/com/android/internal/app/IVoiceInteractorCallback.aidl \
core/java/com/android/internal/app/IVoiceInteractorRequest.aidl \
core/java/com/android/internal/app/IMediaContainerService.aidl \
+ core/java/com/android/internal/app/procstats/IProcessStats.aidl \
core/java/com/android/internal/appwidget/IAppWidgetService.aidl \
core/java/com/android/internal/appwidget/IAppWidgetHost.aidl \
core/java/com/android/internal/backup/IBackupTransport.aidl \
@@ -564,6 +569,7 @@
frameworks/base/core/java/android/print/PrintJobInfo.aidl \
frameworks/base/core/java/android/print/PrinterInfo.aidl \
frameworks/base/core/java/android/print/PrintJobId.aidl \
+ frameworks/base/core/java/android/printservice/recommendation/RecommendationInfo.aidl \
frameworks/base/core/java/android/hardware/usb/UsbDevice.aidl \
frameworks/base/core/java/android/hardware/usb/UsbInterface.aidl \
frameworks/base/core/java/android/hardware/usb/UsbEndpoint.aidl \
diff --git a/api/current.txt b/api/current.txt
index 95eeeee..f346e0b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -71,6 +71,7 @@
field public static final java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS";
field public static final java.lang.String GET_ACCOUNTS_PRIVILEGED = "android.permission.GET_ACCOUNTS_PRIVILEGED";
field public static final java.lang.String GET_PACKAGE_SIZE = "android.permission.GET_PACKAGE_SIZE";
+ field public static final java.lang.String GET_PASSWORD_PRIVILEGED = "android.permission.GET_PASSWORD_PRIVILEGED";
field public static final deprecated java.lang.String GET_TASKS = "android.permission.GET_TASKS";
field public static final java.lang.String GLOBAL_SEARCH = "android.permission.GLOBAL_SEARCH";
field public static final java.lang.String INSTALL_LOCATION_PROVIDER = "android.permission.INSTALL_LOCATION_PROVIDER";
@@ -416,9 +417,11 @@
field public static final int contentAuthority = 16843408; // 0x1010290
field public static final int contentDescription = 16843379; // 0x1010273
field public static final int contentInsetEnd = 16843860; // 0x1010454
+ field public static final int contentInsetEndWithActions = 16844070; // 0x1010526
field public static final int contentInsetLeft = 16843861; // 0x1010455
field public static final int contentInsetRight = 16843862; // 0x1010456
field public static final int contentInsetStart = 16843859; // 0x1010453
+ field public static final int contentInsetStartWithNavigation = 16844069; // 0x1010525
field public static final int contextClickable = 16844007; // 0x10104e7
field public static final int contextPopupMenuStyle = 16844034; // 0x1010502
field public static final int controlX1 = 16843772; // 0x10103fc
@@ -576,6 +579,7 @@
field public static final int fontFamily = 16843692; // 0x10103ac
field public static final int fontFeatureSettings = 16843959; // 0x10104b7
field public static final int footerDividersEnabled = 16843311; // 0x101022f
+ field public static final int forceHasOverlappingRendering = 16844068; // 0x1010524
field public static final int foreground = 16843017; // 0x1010109
field public static final int foregroundGravity = 16843264; // 0x1010200
field public static final int foregroundTint = 16843885; // 0x101046d
@@ -859,7 +863,8 @@
field public static final int minResizeWidth = 16843669; // 0x1010395
field public static final int minSdkVersion = 16843276; // 0x101020c
field public static final int minWidth = 16843071; // 0x101013f
- field public static final int minimalSize = 16844022; // 0x10104f6
+ field public static final int minimalHeight = 16844067; // 0x1010523
+ field public static final int minimalWidth = 16844022; // 0x10104f6
field public static final int minimumHorizontalAngle = 16843901; // 0x101047d
field public static final int minimumVerticalAngle = 16843902; // 0x101047e
field public static final int mipMap = 16843725; // 0x10103cd
@@ -2480,6 +2485,7 @@
field public static final int Widget_Material_CompoundButton_CheckBox = 16974435; // 0x1030263
field public static final int Widget_Material_CompoundButton_RadioButton = 16974436; // 0x1030264
field public static final int Widget_Material_CompoundButton_Star = 16974437; // 0x1030265
+ field public static final int Widget_Material_CompoundButton_Switch = 16974554; // 0x10302da
field public static final int Widget_Material_DatePicker = 16974438; // 0x1030266
field public static final int Widget_Material_DropDownItem = 16974439; // 0x1030267
field public static final int Widget_Material_DropDownItem_Spinner = 16974440; // 0x1030268
@@ -2514,6 +2520,7 @@
field public static final int Widget_Material_Light_CompoundButton_CheckBox = 16974500; // 0x10302a4
field public static final int Widget_Material_Light_CompoundButton_RadioButton = 16974501; // 0x10302a5
field public static final int Widget_Material_Light_CompoundButton_Star = 16974502; // 0x10302a6
+ field public static final int Widget_Material_Light_CompoundButton_Switch = 16974555; // 0x10302db
field public static final int Widget_Material_Light_DatePicker = 16974503; // 0x10302a7
field public static final int Widget_Material_Light_DropDownItem = 16974504; // 0x10302a8
field public static final int Widget_Material_Light_DropDownItem_Spinner = 16974505; // 0x10302a9
@@ -3421,7 +3428,7 @@
method public boolean dispatchTouchEvent(android.view.MotionEvent);
method public boolean dispatchTrackballEvent(android.view.MotionEvent);
method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
- method public void enterPictureInPicture();
+ method public void enterPictureInPictureMode();
method public android.view.View findViewById(int);
method public void finish();
method public void finishActivity(int);
@@ -3461,15 +3468,16 @@
method public android.view.Window getWindow();
method public android.view.WindowManager getWindowManager();
method public boolean hasWindowFocus();
- method public boolean inMultiWindow();
- method public boolean inPictureInPicture();
method public void invalidateOptionsMenu();
method public boolean isChangingConfigurations();
method public final boolean isChild();
method public boolean isDestroyed();
method public boolean isFinishing();
method public boolean isImmersive();
+ method public boolean isInMultiWindowMode();
+ method public boolean isInPictureInPictureMode();
method public boolean isLocalVoiceInteractionSupported();
+ method public boolean isOverlayWithDecorCaptionEnabled();
method public boolean isTaskRoot();
method public boolean isVoiceInteraction();
method public boolean isVoiceInteractionRoot();
@@ -3516,7 +3524,7 @@
method public void onLowMemory();
method public boolean onMenuItemSelected(int, android.view.MenuItem);
method public boolean onMenuOpened(int, android.view.Menu);
- method public void onMultiWindowChanged(boolean);
+ method public void onMultiWindowModeChanged(boolean);
method public boolean onNavigateUp();
method public boolean onNavigateUpFromChild(android.app.Activity);
method protected void onNewIntent(android.content.Intent);
@@ -3524,7 +3532,7 @@
method public void onOptionsMenuClosed(android.view.Menu);
method public void onPanelClosed(int, android.view.Menu);
method protected void onPause();
- method public void onPictureInPictureChanged(boolean);
+ method public void onPictureInPictureModeChanged(boolean);
method protected void onPostCreate(android.os.Bundle);
method public void onPostCreate(android.os.Bundle, android.os.PersistableBundle);
method protected void onPostResume();
@@ -3562,7 +3570,6 @@
method public android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback, int);
method public void openContextMenu(android.view.View);
method public void openOptionsMenu();
- method public void overlayWithDecorCaption(boolean);
method public void overridePendingTransition(int, int);
method public void postponeEnterTransition();
method public void recreate();
@@ -3591,6 +3598,7 @@
method public void setImmersive(boolean);
method public void setIntent(android.content.Intent);
method public final void setMediaController(android.media.session.MediaController);
+ method public void setOverlayWithDecorCaptionEnabled(boolean);
method public final deprecated void setProgress(int);
method public final deprecated void setProgressBarIndeterminate(boolean);
method public final deprecated void setProgressBarIndeterminateVisibility(boolean);
@@ -4280,6 +4288,7 @@
public class DownloadManager {
method public long addCompletedDownload(java.lang.String, java.lang.String, boolean, java.lang.String, java.lang.String, long, boolean);
+ method public long addCompletedDownload(java.lang.String, java.lang.String, boolean, java.lang.String, java.lang.String, long, boolean, android.net.Uri, android.net.Uri);
method public long enqueue(android.app.DownloadManager.Request);
method public static java.lang.Long getMaxBytesOverMobile(android.content.Context);
method public java.lang.String getMimeTypeForDownloadedFile(long);
@@ -4432,11 +4441,11 @@
method public void onInflate(android.content.Context, android.util.AttributeSet, android.os.Bundle);
method public deprecated void onInflate(android.app.Activity, android.util.AttributeSet, android.os.Bundle);
method public void onLowMemory();
- method public void onMultiWindowChanged(boolean);
+ method public void onMultiWindowModeChanged(boolean);
method public boolean onOptionsItemSelected(android.view.MenuItem);
method public void onOptionsMenuClosed(android.view.Menu);
method public void onPause();
- method public void onPictureInPictureChanged(boolean);
+ method public void onPictureInPictureModeChanged(boolean);
method public void onPrepareOptionsMenu(android.view.Menu);
method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
method public void onResume();
@@ -4517,11 +4526,11 @@
method public void dispatchDestroy();
method public void dispatchDestroyView();
method public void dispatchLowMemory();
- method public void dispatchMultiWindowChanged(boolean);
+ method public void dispatchMultiWindowModeChanged(boolean);
method public boolean dispatchOptionsItemSelected(android.view.MenuItem);
method public void dispatchOptionsMenuClosed(android.view.Menu);
method public void dispatchPause();
- method public void dispatchPictureInPictureChanged(boolean);
+ method public void dispatchPictureInPictureModeChanged(boolean);
method public boolean dispatchPrepareOptionsMenu(android.view.Menu);
method public void dispatchResume();
method public void dispatchStart();
@@ -4970,7 +4979,7 @@
field public int ledARGB;
field public int ledOffMS;
field public int ledOnMS;
- field public int number;
+ field public deprecated int number;
field public int priority;
field public android.app.Notification publicVersion;
field public android.net.Uri sound;
@@ -5017,11 +5026,13 @@
method public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Builder);
method public java.lang.CharSequence getCancelLabel();
method public java.lang.CharSequence getConfirmLabel();
+ method public boolean getHintContentIntentLaunchesActivity();
method public java.lang.CharSequence getInProgressLabel();
method public boolean isAvailableOffline();
method public android.app.Notification.Action.WearableExtender setAvailableOffline(boolean);
method public android.app.Notification.Action.WearableExtender setCancelLabel(java.lang.CharSequence);
method public android.app.Notification.Action.WearableExtender setConfirmLabel(java.lang.CharSequence);
+ method public android.app.Notification.Action.WearableExtender setHintContentIntentLaunchesActivity(boolean);
method public android.app.Notification.Action.WearableExtender setInProgressLabel(java.lang.CharSequence);
}
@@ -5063,7 +5074,7 @@
method public android.app.Notification.Builder setChronometerCountsDown(boolean);
method public android.app.Notification.Builder setColor(int);
method public deprecated android.app.Notification.Builder setContent(android.widget.RemoteViews);
- method public android.app.Notification.Builder setContentInfo(java.lang.CharSequence);
+ method public deprecated android.app.Notification.Builder setContentInfo(java.lang.CharSequence);
method public android.app.Notification.Builder setContentIntent(android.app.PendingIntent);
method public android.app.Notification.Builder setContentText(java.lang.CharSequence);
method public android.app.Notification.Builder setContentTitle(java.lang.CharSequence);
@@ -5080,7 +5091,7 @@
method public android.app.Notification.Builder setLargeIcon(android.graphics.drawable.Icon);
method public android.app.Notification.Builder setLights(int, int, int);
method public android.app.Notification.Builder setLocalOnly(boolean);
- method public android.app.Notification.Builder setNumber(int);
+ method public deprecated android.app.Notification.Builder setNumber(int);
method public android.app.Notification.Builder setOngoing(boolean);
method public android.app.Notification.Builder setOnlyAlertOnce(boolean);
method public android.app.Notification.Builder setPriority(int);
@@ -5198,6 +5209,7 @@
method public android.app.PendingIntent getDisplayIntent();
method public int getGravity();
method public boolean getHintAvoidBackgroundClipping();
+ method public boolean getHintContentIntentLaunchesActivity();
method public boolean getHintHideIcon();
method public int getHintScreenTimeout();
method public boolean getHintShowBackgroundOnly();
@@ -5213,6 +5225,7 @@
method public android.app.Notification.WearableExtender setDisplayIntent(android.app.PendingIntent);
method public android.app.Notification.WearableExtender setGravity(int);
method public android.app.Notification.WearableExtender setHintAvoidBackgroundClipping(boolean);
+ method public android.app.Notification.WearableExtender setHintContentIntentLaunchesActivity(boolean);
method public android.app.Notification.WearableExtender setHintHideIcon(boolean);
method public android.app.Notification.WearableExtender setHintScreenTimeout(int);
method public android.app.Notification.WearableExtender setHintShowBackgroundOnly(boolean);
@@ -5749,8 +5762,8 @@
field public static final java.lang.String COMMAND_SECONDARY_TAP = "android.wallpaper.secondaryTap";
field public static final java.lang.String COMMAND_TAP = "android.wallpaper.tap";
field public static final java.lang.String EXTRA_LIVE_WALLPAPER_COMPONENT = "android.service.wallpaper.extra.LIVE_WALLPAPER_COMPONENT";
- field public static final int FLAG_SET_LOCK = 2; // 0x2
- field public static final int FLAG_SET_SYSTEM = 1; // 0x1
+ field public static final int FLAG_LOCK = 2; // 0x2
+ field public static final int FLAG_SYSTEM = 1; // 0x1
field public static final java.lang.String WALLPAPER_PREVIEW_META_DATA = "android.wallpaper.preview";
}
@@ -5850,7 +5863,7 @@
method public boolean getCrossProfileContactsSearchDisabled(android.content.ComponentName);
method public java.util.List<java.lang.String> getCrossProfileWidgetProviders(android.content.ComponentName);
method public int getCurrentFailedPasswordAttempts();
- method public java.lang.String getDeviceOwnerLockScreenInfo();
+ method public java.lang.CharSequence getDeviceOwnerLockScreenInfo();
method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName);
method public int getKeyguardDisabledFeatures(android.content.ComponentName);
method public java.lang.String getLongSupportMessage(android.content.ComponentName);
@@ -5858,7 +5871,6 @@
method public long getMaximumTimeToLock(android.content.ComponentName);
method public int getOrganizationColor(android.content.ComponentName);
method public java.lang.String getOrganizationName(android.content.ComponentName);
- method public boolean getPackageSuspended(android.content.ComponentName, java.lang.String);
method public android.app.admin.DevicePolicyManager getParentProfileInstance(android.content.ComponentName);
method public long getPasswordExpiration(android.content.ComponentName);
method public long getPasswordExpirationTimeout(android.content.ComponentName);
@@ -5888,14 +5900,16 @@
method public boolean hasGrantedPolicy(android.content.ComponentName, int);
method public boolean installCaCert(android.content.ComponentName, byte[]);
method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate, java.lang.String);
- method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate, java.lang.String, boolean);
+ method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], java.lang.String, boolean);
method public boolean isActivePasswordSufficient();
method public boolean isAdminActive(android.content.ComponentName);
method public boolean isApplicationHidden(android.content.ComponentName, java.lang.String);
method public boolean isCallerApplicationRestrictionsManagingPackage();
method public boolean isDeviceOwnerApp(java.lang.String);
method public boolean isLockTaskPermitted(java.lang.String);
+ method public boolean isManagedProfile(android.content.ComponentName);
method public boolean isMasterVolumeMuted(android.content.ComponentName);
+ method public boolean isPackageSuspended(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
method public boolean isProfileOwnerApp(java.lang.String);
method public boolean isProvisioningAllowed(java.lang.String);
method public boolean isSecurityLoggingEnabled(android.content.ComponentName);
@@ -5911,7 +5925,7 @@
method public java.util.List<android.app.admin.SecurityLog.SecurityEvent> retrievePreRebootSecurityLogs(android.content.ComponentName);
method public java.util.List<android.app.admin.SecurityLog.SecurityEvent> retrieveSecurityLogs(android.content.ComponentName);
method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
- method public boolean setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String);
+ method public void setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException, java.lang.UnsupportedOperationException;
method public boolean setApplicationHidden(android.content.ComponentName, java.lang.String, boolean);
method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
method public void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -5921,7 +5935,7 @@
method public void setCertInstallerPackage(android.content.ComponentName, java.lang.String) throws java.lang.SecurityException;
method public void setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean);
method public void setCrossProfileContactsSearchDisabled(android.content.ComponentName, boolean);
- method public boolean setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.String);
+ method public void setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.CharSequence);
method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public boolean setKeyguardDisabled(android.content.ComponentName, boolean);
method public void setKeyguardDisabledFeatures(android.content.ComponentName, int);
@@ -7875,6 +7889,7 @@
method public static boolean isSyncPending(android.accounts.Account, java.lang.String);
method public void notifyChange(android.net.Uri, android.database.ContentObserver);
method public void notifyChange(android.net.Uri, android.database.ContentObserver, boolean);
+ method public void notifyChange(android.net.Uri, android.database.ContentObserver, int);
method public final android.content.res.AssetFileDescriptor openAssetFileDescriptor(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
method public final android.content.res.AssetFileDescriptor openAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException;
method public final android.os.ParcelFileDescriptor openFileDescriptor(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
@@ -7905,6 +7920,8 @@
field public static final java.lang.String CURSOR_DIR_BASE_TYPE = "vnd.android.cursor.dir";
field public static final java.lang.String CURSOR_ITEM_BASE_TYPE = "vnd.android.cursor.item";
field public static final java.lang.String EXTRA_SIZE = "android.content.extra.SIZE";
+ field public static final int NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS = 2; // 0x2
+ field public static final int NOTIFY_SYNC_TO_NETWORK = 1; // 0x1
field public static final java.lang.String SCHEME_ANDROID_RESOURCE = "android.resource";
field public static final java.lang.String SCHEME_CONTENT = "content";
field public static final java.lang.String SCHEME_FILE = "file";
@@ -8219,8 +8236,6 @@
method public java.lang.String getPackageResourcePath();
method public android.content.res.Resources getResources();
method public android.content.SharedPreferences getSharedPreferences(java.lang.String, int);
- method public android.content.SharedPreferences getSharedPreferences(java.io.File, int);
- method public java.io.File getSharedPreferencesPath(java.lang.String);
method public java.lang.Object getSystemService(java.lang.String);
method public java.lang.String getSystemServiceName(java.lang.Class<?>);
method public android.content.res.Resources.Theme getTheme();
@@ -8515,8 +8530,9 @@
field public static final java.lang.String ACTION_LOCKED_BOOT_COMPLETED = "android.intent.action.LOCKED_BOOT_COMPLETED";
field public static final java.lang.String ACTION_MAIN = "android.intent.action.MAIN";
field public static final java.lang.String ACTION_MANAGED_PROFILE_ADDED = "android.intent.action.MANAGED_PROFILE_ADDED";
- field public static final java.lang.String ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED = "android.intent.action.MANAGED_PROFILE_AVAILABILITY_CHANGED";
+ field public static final java.lang.String ACTION_MANAGED_PROFILE_AVAILABLE = "android.intent.action.MANAGED_PROFILE_AVAILABLE";
field public static final java.lang.String ACTION_MANAGED_PROFILE_REMOVED = "android.intent.action.MANAGED_PROFILE_REMOVED";
+ field public static final java.lang.String ACTION_MANAGED_PROFILE_UNAVAILABLE = "android.intent.action.MANAGED_PROFILE_UNAVAILABLE";
field public static final java.lang.String ACTION_MANAGED_PROFILE_UNLOCKED = "android.intent.action.MANAGED_PROFILE_UNLOCKED";
field public static final java.lang.String ACTION_MANAGE_NETWORK_USAGE = "android.intent.action.MANAGE_NETWORK_USAGE";
field public static final java.lang.String ACTION_MANAGE_PACKAGE_STORAGE = "android.intent.action.MANAGE_PACKAGE_STORAGE";
@@ -9256,7 +9272,6 @@
field public int documentLaunchMode;
field public int flags;
field public int launchMode;
- field public android.content.pm.ActivityInfo.Layout layout;
field public int maxRecents;
field public java.lang.String parentActivityName;
field public java.lang.String permission;
@@ -9267,14 +9282,16 @@
field public java.lang.String taskAffinity;
field public int theme;
field public int uiOptions;
+ field public android.content.pm.ActivityInfo.WindowLayout windowLayout;
}
- public static final class ActivityInfo.Layout {
- ctor public ActivityInfo.Layout(int, float, int, float, int, int);
+ public static final class ActivityInfo.WindowLayout {
+ ctor public ActivityInfo.WindowLayout(int, float, int, float, int, int, int);
field public final int gravity;
field public final int height;
field public final float heightFraction;
- field public final int minimalSize;
+ field public final int minimalHeight;
+ field public final int minimalWidth;
field public final int width;
field public final float widthFraction;
}
@@ -9910,6 +9927,7 @@
field public static final int PROTECTION_FLAG_PRE23 = 128; // 0x80
field public static final int PROTECTION_FLAG_PREINSTALLED = 1024; // 0x400
field public static final int PROTECTION_FLAG_PRIVILEGED = 16; // 0x10
+ field public static final int PROTECTION_FLAG_SETUP = 2048; // 0x800
field public static final deprecated int PROTECTION_FLAG_SYSTEM = 16; // 0x10
field public static final int PROTECTION_FLAG_VERIFIER = 512; // 0x200
field public static final int PROTECTION_MASK_BASE = 15; // 0xf
@@ -9987,7 +10005,7 @@
field public java.lang.String permission;
}
- public class ShortcutInfo implements android.os.Parcelable {
+ public final class ShortcutInfo implements android.os.Parcelable {
method public int describeContents();
method public android.content.ComponentName getActivityComponent();
method public android.os.PersistableBundle getExtras();
@@ -9995,6 +10013,7 @@
method public android.content.Intent getIntent();
method public long getLastChangedTimestamp();
method public java.lang.String getPackageName();
+ method public java.lang.String getText();
method public java.lang.String getTitle();
method public int getWeight();
method public boolean hasIconFile();
@@ -10022,6 +10041,7 @@
method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon);
method public android.content.pm.ShortcutInfo.Builder setId(java.lang.String);
method public android.content.pm.ShortcutInfo.Builder setIntent(android.content.Intent);
+ method public android.content.pm.ShortcutInfo.Builder setText(java.lang.String);
method public android.content.pm.ShortcutInfo.Builder setTitle(java.lang.String);
method public android.content.pm.ShortcutInfo.Builder setWeight(int);
}
@@ -13496,6 +13516,7 @@
public final class Sensor {
method public int getFifoMaxEventCount();
method public int getFifoReservedEventCount();
+ method public int getId();
method public int getMaxDelay();
method public float getMaximumRange();
method public int getMinDelay();
@@ -13505,9 +13526,10 @@
method public float getResolution();
method public java.lang.String getStringType();
method public int getType();
- method public java.util.UUID getUuid();
method public java.lang.String getVendor();
method public int getVersion();
+ method public boolean isAdditionalInfoSupported();
+ method public boolean isDynamicSensor();
method public boolean isWakeUpSensor();
field public static final int REPORTING_MODE_CONTINUOUS = 0; // 0x0
field public static final int REPORTING_MODE_ONE_SHOT = 2; // 0x2
@@ -13626,8 +13648,9 @@
method public static void getRotationMatrixFromVector(float[], float[]);
method public java.util.List<android.hardware.Sensor> getSensorList(int);
method public deprecated int getSensors();
- method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback);
- method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback, android.os.Handler);
+ method public boolean isDynamicSensorDiscoverySupported();
+ method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorCallback);
+ method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorCallback, android.os.Handler);
method public deprecated boolean registerListener(android.hardware.SensorListener, int);
method public deprecated boolean registerListener(android.hardware.SensorListener, int, int);
method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int);
@@ -13636,7 +13659,7 @@
method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int, int, android.os.Handler);
method public static boolean remapCoordinateSystem(float[], int, int, float[]);
method public boolean requestTriggerSensor(android.hardware.TriggerEventListener, android.hardware.Sensor);
- method public void unregisterDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback);
+ method public void unregisterDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorCallback);
method public deprecated void unregisterListener(android.hardware.SensorListener);
method public deprecated void unregisterListener(android.hardware.SensorListener, int);
method public void unregisterListener(android.hardware.SensorEventListener, android.hardware.Sensor);
@@ -13701,8 +13724,8 @@
field public static final float STANDARD_GRAVITY = 9.80665f;
}
- public static abstract class SensorManager.DynamicSensorConnectionCallback {
- ctor public SensorManager.DynamicSensorConnectionCallback();
+ public static abstract class SensorManager.DynamicSensorCallback {
+ ctor public SensorManager.DynamicSensorCallback();
method public void onDynamicSensorConnected(android.hardware.Sensor);
method public void onDynamicSensorDisconnected(android.hardware.Sensor);
}
@@ -14827,7 +14850,6 @@
public static abstract interface UCharacter.BidiPairedBracketType {
field public static final int CLOSE = 2; // 0x2
- field public static final int COUNT = 3; // 0x3
field public static final int NONE = 0; // 0x0
field public static final int OPEN = 1; // 0x1
}
@@ -14836,7 +14858,6 @@
field public static final int CANONICAL = 1; // 0x1
field public static final int CIRCLE = 3; // 0x3
field public static final int COMPAT = 2; // 0x2
- field public static final int COUNT = 18; // 0x12
field public static final int FINAL = 4; // 0x4
field public static final int FONT = 5; // 0x5
field public static final int FRACTION = 6; // 0x6
@@ -14856,7 +14877,6 @@
public static abstract interface UCharacter.EastAsianWidth {
field public static final int AMBIGUOUS = 1; // 0x1
- field public static final int COUNT = 6; // 0x6
field public static final int FULLWIDTH = 3; // 0x3
field public static final int HALFWIDTH = 2; // 0x2
field public static final int NARROW = 4; // 0x4
@@ -14866,7 +14886,6 @@
public static abstract interface UCharacter.GraphemeClusterBreak {
field public static final int CONTROL = 1; // 0x1
- field public static final int COUNT = 13; // 0xd
field public static final int CR = 2; // 0x2
field public static final int EXTEND = 3; // 0x3
field public static final int L = 4; // 0x4
@@ -14882,7 +14901,6 @@
}
public static abstract interface UCharacter.HangulSyllableType {
- field public static final int COUNT = 6; // 0x6
field public static final int LEADING_JAMO = 1; // 0x1
field public static final int LVT_SYLLABLE = 5; // 0x5
field public static final int LV_SYLLABLE = 4; // 0x4
@@ -14898,7 +14916,6 @@
field public static final int BEH = 4; // 0x4
field public static final int BETH = 5; // 0x5
field public static final int BURUSHASKI_YEH_BARREE = 54; // 0x36
- field public static final int COUNT = 86; // 0x56
field public static final int DAL = 6; // 0x6
field public static final int DALATH_RISH = 7; // 0x7
field public static final int E = 8; // 0x8
@@ -14983,7 +15000,6 @@
}
public static abstract interface UCharacter.JoiningType {
- field public static final int COUNT = 6; // 0x6
field public static final int DUAL_JOINING = 2; // 0x2
field public static final int JOIN_CAUSING = 1; // 0x1
field public static final int LEFT_JOINING = 3; // 0x3
@@ -15006,7 +15022,6 @@
field public static final int COMPLEX_CONTEXT = 24; // 0x18
field public static final int CONDITIONAL_JAPANESE_STARTER = 37; // 0x25
field public static final int CONTINGENT_BREAK = 7; // 0x7
- field public static final int COUNT = 40; // 0x28
field public static final int EXCLAMATION = 11; // 0xb
field public static final int GLUE = 12; // 0xc
field public static final int H2 = 31; // 0x1f
@@ -15038,7 +15053,6 @@
}
public static abstract interface UCharacter.NumericType {
- field public static final int COUNT = 4; // 0x4
field public static final int DECIMAL = 1; // 0x1
field public static final int DIGIT = 2; // 0x2
field public static final int NONE = 0; // 0x0
@@ -15048,7 +15062,6 @@
public static abstract interface UCharacter.SentenceBreak {
field public static final int ATERM = 1; // 0x1
field public static final int CLOSE = 2; // 0x2
- field public static final int COUNT = 15; // 0xf
field public static final int CR = 11; // 0xb
field public static final int EXTEND = 12; // 0xc
field public static final int FORMAT = 3; // 0x3
@@ -15191,7 +15204,6 @@
field public static final android.icu.lang.UCharacter.UnicodeBlock COPTIC_EPACT_NUMBERS;
field public static final int COPTIC_EPACT_NUMBERS_ID = 223; // 0xdf
field public static final int COPTIC_ID = 132; // 0x84
- field public static final int COUNT = 263; // 0x107
field public static final android.icu.lang.UCharacter.UnicodeBlock COUNTING_ROD_NUMERALS;
field public static final int COUNTING_ROD_NUMERALS_ID = 154; // 0x9a
field public static final android.icu.lang.UCharacter.UnicodeBlock CUNEIFORM;
@@ -15605,7 +15617,6 @@
public static abstract interface UCharacter.WordBreak {
field public static final int ALETTER = 1; // 0x1
- field public static final int COUNT = 17; // 0x11
field public static final int CR = 8; // 0x8
field public static final int DOUBLE_QUOTE = 16; // 0x10
field public static final int EXTEND = 9; // 0x9
@@ -15636,7 +15647,6 @@
}
public static abstract interface UCharacterEnums.ECharacterCategory {
- field public static final byte CHAR_CATEGORY_COUNT = 30; // 0x1e
field public static final byte COMBINING_SPACING_MARK = 8; // 0x8
field public static final byte CONNECTOR_PUNCTUATION = 22; // 0x16
field public static final byte CONTROL = 15; // 0xf
@@ -15676,7 +15686,6 @@
field public static final int ARABIC_NUMBER = 5; // 0x5
field public static final int BLOCK_SEPARATOR = 7; // 0x7
field public static final int BOUNDARY_NEUTRAL = 18; // 0x12
- field public static final int CHAR_DIRECTION_COUNT = 23; // 0x17
field public static final int COMMON_NUMBER_SEPARATOR = 6; // 0x6
field public static final byte DIRECTIONALITY_ARABIC_NUMBER = 5; // 0x5
field public static final byte DIRECTIONALITY_BOUNDARY_NEUTRAL = 18; // 0x12
@@ -15729,7 +15738,6 @@
field public static final int BIDI_MIRRORING_GLYPH = 16385; // 0x4001
field public static final int BIDI_PAIRED_BRACKET = 16397; // 0x400d
field public static final int BIDI_PAIRED_BRACKET_TYPE = 4117; // 0x1015
- field public static final int BINARY_LIMIT = 57; // 0x39
field public static final int BINARY_START = 0; // 0x0
field public static final int BLOCK = 4097; // 0x1001
field public static final int CANONICAL_COMBINING_CLASS = 4098; // 0x1002
@@ -15748,7 +15756,6 @@
field public static final int DEFAULT_IGNORABLE_CODE_POINT = 5; // 0x5
field public static final int DEPRECATED = 6; // 0x6
field public static final int DIACRITIC = 7; // 0x7
- field public static final int DOUBLE_LIMIT = 12289; // 0x3001
field public static final int DOUBLE_START = 12288; // 0x3000
field public static final int EAST_ASIAN_WIDTH = 4100; // 0x1004
field public static final int EXTENDER = 8; // 0x8
@@ -15767,7 +15774,6 @@
field public static final int IDS_TRINARY_OPERATOR = 19; // 0x13
field public static final int ID_CONTINUE = 15; // 0xf
field public static final int ID_START = 16; // 0x10
- field public static final int INT_LIMIT = 4118; // 0x1016
field public static final int INT_START = 4096; // 0x1000
field public static final int JOINING_GROUP = 4102; // 0x1006
field public static final int JOINING_TYPE = 4103; // 0x1007
@@ -15777,7 +15783,6 @@
field public static final int LOGICAL_ORDER_EXCEPTION = 21; // 0x15
field public static final int LOWERCASE = 22; // 0x16
field public static final int LOWERCASE_MAPPING = 16388; // 0x4004
- field public static final int MASK_LIMIT = 8193; // 0x2001
field public static final int MASK_START = 8192; // 0x2000
field public static final int MATH = 23; // 0x17
field public static final int NAME = 16389; // 0x4005
@@ -15792,7 +15797,6 @@
field public static final int NONCHARACTER_CODE_POINT = 24; // 0x18
field public static final int NUMERIC_TYPE = 4105; // 0x1009
field public static final int NUMERIC_VALUE = 12288; // 0x3000
- field public static final int OTHER_PROPERTY_LIMIT = 28673; // 0x7001
field public static final int OTHER_PROPERTY_START = 28672; // 0x7000
field public static final int PATTERN_SYNTAX = 42; // 0x2a
field public static final int PATTERN_WHITE_SPACE = 43; // 0x2b
@@ -15812,7 +15816,6 @@
field public static final int SIMPLE_TITLECASE_MAPPING = 16392; // 0x4008
field public static final int SIMPLE_UPPERCASE_MAPPING = 16393; // 0x4009
field public static final int SOFT_DOTTED = 27; // 0x1b
- field public static final int STRING_LIMIT = 16398; // 0x400e
field public static final int STRING_START = 16384; // 0x4000
field public static final int S_TERM = 35; // 0x23
field public static final int TERMINAL_PUNCTUATION = 28; // 0x1c
@@ -15829,7 +15832,6 @@
}
public static abstract interface UProperty.NameChoice {
- field public static final int COUNT = 2; // 0x2
field public static final int LONG = 1; // 0x1
field public static final int SHORT = 0; // 0x0
}
@@ -15874,7 +15876,6 @@
field public static final int CHAM = 66; // 0x42
field public static final int CHEROKEE = 6; // 0x6
field public static final int CIRTH = 67; // 0x43
- field public static final int CODE_LIMIT = 167; // 0xa7
field public static final int COMMON = 0; // 0x0
field public static final int COPTIC = 7; // 0x7
field public static final int CUNEIFORM = 101; // 0x65
@@ -16269,7 +16270,6 @@
public final class CollationKey implements java.lang.Comparable {
ctor public CollationKey(java.lang.String, byte[]);
- ctor public CollationKey(java.lang.String, android.icu.text.RawCollationKey);
method public int compareTo(android.icu.text.CollationKey);
method public boolean equals(android.icu.text.CollationKey);
method public android.icu.text.CollationKey getBound(int, int);
@@ -16279,7 +16279,6 @@
}
public static final class CollationKey.BoundMode {
- field public static final int COUNT = 3; // 0x3
field public static final int LOWER = 0; // 0x0
field public static final int UPPER = 1; // 0x1
field public static final int UPPER_LONG = 2; // 0x2
@@ -16311,7 +16310,6 @@
method public static final java.lang.String[] getKeywordValuesForLocale(java.lang.String, android.icu.util.ULocale, boolean);
method public static final java.lang.String[] getKeywords();
method public int getMaxVariable();
- method public abstract android.icu.text.RawCollationKey getRawCollationKey(java.lang.String, android.icu.text.RawCollationKey);
method public int[] getReorderCodes();
method public int getStrength();
method public android.icu.text.UnicodeSet getTailoredSet();
@@ -16351,7 +16349,6 @@
field public static final int DEFAULT = -1; // 0xffffffff
field public static final int DIGIT = 4100; // 0x1004
field public static final int FIRST = 4096; // 0x1000
- field public static final int LIMIT = 4101; // 0x1005
field public static final int NONE = 103; // 0x67
field public static final int OTHERS = 103; // 0x67
field public static final int PUNCTUATION = 4097; // 0x1001
@@ -16464,7 +16461,6 @@
field public static final int DOW_LOCAL_FIELD = 19; // 0x13
field public static final int ERA_FIELD = 0; // 0x0
field public static final int EXTENDED_YEAR_FIELD = 20; // 0x14
- field public static final int FIELD_COUNT = 36; // 0x24
field public static final int FRACTIONAL_SECOND_FIELD = 8; // 0x8
field public static final int FULL = 0; // 0x0
field public static final java.lang.String GENERIC_TZ = "vvvv";
@@ -16708,7 +16704,6 @@
field public static final int MONTH = 3; // 0x3
field public static final int QUARTER = 2; // 0x2
field public static final int SECOND = 13; // 0xd
- field public static final int TYPE_LIMIT = 16; // 0x10
field public static final int WEEKDAY = 6; // 0x6
field public static final int WEEK_OF_MONTH = 5; // 0x5
field public static final int WEEK_OF_YEAR = 4; // 0x4
@@ -17339,14 +17334,6 @@
enum_constant public static final android.icu.text.PluralRules.PluralType ORDINAL;
}
- public final class RawCollationKey extends android.icu.util.ByteArrayWrapper {
- ctor public RawCollationKey();
- ctor public RawCollationKey(int);
- ctor public RawCollationKey(byte[]);
- ctor public RawCollationKey(byte[], int);
- method public int compareTo(android.icu.text.RawCollationKey);
- }
-
public final class RelativeDateTimeFormatter {
method public java.lang.String combineDateAndTime(java.lang.String, java.lang.String);
method public java.lang.String format(double, android.icu.text.RelativeDateTimeFormatter.Direction, android.icu.text.RelativeDateTimeFormatter.RelativeUnit);
@@ -17430,7 +17417,6 @@
method public android.icu.text.CollationKey getCollationKey(java.lang.String);
method public void getContractionsAndExpansions(android.icu.text.UnicodeSet, android.icu.text.UnicodeSet, boolean) throws java.lang.Exception;
method public boolean getNumericCollation();
- method public android.icu.text.RawCollationKey getRawCollationKey(java.lang.String, android.icu.text.RawCollationKey);
method public java.lang.String getRules();
method public java.lang.String getRules(boolean);
method public android.icu.util.VersionInfo getUCAVersion();
@@ -17925,19 +17911,6 @@
field public static final int BE = 0; // 0x0
}
- public class ByteArrayWrapper implements java.lang.Comparable {
- ctor public ByteArrayWrapper();
- ctor public ByteArrayWrapper(byte[], int);
- ctor public ByteArrayWrapper(java.nio.ByteBuffer);
- method public final android.icu.util.ByteArrayWrapper append(byte[], int, int);
- method public int compareTo(android.icu.util.ByteArrayWrapper);
- method public android.icu.util.ByteArrayWrapper ensureCapacity(int);
- method public final byte[] releaseBytes();
- method public final android.icu.util.ByteArrayWrapper set(byte[], int, int);
- field public byte[] bytes;
- field public int size;
- }
-
abstract class CECalendar extends android.icu.util.Calendar {
ctor protected CECalendar();
ctor protected CECalendar(android.icu.util.TimeZone);
@@ -19250,10 +19223,19 @@
field public static final int MULTIPATH_INDICATOR_DETECTED = 1; // 0x1
field public static final int MULTIPATH_INDICATOR_NOT_USED = 2; // 0x2
field public static final int MULTIPATH_INDICATOR_UNKNOWN = 0; // 0x0
+ field public static final int STATE_BDS_D2_BIT_SYNC = 256; // 0x100
+ field public static final int STATE_BDS_D2_SUBFRAME_SYNC = 512; // 0x200
field public static final int STATE_BIT_SYNC = 2; // 0x2
field public static final int STATE_CODE_LOCK = 1; // 0x1
+ field public static final int STATE_GAL_E1BC_CODE_LOCK = 1024; // 0x400
+ field public static final int STATE_GAL_E1B_PAGE_SYNC = 4096; // 0x1000
+ field public static final int STATE_GAL_E1C_2ND_CODE_LOCK = 2048; // 0x800
+ field public static final int STATE_GLO_STRING_SYNC = 64; // 0x40
+ field public static final int STATE_GLO_TOD_DECODED = 128; // 0x80
field public static final int STATE_MSEC_AMBIGUOUS = 16; // 0x10
+ field public static final int STATE_SBAS_SYNC = 8192; // 0x2000
field public static final int STATE_SUBFRAME_SYNC = 4; // 0x4
+ field public static final int STATE_SYMBOL_SYNC = 32; // 0x20
field public static final int STATE_TOW_DECODED = 8; // 0x8
field public static final int STATE_UNKNOWN = 0; // 0x0
}
@@ -19840,7 +19822,7 @@
public static abstract class AudioManager.AudioRecordingCallback {
ctor public AudioManager.AudioRecordingCallback();
- method public void onRecordConfigChanged(android.media.AudioRecordingConfiguration[]);
+ method public void onRecordingConfigChanged(android.media.AudioRecordingConfiguration[]);
}
public static abstract interface AudioManager.OnAudioFocusChangeListener {
@@ -20098,7 +20080,6 @@
}
public abstract class DrmInitData {
- ctor public DrmInitData();
method public abstract android.media.DrmInitData.SchemeInitData get(java.util.UUID);
}
@@ -20117,6 +20098,7 @@
method public int getAttributeInt(java.lang.String, int);
method public boolean getLatLong(float[]);
method public byte[] getThumbnail();
+ method public long[] getThumbnailRange();
method public boolean hasThumbnail();
method public void saveAttributes() throws java.io.IOException;
method public void setAttribute(java.lang.String, java.lang.String);
@@ -20129,7 +20111,7 @@
field public static final int ORIENTATION_TRANSPOSE = 5; // 0x5
field public static final int ORIENTATION_TRANSVERSE = 7; // 0x7
field public static final int ORIENTATION_UNDEFINED = 0; // 0x0
- field public static final java.lang.String TAG_APERTURE = "FNumber";
+ field public static final deprecated java.lang.String TAG_APERTURE = "FNumber";
field public static final java.lang.String TAG_APERTURE_VALUE = "ApertureValue";
field public static final java.lang.String TAG_ARTIST = "Artist";
field public static final java.lang.String TAG_BITS_PER_SAMPLE = "BitsPerSample";
@@ -20200,7 +20182,7 @@
field public static final java.lang.String TAG_IMAGE_UNIQUE_ID = "ImageUniqueID";
field public static final java.lang.String TAG_IMAGE_WIDTH = "ImageWidth";
field public static final java.lang.String TAG_INTEROPERABILITY_INDEX = "InteroperabilityIndex";
- field public static final java.lang.String TAG_ISO = "ISOSpeedRatings";
+ field public static final deprecated java.lang.String TAG_ISO = "ISOSpeedRatings";
field public static final java.lang.String TAG_ISO_SPEED_RATINGS = "ISOSpeedRatings";
field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT = "JPEGInterchangeFormat";
field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = "JPEGInterchangeFormatLength";
@@ -20352,8 +20334,8 @@
public class MediaActionSound {
ctor public MediaActionSound();
- method public synchronized void load(int);
- method public synchronized void play(int);
+ method public void load(int);
+ method public void play(int);
method public void release();
field public static final int FOCUS_COMPLETE = 1; // 0x1
field public static final int SHUTTER_CLICK = 0; // 0x0
@@ -20608,12 +20590,13 @@
field public static final int DolbyVisionLevelUhd30 = 64; // 0x40
field public static final int DolbyVisionLevelUhd48 = 128; // 0x80
field public static final int DolbyVisionLevelUhd60 = 256; // 0x100
- field public static final int DolbyVisionProfileDvavDen = 2; // 0x2
- field public static final int DolbyVisionProfileDvavDer = 1; // 0x1
- field public static final int DolbyVisionProfileDvheDen = 4; // 0x4
- field public static final int DolbyVisionProfileDvheDer = 3; // 0x3
- field public static final int DolbyVisionProfileDvheDtr = 5; // 0x5
- field public static final int DolbyVisionProfileDvheStn = 6; // 0x6
+ field public static final int DolbyVisionProfileDvavPen = 2; // 0x2
+ field public static final int DolbyVisionProfileDvavPer = 1; // 0x1
+ field public static final int DolbyVisionProfileDvheDen = 8; // 0x8
+ field public static final int DolbyVisionProfileDvheDer = 4; // 0x4
+ field public static final int DolbyVisionProfileDvheDth = 64; // 0x40
+ field public static final int DolbyVisionProfileDvheDtr = 16; // 0x10
+ field public static final int DolbyVisionProfileDvheStn = 32; // 0x20
field public static final int H263Level10 = 1; // 0x1
field public static final int H263Level20 = 2; // 0x2
field public static final int H263Level30 = 4; // 0x4
@@ -22280,7 +22263,7 @@
method public void subscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback);
method public void subscribe(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SubscriptionCallback);
method public void unsubscribe(java.lang.String);
- method public void unsubscribe(java.lang.String, android.os.Bundle);
+ method public void unsubscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback);
field public static final java.lang.String EXTRA_PAGE = "android.media.browse.extra.PAGE";
field public static final java.lang.String EXTRA_PAGE_SIZE = "android.media.browse.extra.PAGE_SIZE";
}
@@ -23053,6 +23036,7 @@
method public abstract void onStartRecording(android.net.Uri);
method public abstract void onStopRecording();
method public abstract void onTune(android.net.Uri);
+ method public void onTune(android.net.Uri, android.os.Bundle);
}
public static abstract class TvInputService.Session implements android.view.KeyEvent.Callback {
@@ -23102,6 +23086,7 @@
method public void startRecording(android.net.Uri);
method public void stopRecording();
method public void tune(java.lang.String, android.net.Uri);
+ method public void tune(java.lang.String, android.net.Uri, android.os.Bundle);
}
public static abstract class TvRecordingClient.RecordingCallback {
@@ -23473,6 +23458,7 @@
method public boolean isActiveNetworkMetered();
method public boolean isDefaultNetworkActive();
method public static deprecated boolean isNetworkTypeValid(int);
+ method public void registerDefaultNetworkCallback(android.net.ConnectivityManager.NetworkCallback);
method public void registerNetworkCallback(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
method public void registerNetworkCallback(android.net.NetworkRequest, android.app.PendingIntent);
method public void releaseNetworkRequest(android.app.PendingIntent);
@@ -28738,6 +28724,7 @@
field public static final int TEMPERATURE_CURRENT = 0; // 0x0
field public static final int TEMPERATURE_SHUTDOWN = 2; // 0x2
field public static final int TEMPERATURE_THROTTLING = 1; // 0x1
+ field public static final int TEMPERATURE_THROTTLING_BELOW_VR_MIN = 3; // 0x3
field public static final float UNDEFINED_TEMPERATURE = -3.4028235E38f;
}
@@ -29097,6 +29084,7 @@
method public boolean isInteractive();
method public boolean isPowerSaveMode();
method public deprecated boolean isScreenOn();
+ method public boolean isSustainedPerformanceModeSupported();
method public boolean isWakeLockLevelSupported(int);
method public android.os.PowerManager.WakeLock newWakeLock(int, java.lang.String);
method public void reboot(java.lang.String);
@@ -29110,6 +29098,7 @@
field public static final int RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY = 1; // 0x1
field public static final deprecated int SCREEN_BRIGHT_WAKE_LOCK = 10; // 0xa
field public static final deprecated int SCREEN_DIM_WAKE_LOCK = 6; // 0x6
+ field public static final int SUSTAINED_PERFORMANCE_WAKE_LOCK = 256; // 0x100
}
public final class PowerManager.WakeLock {
@@ -29466,7 +29455,7 @@
method public android.os.health.HealthStats[] takeUidSnapshots(int[]);
}
- public class TimerStat implements android.os.Parcelable {
+ public final class TimerStat implements android.os.Parcelable {
ctor public TimerStat();
ctor public TimerStat(int, long);
ctor public TimerStat(android.os.Parcel);
@@ -29564,8 +29553,8 @@
public class StorageManager {
method public java.lang.String getMountedObbPath(java.lang.String);
- method public android.os.storage.StorageVolume getPrimaryVolume();
- method public android.os.storage.StorageVolume[] getVolumeList();
+ method public android.os.storage.StorageVolume getPrimaryStorageVolume();
+ method public java.util.List<android.os.storage.StorageVolume> getStorageVolumes();
method public boolean isEncrypted(java.io.File);
method public boolean isObbMounted(java.lang.String);
method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener);
@@ -29722,7 +29711,6 @@
method protected void onClick();
method protected android.view.View onCreateView(android.view.ViewGroup);
method public void onDependencyChanged(android.preference.Preference, boolean);
- method protected void onDetachedFromActivity();
method protected java.lang.Object onGetDefaultValue(android.content.res.TypedArray, int);
method public void onParentChanged(android.preference.Preference, boolean);
method protected void onPrepareForRemoval();
@@ -29893,6 +29881,8 @@
method public android.content.SharedPreferences getSharedPreferences();
method public int getSharedPreferencesMode();
method public java.lang.String getSharedPreferencesName();
+ method public boolean isStorageDefault();
+ method public boolean isStorageDeviceProtected();
method public static void setDefaultValues(android.content.Context, int, boolean);
method public static void setDefaultValues(android.content.Context, java.lang.String, int, int, boolean);
method public void setSharedPreferencesMode(int);
@@ -30268,7 +30258,7 @@
method public android.print.PrinterInfo build();
method public android.print.PrinterInfo.Builder setCapabilities(android.print.PrinterCapabilitiesInfo);
method public android.print.PrinterInfo.Builder setDescription(java.lang.String);
- method public android.print.PrinterInfo.Builder setHasCustomPrinterIcon();
+ method public android.print.PrinterInfo.Builder setHasCustomPrinterIcon(boolean);
method public android.print.PrinterInfo.Builder setIconResourceId(int);
method public android.print.PrinterInfo.Builder setInfoIntent(android.app.PendingIntent);
method public android.print.PrinterInfo.Builder setName(java.lang.String);
@@ -30320,6 +30310,7 @@
method public boolean isStarted();
method public void setProgress(float);
method public void setStatus(java.lang.CharSequence);
+ method public void setStatus(int);
method public boolean setTag(java.lang.String);
method public boolean start();
}
@@ -30350,7 +30341,7 @@
method public final boolean isDestroyed();
method public final boolean isPrinterDiscoveryStarted();
method public abstract void onDestroy();
- method public void onRequestCustomPrinterIcon(android.print.PrinterId, android.printservice.CustomPrinterIconCallback);
+ method public void onRequestCustomPrinterIcon(android.print.PrinterId, android.os.CancellationSignal, android.printservice.CustomPrinterIconCallback);
method public abstract void onStartPrinterDiscovery(java.util.List<android.print.PrinterId>);
method public abstract void onStartPrinterStateTracking(android.print.PrinterId);
method public abstract void onStopPrinterDiscovery();
@@ -30396,6 +30387,7 @@
public class BlockedNumberContract {
method public static boolean canCurrentUserBlockNumbers(android.content.Context);
method public static boolean isBlocked(android.content.Context, java.lang.String);
+ method public static int unblock(android.content.Context, java.lang.String);
field public static final java.lang.String AUTHORITY = "com.android.blockednumber";
field public static final android.net.Uri AUTHORITY_URI;
}
@@ -30755,6 +30747,7 @@
field public static final int REJECTED_TYPE = 5; // 0x5
field public static final java.lang.String TRANSCRIPTION = "transcription";
field public static final java.lang.String TYPE = "type";
+ field public static final java.lang.String VIA_NUMBER = "via_number";
field public static final int VOICEMAIL_TYPE = 4; // 0x4
field public static final java.lang.String VOICEMAIL_URI = "voicemail_uri";
}
@@ -31905,6 +31898,7 @@
field public static final java.lang.String EXTRA_EXCLUDE_SELF = "android.provider.extra.EXCLUDE_SELF";
field public static final java.lang.String EXTRA_INFO = "info";
field public static final java.lang.String EXTRA_LOADING = "loading";
+ field public static final java.lang.String EXTRA_ORIENTATION = "android.provider.extra.ORIENTATION";
field public static final java.lang.String EXTRA_PROMPT = "android.provider.extra.PROMPT";
field public static final java.lang.String PROVIDER_INTERFACE = "android.content.action.DOCUMENTS_PROVIDER";
}
@@ -32388,6 +32382,7 @@
field public static final java.lang.String ACTION_VOICE_CONTROL_BATTERY_SAVER_MODE = "android.settings.VOICE_CONTROL_BATTERY_SAVER_MODE";
field public static final java.lang.String ACTION_VOICE_CONTROL_DO_NOT_DISTURB_MODE = "android.settings.VOICE_CONTROL_DO_NOT_DISTURB_MODE";
field public static final java.lang.String ACTION_VOICE_INPUT_SETTINGS = "android.settings.VOICE_INPUT_SETTINGS";
+ field public static final java.lang.String ACTION_VPN_SETTINGS = "android.settings.VPN_SETTINGS";
field public static final java.lang.String ACTION_VR_LISTENER_SETTINGS = "android.settings.VR_LISTENER_SETTINGS";
field public static final java.lang.String ACTION_WIFI_IP_SETTINGS = "android.settings.WIFI_IP_SETTINGS";
field public static final java.lang.String ACTION_WIFI_SETTINGS = "android.settings.WIFI_SETTINGS";
@@ -34577,7 +34572,6 @@
method public boolean onMenuOpened(int, android.view.Menu);
method public void onPanelClosed(int, android.view.Menu);
method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
- method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu, int);
method public boolean onSearchRequested(android.view.SearchEvent);
method public boolean onSearchRequested();
method public void onWakeUp();
@@ -36141,6 +36135,7 @@
method public final android.telecom.CallAudioState getCallAudioState();
method public final java.util.List<android.telecom.Connection> getConferenceableConnections();
method public final int getConnectionCapabilities();
+ method public final int getConnectionProperties();
method public final long getConnectionTime();
method public final java.util.List<android.telecom.Connection> getConnections();
method public final android.telecom.DisconnectCause getDisconnectCause();
@@ -36168,6 +36163,7 @@
method public final void setActive();
method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
method public final void setConnectionCapabilities(int);
+ method public final void setConnectionProperties(int);
method public final void setConnectionTime(long);
method public final void setDialing();
method public final void setDisconnected(android.telecom.DisconnectCause);
@@ -36197,6 +36193,7 @@
method public final android.telecom.Conference getConference();
method public final java.util.List<android.telecom.Conferenceable> getConferenceables();
method public final int getConnectionCapabilities();
+ method public final int getConnectionProperties();
method public final android.telecom.DisconnectCause getDisconnectCause();
method public final android.os.Bundle getExtras();
method public final int getState();
@@ -36220,6 +36217,7 @@
method public void onStateChanged(int);
method public void onStopDtmfTone();
method public void onUnhold();
+ method public static java.lang.String propertiesToString(int);
method public final void putExtras(android.os.Bundle);
method public final void removeExtras(java.util.List<java.lang.String>);
method public void sendConnectionEvent(java.lang.String, android.os.Bundle);
@@ -36230,6 +36228,7 @@
method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
method public final void setConferenceables(java.util.List<android.telecom.Conferenceable>);
method public final void setConnectionCapabilities(int);
+ method public final void setConnectionProperties(int);
method public final void setDialing();
method public final void setDisconnected(android.telecom.DisconnectCause);
method public final deprecated void setExtras(android.os.Bundle);
@@ -36246,12 +36245,11 @@
method public static java.lang.String stateToString(int);
field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 8388608; // 0x800000
field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
- field public static final int CAPABILITY_CAN_PULL_CALL = 33554432; // 0x2000000
+ field public static final int CAPABILITY_CAN_PULL_CALL = 16777216; // 0x1000000
field public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 4194304; // 0x400000
field public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 524288; // 0x80000
field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
field public static final int CAPABILITY_HOLD = 1; // 0x1
- field public static final int CAPABILITY_IS_EXTERNAL_CALL = 16777216; // 0x1000000
field public static final int CAPABILITY_MANAGE_CONFERENCE = 128; // 0x80
field public static final int CAPABILITY_MERGE_CONFERENCE = 4; // 0x4
field public static final int CAPABILITY_MUTE = 64; // 0x40
@@ -36269,6 +36267,7 @@
field public static final java.lang.String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
field public static final java.lang.String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS";
field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
+ field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
field public static final int STATE_ACTIVE = 4; // 0x4
field public static final int STATE_DIALING = 3; // 0x3
field public static final int STATE_DISCONNECTED = 6; // 0x6
@@ -36491,6 +36490,7 @@
method public void disconnect();
method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections();
method public final int getConnectionCapabilities();
+ method public final int getConnectionProperties();
method public final java.util.List<android.telecom.RemoteConnection> getConnections();
method public android.telecom.DisconnectCause getDisconnectCause();
method public final android.os.Bundle getExtras();
@@ -36513,6 +36513,7 @@
method public void onConferenceableConnectionsChanged(android.telecom.RemoteConference, java.util.List<android.telecom.RemoteConnection>);
method public void onConnectionAdded(android.telecom.RemoteConference, android.telecom.RemoteConnection);
method public void onConnectionCapabilitiesChanged(android.telecom.RemoteConference, int);
+ method public void onConnectionPropertiesChanged(android.telecom.RemoteConference, int);
method public void onConnectionRemoved(android.telecom.RemoteConference, android.telecom.RemoteConnection);
method public void onDestroyed(android.telecom.RemoteConference);
method public void onDisconnected(android.telecom.RemoteConference, android.telecom.DisconnectCause);
@@ -36531,6 +36532,7 @@
method public android.telecom.RemoteConference getConference();
method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections();
method public int getConnectionCapabilities();
+ method public int getConnectionProperties();
method public android.telecom.DisconnectCause getDisconnectCause();
method public final android.os.Bundle getExtras();
method public int getState();
@@ -36560,6 +36562,7 @@
method public void onConferenceableConnectionsChanged(android.telecom.RemoteConnection, java.util.List<android.telecom.RemoteConnection>);
method public void onConnectionCapabilitiesChanged(android.telecom.RemoteConnection, int);
method public void onConnectionEvent(android.telecom.RemoteConnection, java.lang.String, android.os.Bundle);
+ method public void onConnectionPropertiesChanged(android.telecom.RemoteConnection, int);
method public void onDestroyed(android.telecom.RemoteConnection);
method public void onDisconnected(android.telecom.RemoteConnection, android.telecom.DisconnectCause);
method public void onExtrasChanged(android.telecom.RemoteConnection, android.os.Bundle);
@@ -36625,7 +36628,6 @@
method public boolean handleMmi(java.lang.String, android.telecom.PhoneAccountHandle);
method public boolean isInCall();
method public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, java.lang.String);
- method public deprecated void launchManageBlockedNumbersActivity();
method public void placeCall(android.net.Uri, android.os.Bundle);
method public void registerPhoneAccount(android.telecom.PhoneAccount);
method public void showInCallScreen(boolean);
@@ -36737,6 +36739,7 @@
field public static final java.lang.String KEY_CARRIER_VT_AVAILABLE_BOOL = "carrier_vt_available_bool";
field public static final java.lang.String KEY_CARRIER_VVM_PACKAGE_NAME_STRING = "carrier_vvm_package_name_string";
field public static final java.lang.String KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL = "carrier_wfc_ims_available_bool";
+ field public static final java.lang.String KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL = "carrier_wfc_supports_wifi_only_bool";
field public static final java.lang.String KEY_CDMA_DTMF_TONE_DELAY_INT = "cdma_dtmf_tone_delay_int";
field public static final java.lang.String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array";
field public static final java.lang.String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array";
@@ -37313,7 +37316,8 @@
method public java.lang.String getDeviceSoftwareVersion();
method public java.lang.String getGroupIdLevel1();
method public java.lang.String getGroupIdLevel1(int);
- method public java.lang.String getIccSimChallengeResponse(int, java.lang.String);
+ method public java.lang.String getIccAuthentication(int, int, java.lang.String);
+ method public java.lang.String getIccAuthentication(int, int, int, java.lang.String);
method public java.lang.String getLine1AlphaTag(int);
method public java.lang.String getLine1Number();
method public java.lang.String getLine1Number(int);
@@ -37382,6 +37386,13 @@
field public static final java.lang.String ACTION_CONFIGURE_VOICEMAIL = "android.telephony.action.CONFIGURE_VOICEMAIL";
field public static final java.lang.String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
field public static final java.lang.String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
+ field public static final int APPTYPE_CSIM = 4; // 0x4
+ field public static final int APPTYPE_ISIM = 5; // 0x5
+ field public static final int APPTYPE_RUIM = 3; // 0x3
+ field public static final int APPTYPE_SIM = 1; // 0x1
+ field public static final int APPTYPE_USIM = 2; // 0x2
+ field public static final int AUTHTYPE_EAP_AKA = 129; // 0x81
+ field public static final int AUTHTYPE_EAP_SIM = 128; // 0x80
field public static final int CALL_STATE_IDLE = 0; // 0x0
field public static final int CALL_STATE_OFFHOOK = 2; // 0x2
field public static final int CALL_STATE_RINGING = 1; // 0x1
@@ -37925,8 +37936,6 @@
method public java.lang.String getPackageResourcePath();
method public android.content.res.Resources getResources();
method public android.content.SharedPreferences getSharedPreferences(java.lang.String, int);
- method public android.content.SharedPreferences getSharedPreferences(java.io.File, int);
- method public java.io.File getSharedPreferencesPath(java.lang.String);
method public java.lang.Object getSystemService(java.lang.String);
method public java.lang.String getSystemServiceName(java.lang.Class<?>);
method public android.content.res.Resources.Theme getTheme();
@@ -42281,6 +42290,7 @@
method public boolean dispatchDragEvent(android.view.DragEvent);
method protected void dispatchDraw(android.graphics.Canvas);
method public void dispatchDrawableHotspotChanged(float, float);
+ method public void dispatchFinishTemporaryDetach();
method protected boolean dispatchGenericFocusedEvent(android.view.MotionEvent);
method public boolean dispatchGenericMotionEvent(android.view.MotionEvent);
method protected boolean dispatchGenericPointerEvent(android.view.MotionEvent);
@@ -42300,6 +42310,7 @@
method protected void dispatchSetActivated(boolean);
method protected void dispatchSetPressed(boolean);
method protected void dispatchSetSelected(boolean);
+ method public void dispatchStartTemporaryDetach();
method public void dispatchSystemUiVisibilityChanged(int);
method public boolean dispatchTouchEvent(android.view.MotionEvent);
method public boolean dispatchTrackballEvent(android.view.MotionEvent);
@@ -42317,6 +42328,7 @@
method public void findViewsWithText(java.util.ArrayList<android.view.View>, java.lang.CharSequence, int);
method protected deprecated boolean fitSystemWindows(android.graphics.Rect);
method public android.view.View focusSearch(int);
+ method public void forceHasOverlappingRendering(boolean);
method public void forceLayout();
method public static int generateViewId();
method public java.lang.CharSequence getAccessibilityClassName();
@@ -42362,6 +42374,7 @@
method public boolean getGlobalVisibleRect(android.graphics.Rect, android.graphics.Point);
method public final boolean getGlobalVisibleRect(android.graphics.Rect);
method public android.os.Handler getHandler();
+ method public final boolean getHasOverlappingRendering();
method public final int getHeight();
method public void getHitRect(android.graphics.Rect);
method public int getHorizontalFadingEdgeLength();
@@ -42510,6 +42523,7 @@
method public boolean isSelected();
method public boolean isShown();
method public boolean isSoundEffectsEnabled();
+ method public final boolean isTemporarilyDetached();
method public boolean isTextAlignmentResolved();
method public boolean isTextDirectionResolved();
method public boolean isVerticalFadingEdgeEnabled();
@@ -44094,8 +44108,6 @@
method public void setVisibleToUser(boolean);
method public void writeToParcel(android.os.Parcel, int);
field public static final int ACTION_ACCESSIBILITY_FOCUS = 64; // 0x40
- field public static final java.lang.String ACTION_ARGUMENT_CLICK_CHARACTER_INDEX_INT = "android.view.accessibility.action.ARGUMENT_CLICK_CHARACTER_INDEX_INT";
- field public static final java.lang.String ACTION_ARGUMENT_CLICK_SPAN_INDEX_INT = "android.view.accessibility.action.ARGUMENT_CLICK_SPAN_INDEX_INT";
field public static final java.lang.String ACTION_ARGUMENT_COLUMN_INT = "android.view.accessibility.action.ARGUMENT_COLUMN_INT";
field public static final java.lang.String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN = "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
field public static final java.lang.String ACTION_ARGUMENT_HTML_ELEMENT_STRING = "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
@@ -44266,6 +44278,7 @@
public final class AccessibilityWindowInfo implements android.os.Parcelable {
method public int describeContents();
+ method public android.view.accessibility.AccessibilityNodeInfo getAnchor();
method public void getBoundsInScreen(android.graphics.Rect);
method public android.view.accessibility.AccessibilityWindowInfo getChild(int);
method public int getChildCount();
@@ -44273,6 +44286,7 @@
method public int getLayer();
method public android.view.accessibility.AccessibilityWindowInfo getParent();
method public android.view.accessibility.AccessibilityNodeInfo getRoot();
+ method public java.lang.CharSequence getTitle();
method public int getType();
method public boolean isAccessibilityFocused();
method public boolean isActive();
@@ -44613,6 +44627,7 @@
ctor public BaseInputConnection(android.view.View, boolean);
method public boolean beginBatchEdit();
method public boolean clearMetaKeyStates(int);
+ method public void closeConnection();
method public boolean commitCompletion(android.view.inputmethod.CompletionInfo);
method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public boolean commitText(java.lang.CharSequence, int);
@@ -44780,6 +44795,7 @@
public abstract interface InputConnection {
method public abstract boolean beginBatchEdit();
method public abstract boolean clearMetaKeyStates(int);
+ method public abstract void closeConnection();
method public abstract boolean commitCompletion(android.view.inputmethod.CompletionInfo);
method public abstract boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public abstract boolean commitText(java.lang.CharSequence, int);
@@ -44812,6 +44828,7 @@
ctor public InputConnectionWrapper(android.view.inputmethod.InputConnection, boolean);
method public boolean beginBatchEdit();
method public boolean clearMetaKeyStates(int);
+ method public void closeConnection();
method public boolean commitCompletion(android.view.inputmethod.CompletionInfo);
method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public boolean commitText(java.lang.CharSequence, int);
@@ -46261,7 +46278,7 @@
method public long getMinDate();
method public deprecated android.graphics.drawable.Drawable getSelectedDateVerticalBar();
method public deprecated int getSelectedWeekBackgroundColor();
- method public boolean getShowWeekNumber();
+ method public deprecated boolean getShowWeekNumber();
method public deprecated int getShownWeekCount();
method public deprecated int getUnfocusedMonthDateColor();
method public int getWeekDayTextAppearance();
@@ -46278,7 +46295,7 @@
method public deprecated void setSelectedDateVerticalBar(int);
method public deprecated void setSelectedDateVerticalBar(android.graphics.drawable.Drawable);
method public deprecated void setSelectedWeekBackgroundColor(int);
- method public void setShowWeekNumber(boolean);
+ method public deprecated void setShowWeekNumber(boolean);
method public deprecated void setShownWeekCount(int);
method public deprecated void setUnfocusedMonthDateColor(int);
method public void setWeekDayTextAppearance(int);
@@ -46425,21 +46442,21 @@
ctor public DatePicker(android.content.Context, android.util.AttributeSet);
ctor public DatePicker(android.content.Context, android.util.AttributeSet, int);
ctor public DatePicker(android.content.Context, android.util.AttributeSet, int, int);
- method public android.widget.CalendarView getCalendarView();
- method public boolean getCalendarViewShown();
+ method public deprecated android.widget.CalendarView getCalendarView();
+ method public deprecated boolean getCalendarViewShown();
method public int getDayOfMonth();
method public int getFirstDayOfWeek();
method public long getMaxDate();
method public long getMinDate();
method public int getMonth();
- method public boolean getSpinnersShown();
+ method public deprecated boolean getSpinnersShown();
method public int getYear();
method public void init(int, int, int, android.widget.DatePicker.OnDateChangedListener);
- method public void setCalendarViewShown(boolean);
+ method public deprecated void setCalendarViewShown(boolean);
method public void setFirstDayOfWeek(int);
method public void setMaxDate(long);
method public void setMinDate(long);
- method public void setSpinnersShown(boolean);
+ method public deprecated void setSpinnersShown(boolean);
method public void updateDate(int, int, int);
}
@@ -48259,9 +48276,15 @@
method public void collapseActionView();
method public void dismissPopupMenus();
method public int getContentInsetEnd();
+ method public int getContentInsetEndWithActions();
method public int getContentInsetLeft();
method public int getContentInsetRight();
method public int getContentInsetStart();
+ method public int getContentInsetStartWithNavigation();
+ method public int getCurrentContentInsetEnd();
+ method public int getCurrentContentInsetLeft();
+ method public int getCurrentContentInsetRight();
+ method public int getCurrentContentInsetStart();
method public android.graphics.drawable.Drawable getLogo();
method public java.lang.CharSequence getLogoDescription();
method public android.view.Menu getMenu();
@@ -48280,6 +48303,8 @@
method public void inflateMenu(int);
method public boolean isOverflowMenuShowing();
method protected void onLayout(boolean, int, int, int, int);
+ method public void setContentInsetEndWithActions(int);
+ method public void setContentInsetStartWithNavigation(int);
method public void setContentInsetsAbsolute(int, int);
method public void setContentInsetsRelative(int, int);
method public void setLogo(int);
@@ -49264,9 +49289,7 @@
public final class FilePermission extends java.security.Permission implements java.io.Serializable {
ctor public FilePermission(java.lang.String, java.lang.String);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -50005,6 +50028,7 @@
method public static int compare(boolean, boolean);
method public int compareTo(java.lang.Boolean);
method public static boolean getBoolean(java.lang.String);
+ method public static int hashCode(boolean);
method public static boolean parseBoolean(java.lang.String);
method public static java.lang.String toString(boolean);
method public static java.lang.Boolean valueOf(boolean);
@@ -50022,6 +50046,7 @@
method public static java.lang.Byte decode(java.lang.String) throws java.lang.NumberFormatException;
method public double doubleValue();
method public float floatValue();
+ method public static int hashCode(byte);
method public int intValue();
method public long longValue();
method public static byte parseByte(java.lang.String, int) throws java.lang.NumberFormatException;
@@ -50030,6 +50055,7 @@
method public static java.lang.Byte valueOf(byte);
method public static java.lang.Byte valueOf(java.lang.String, int) throws java.lang.NumberFormatException;
method public static java.lang.Byte valueOf(java.lang.String) throws java.lang.NumberFormatException;
+ field public static final int BYTES = 1; // 0x1
field public static final byte MAX_VALUE = 127; // 0x7f
field public static final byte MIN_VALUE = -128; // 0xffffff80
field public static final int SIZE = 8; // 0x8
@@ -50067,6 +50093,7 @@
method public static int getNumericValue(int);
method public static int getType(char);
method public static int getType(int);
+ method public static int hashCode(char);
method public static char highSurrogate(int);
method public static boolean isAlphabetic(int);
method public static boolean isBmpCodePoint(int);
@@ -50127,6 +50154,7 @@
method public static char toUpperCase(char);
method public static int toUpperCase(int);
method public static java.lang.Character valueOf(char);
+ field public static final int BYTES = 2; // 0x2
field public static final byte COMBINING_SPACING_MARK = 8; // 0x8
field public static final byte CONNECTOR_PUNCTUATION = 23; // 0x17
field public static final byte CONTROL = 15; // 0xf
@@ -50753,6 +50781,7 @@
method public static int floatToIntBits(float);
method public static int floatToRawIntBits(float);
method public float floatValue();
+ method public static int hashCode(float);
method public static float intBitsToFloat(int);
method public int intValue();
method public static boolean isFinite(float);
@@ -50761,11 +50790,15 @@
method public static boolean isNaN(float);
method public boolean isNaN();
method public long longValue();
+ method public static float max(float, float);
+ method public static float min(float, float);
method public static float parseFloat(java.lang.String) throws java.lang.NumberFormatException;
+ method public static float sum(float, float);
method public static java.lang.String toHexString(float);
method public static java.lang.String toString(float);
method public static java.lang.Float valueOf(java.lang.String) throws java.lang.NumberFormatException;
method public static java.lang.Float valueOf(float);
+ field public static final int BYTES = 4; // 0x4
field public static final int MAX_EXPONENT = 127; // 0x7f
field public static final float MAX_VALUE = 3.4028235E38f;
field public static final int MIN_EXPONENT = -126; // 0xffffff82
@@ -50945,6 +50978,7 @@
method public static java.lang.Long valueOf(java.lang.String, int) throws java.lang.NumberFormatException;
method public static java.lang.Long valueOf(java.lang.String) throws java.lang.NumberFormatException;
method public static java.lang.Long valueOf(long);
+ field public static final int BYTES = 8; // 0x8
field public static final long MAX_VALUE = 9223372036854775807L; // 0x7fffffffffffffffL
field public static final long MIN_VALUE = -9223372036854775808L; // 0x8000000000000000L
field public static final int SIZE = 64; // 0x40
@@ -51124,41 +51158,11 @@
method public java.io.File directory();
method public java.lang.ProcessBuilder directory(java.io.File);
method public java.util.Map<java.lang.String, java.lang.String> environment();
- method public java.lang.ProcessBuilder inheritIO();
- method public java.lang.ProcessBuilder redirectError(java.lang.ProcessBuilder.Redirect);
- method public java.lang.ProcessBuilder redirectError(java.io.File);
- method public java.lang.ProcessBuilder.Redirect redirectError();
method public boolean redirectErrorStream();
method public java.lang.ProcessBuilder redirectErrorStream(boolean);
- method public java.lang.ProcessBuilder redirectInput(java.lang.ProcessBuilder.Redirect);
- method public java.lang.ProcessBuilder redirectInput(java.io.File);
- method public java.lang.ProcessBuilder.Redirect redirectInput();
- method public java.lang.ProcessBuilder redirectOutput(java.lang.ProcessBuilder.Redirect);
- method public java.lang.ProcessBuilder redirectOutput(java.io.File);
- method public java.lang.ProcessBuilder.Redirect redirectOutput();
method public java.lang.Process start() throws java.io.IOException;
}
- public static abstract class ProcessBuilder.Redirect {
- method public static java.lang.ProcessBuilder.Redirect appendTo(java.io.File);
- method public java.io.File file();
- method public static java.lang.ProcessBuilder.Redirect from(java.io.File);
- method public static java.lang.ProcessBuilder.Redirect to(java.io.File);
- method public abstract java.lang.ProcessBuilder.Redirect.Type type();
- field public static final java.lang.ProcessBuilder.Redirect INHERIT;
- field public static final java.lang.ProcessBuilder.Redirect PIPE;
- }
-
- public static final class ProcessBuilder.Redirect.Type extends java.lang.Enum {
- method public static java.lang.ProcessBuilder.Redirect.Type valueOf(java.lang.String);
- method public static final java.lang.ProcessBuilder.Redirect.Type[] values();
- enum_constant public static final java.lang.ProcessBuilder.Redirect.Type APPEND;
- enum_constant public static final java.lang.ProcessBuilder.Redirect.Type INHERIT;
- enum_constant public static final java.lang.ProcessBuilder.Redirect.Type PIPE;
- enum_constant public static final java.lang.ProcessBuilder.Redirect.Type READ;
- enum_constant public static final java.lang.ProcessBuilder.Redirect.Type WRITE;
- }
-
public abstract interface Readable {
method public abstract int read(java.nio.CharBuffer) throws java.io.IOException;
}
@@ -51278,6 +51282,7 @@
method public static java.lang.Short decode(java.lang.String) throws java.lang.NumberFormatException;
method public double doubleValue();
method public float floatValue();
+ method public static int hashCode(short);
method public int intValue();
method public long longValue();
method public static short parseShort(java.lang.String, int) throws java.lang.NumberFormatException;
@@ -51287,6 +51292,7 @@
method public static java.lang.Short valueOf(java.lang.String, int) throws java.lang.NumberFormatException;
method public static java.lang.Short valueOf(java.lang.String) throws java.lang.NumberFormatException;
method public static java.lang.Short valueOf(short);
+ field public static final int BYTES = 2; // 0x2
field public static final short MAX_VALUE = 32767; // 0x7fff
field public static final short MIN_VALUE = -32768; // 0xffff8000
field public static final int SIZE = 16; // 0x10
@@ -52810,9 +52816,7 @@
public final class SocketPermission extends java.security.Permission implements java.io.Serializable {
ctor public SocketPermission(java.lang.String, java.lang.String);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -53894,9 +53898,7 @@
public final class AllPermission extends java.security.Permission {
ctor public AllPermission();
ctor public AllPermission(java.lang.String, java.lang.String);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -53910,9 +53912,7 @@
public abstract class BasicPermission extends java.security.Permission implements java.io.Serializable {
ctor public BasicPermission(java.lang.String);
ctor public BasicPermission(java.lang.String, java.lang.String);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -54290,10 +54290,8 @@
public abstract class Permission implements java.security.Guard java.io.Serializable {
ctor public Permission(java.lang.String);
method public void checkGuard(java.lang.Object) throws java.lang.SecurityException;
- method public abstract boolean equals(java.lang.Object);
method public abstract java.lang.String getActions();
method public final java.lang.String getName();
- method public abstract int hashCode();
method public abstract boolean implies(java.security.Permission);
method public java.security.PermissionCollection newPermissionCollection();
}
@@ -54551,13 +54549,11 @@
public final class UnresolvedPermission extends java.security.Permission implements java.io.Serializable {
ctor public UnresolvedPermission(java.lang.String, java.lang.String, java.lang.String, java.security.cert.Certificate[]);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
method public java.lang.String getUnresolvedActions();
method public java.security.cert.Certificate[] getUnresolvedCerts();
method public java.lang.String getUnresolvedName();
method public java.lang.String getUnresolvedType();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -54615,8 +54611,6 @@
}
public abstract interface Permission {
- method public abstract boolean equals(java.lang.Object);
- method public abstract java.lang.String toString();
}
}
@@ -57332,6 +57326,7 @@
method public void forEach(java.util.function.Consumer<? super E>);
method public E get(int);
method public boolean removeIf(java.util.function.Predicate<? super E>);
+ method public void replaceAll(java.util.function.UnaryOperator<E>);
method public int size();
method public void sort(java.util.Comparator<? super E>);
method public java.util.Spliterator<E> spliterator();
@@ -57417,6 +57412,10 @@
method public static int hashCode(float[]);
method public static int hashCode(double[]);
method public static int hashCode(java.lang.Object[]);
+ method public static void parallelSetAll(T[], java.util.function.IntFunction<? extends T>);
+ method public static void parallelSetAll(int[], java.util.function.IntUnaryOperator);
+ method public static void parallelSetAll(long[], java.util.function.IntToLongFunction);
+ method public static void parallelSetAll(double[], java.util.function.IntToDoubleFunction);
method public static void parallelSort(byte[]);
method public static void parallelSort(byte[], int, int);
method public static void parallelSort(char[]);
@@ -57965,6 +57964,7 @@
method public java.lang.Object clone();
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
method public void forEach(java.util.function.BiConsumer<? super K, ? super V>);
+ method public boolean replace(K, V, V);
}
public class HashSet extends java.util.AbstractSet implements java.lang.Cloneable java.io.Serializable java.util.Set {
@@ -57985,6 +57985,9 @@
ctor public Hashtable(java.util.Map<? extends K, ? extends V>);
method public synchronized void clear();
method public synchronized java.lang.Object clone();
+ method public synchronized V compute(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>);
+ method public synchronized V computeIfAbsent(K, java.util.function.Function<? super K, ? extends V>);
+ method public synchronized V computeIfPresent(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>);
method public synchronized boolean contains(java.lang.Object);
method public synchronized boolean containsKey(java.lang.Object);
method public boolean containsValue(java.lang.Object);
@@ -57992,13 +57995,19 @@
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
method public synchronized void forEach(java.util.function.BiConsumer<? super K, ? super V>);
method public synchronized V get(java.lang.Object);
+ method public synchronized V getOrDefault(java.lang.Object, V);
method public synchronized boolean isEmpty();
method public java.util.Set<K> keySet();
method public synchronized java.util.Enumeration<K> keys();
+ method public synchronized V merge(K, V, java.util.function.BiFunction<? super V, ? super V, ? extends V>);
method public synchronized V put(K, V);
method public synchronized void putAll(java.util.Map<? extends K, ? extends V>);
+ method public synchronized V putIfAbsent(K, V);
method protected void rehash();
method public synchronized V remove(java.lang.Object);
+ method public synchronized boolean remove(java.lang.Object, java.lang.Object);
+ method public synchronized boolean replace(K, V, V);
+ method public synchronized V replace(K, V);
method public synchronized int size();
method public java.util.Collection<V> values();
}
@@ -58143,9 +58152,11 @@
method public abstract boolean remove(java.lang.Object);
method public abstract E remove(int);
method public abstract boolean removeAll(java.util.Collection<?>);
+ method public default void replaceAll(java.util.function.UnaryOperator<E>);
method public abstract boolean retainAll(java.util.Collection<?>);
method public abstract E set(int, E);
method public abstract int size();
+ method public default void sort(java.util.Comparator<? super E>);
method public abstract java.util.List<E> subList(int, int);
method public abstract java.lang.Object[] toArray();
method public abstract T[] toArray(T[]);
@@ -59060,9 +59071,11 @@
method public synchronized boolean removeElement(java.lang.Object);
method public synchronized void removeElementAt(int);
method public synchronized boolean removeIf(java.util.function.Predicate<? super E>);
+ method public synchronized void replaceAll(java.util.function.UnaryOperator<E>);
method public synchronized void setElementAt(E, int);
method public synchronized void setSize(int);
method public synchronized int size();
+ method public synchronized void sort(java.util.Comparator<? super E>);
method public java.util.Spliterator<E> spliterator();
method public synchronized void trimToSize();
field protected int capacityIncrement;
@@ -59528,6 +59541,7 @@
method public java.lang.Object clone();
method public boolean contains(java.lang.Object);
method public boolean containsAll(java.util.Collection<?>);
+ method public void forEach(java.util.function.Consumer<? super E>);
method public E get(int);
method public int indexOf(E, int);
method public int indexOf(java.lang.Object);
@@ -63905,11 +63919,9 @@
public final class PrivateCredentialPermission extends java.security.Permission {
ctor public PrivateCredentialPermission(java.lang.String, java.lang.String);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
method public java.lang.String getCredentialClass();
method public java.lang.String[][] getPrincipals();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
diff --git a/api/removed.txt b/api/removed.txt
index 36c8ce5..3f16bca 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -94,6 +94,10 @@
package android.media.tv {
+ public final class TvInputManager {
+ method public android.media.tv.TvInputManager.Hardware acquireTvInputHardware(int, android.media.tv.TvInputManager.HardwareCallback, android.media.tv.TvInputInfo);
+ }
+
public class TvView extends android.view.ViewGroup {
method public void requestUnblockContent(android.media.tv.TvContentRating);
}
@@ -136,6 +140,15 @@
}
+package android.os.storage {
+
+ public class StorageManager {
+ method public android.os.storage.StorageVolume getPrimaryVolume();
+ method public android.os.storage.StorageVolume[] getVolumeList();
+ }
+
+}
+
package android.preference {
public class PreferenceManager {
diff --git a/api/system-current.txt b/api/system-current.txt
index 507a1a7..e73287e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -42,6 +42,7 @@
field public static final java.lang.String BIND_MIDI_DEVICE_SERVICE = "android.permission.BIND_MIDI_DEVICE_SERVICE";
field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE";
field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE";
+ field public static final java.lang.String BIND_PRINT_RECOMMENDATION_SERVICE = "android.permission.BIND_PRINT_RECOMMENDATION_SERVICE";
field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE";
field public static final java.lang.String BIND_QUICK_SETTINGS_TILE = "android.permission.BIND_QUICK_SETTINGS_TILE";
field public static final java.lang.String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS";
@@ -102,6 +103,7 @@
field public static final java.lang.String GET_APP_OPS_STATS = "android.permission.GET_APP_OPS_STATS";
field public static final java.lang.String GET_PACKAGE_IMPORTANCE = "android.permission.GET_PACKAGE_IMPORTANCE";
field public static final java.lang.String GET_PACKAGE_SIZE = "android.permission.GET_PACKAGE_SIZE";
+ field public static final java.lang.String GET_PASSWORD_PRIVILEGED = "android.permission.GET_PASSWORD_PRIVILEGED";
field public static final java.lang.String GET_PROCESS_STATE_AND_OOM_SCORE = "android.permission.GET_PROCESS_STATE_AND_OOM_SCORE";
field public static final deprecated java.lang.String GET_TASKS = "android.permission.GET_TASKS";
field public static final java.lang.String GET_TOP_ACTIVITY_INFO = "android.permission.GET_TOP_ACTIVITY_INFO";
@@ -511,9 +513,11 @@
field public static final int contentAuthority = 16843408; // 0x1010290
field public static final int contentDescription = 16843379; // 0x1010273
field public static final int contentInsetEnd = 16843860; // 0x1010454
+ field public static final int contentInsetEndWithActions = 16844070; // 0x1010526
field public static final int contentInsetLeft = 16843861; // 0x1010455
field public static final int contentInsetRight = 16843862; // 0x1010456
field public static final int contentInsetStart = 16843859; // 0x1010453
+ field public static final int contentInsetStartWithNavigation = 16844069; // 0x1010525
field public static final int contextClickable = 16844007; // 0x10104e7
field public static final int contextPopupMenuStyle = 16844034; // 0x1010502
field public static final int controlX1 = 16843772; // 0x10103fc
@@ -671,6 +675,7 @@
field public static final int fontFamily = 16843692; // 0x10103ac
field public static final int fontFeatureSettings = 16843959; // 0x10104b7
field public static final int footerDividersEnabled = 16843311; // 0x101022f
+ field public static final int forceHasOverlappingRendering = 16844068; // 0x1010524
field public static final int foreground = 16843017; // 0x1010109
field public static final int foregroundGravity = 16843264; // 0x1010200
field public static final int foregroundTint = 16843885; // 0x101046d
@@ -954,7 +959,8 @@
field public static final int minResizeWidth = 16843669; // 0x1010395
field public static final int minSdkVersion = 16843276; // 0x101020c
field public static final int minWidth = 16843071; // 0x101013f
- field public static final int minimalSize = 16844022; // 0x10104f6
+ field public static final int minimalHeight = 16844067; // 0x1010523
+ field public static final int minimalWidth = 16844022; // 0x10104f6
field public static final int minimumHorizontalAngle = 16843901; // 0x101047d
field public static final int minimumVerticalAngle = 16843902; // 0x101047e
field public static final int mipMap = 16843725; // 0x10103cd
@@ -2582,6 +2588,7 @@
field public static final int Widget_Material_CompoundButton_CheckBox = 16974435; // 0x1030263
field public static final int Widget_Material_CompoundButton_RadioButton = 16974436; // 0x1030264
field public static final int Widget_Material_CompoundButton_Star = 16974437; // 0x1030265
+ field public static final int Widget_Material_CompoundButton_Switch = 16974554; // 0x10302da
field public static final int Widget_Material_DatePicker = 16974438; // 0x1030266
field public static final int Widget_Material_DropDownItem = 16974439; // 0x1030267
field public static final int Widget_Material_DropDownItem_Spinner = 16974440; // 0x1030268
@@ -2616,6 +2623,7 @@
field public static final int Widget_Material_Light_CompoundButton_CheckBox = 16974500; // 0x10302a4
field public static final int Widget_Material_Light_CompoundButton_RadioButton = 16974501; // 0x10302a5
field public static final int Widget_Material_Light_CompoundButton_Star = 16974502; // 0x10302a6
+ field public static final int Widget_Material_Light_CompoundButton_Switch = 16974555; // 0x10302db
field public static final int Widget_Material_Light_DatePicker = 16974503; // 0x10302a7
field public static final int Widget_Material_Light_DropDownItem = 16974504; // 0x10302a8
field public static final int Widget_Material_Light_DropDownItem_Spinner = 16974505; // 0x10302a9
@@ -3536,7 +3544,7 @@
method public boolean dispatchTouchEvent(android.view.MotionEvent);
method public boolean dispatchTrackballEvent(android.view.MotionEvent);
method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
- method public void enterPictureInPicture();
+ method public void enterPictureInPictureMode();
method public android.view.View findViewById(int);
method public void finish();
method public void finishActivity(int);
@@ -3576,8 +3584,6 @@
method public android.view.Window getWindow();
method public android.view.WindowManager getWindowManager();
method public boolean hasWindowFocus();
- method public boolean inMultiWindow();
- method public boolean inPictureInPicture();
method public void invalidateOptionsMenu();
method public boolean isBackgroundVisibleBehind();
method public boolean isChangingConfigurations();
@@ -3585,7 +3591,10 @@
method public boolean isDestroyed();
method public boolean isFinishing();
method public boolean isImmersive();
+ method public boolean isInMultiWindowMode();
+ method public boolean isInPictureInPictureMode();
method public boolean isLocalVoiceInteractionSupported();
+ method public boolean isOverlayWithDecorCaptionEnabled();
method public boolean isTaskRoot();
method public boolean isVoiceInteraction();
method public boolean isVoiceInteractionRoot();
@@ -3633,7 +3642,7 @@
method public void onLowMemory();
method public boolean onMenuItemSelected(int, android.view.MenuItem);
method public boolean onMenuOpened(int, android.view.Menu);
- method public void onMultiWindowChanged(boolean);
+ method public void onMultiWindowModeChanged(boolean);
method public boolean onNavigateUp();
method public boolean onNavigateUpFromChild(android.app.Activity);
method protected void onNewIntent(android.content.Intent);
@@ -3641,7 +3650,7 @@
method public void onOptionsMenuClosed(android.view.Menu);
method public void onPanelClosed(int, android.view.Menu);
method protected void onPause();
- method public void onPictureInPictureChanged(boolean);
+ method public void onPictureInPictureModeChanged(boolean);
method protected void onPostCreate(android.os.Bundle);
method public void onPostCreate(android.os.Bundle, android.os.PersistableBundle);
method protected void onPostResume();
@@ -3679,7 +3688,6 @@
method public android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback, int);
method public void openContextMenu(android.view.View);
method public void openOptionsMenu();
- method public void overlayWithDecorCaption(boolean);
method public void overridePendingTransition(int, int);
method public void postponeEnterTransition();
method public void recreate();
@@ -3708,6 +3716,7 @@
method public void setImmersive(boolean);
method public void setIntent(android.content.Intent);
method public final void setMediaController(android.media.session.MediaController);
+ method public void setOverlayWithDecorCaptionEnabled(boolean);
method public final deprecated void setProgress(int);
method public final deprecated void setProgressBarIndeterminate(boolean);
method public final deprecated void setProgressBarIndeterminateVisibility(boolean);
@@ -4412,6 +4421,7 @@
public class DownloadManager {
method public long addCompletedDownload(java.lang.String, java.lang.String, boolean, java.lang.String, java.lang.String, long, boolean);
+ method public long addCompletedDownload(java.lang.String, java.lang.String, boolean, java.lang.String, java.lang.String, long, boolean, android.net.Uri, android.net.Uri);
method public long enqueue(android.app.DownloadManager.Request);
method public static java.lang.Long getMaxBytesOverMobile(android.content.Context);
method public java.lang.String getMimeTypeForDownloadedFile(long);
@@ -4564,11 +4574,11 @@
method public void onInflate(android.content.Context, android.util.AttributeSet, android.os.Bundle);
method public deprecated void onInflate(android.app.Activity, android.util.AttributeSet, android.os.Bundle);
method public void onLowMemory();
- method public void onMultiWindowChanged(boolean);
+ method public void onMultiWindowModeChanged(boolean);
method public boolean onOptionsItemSelected(android.view.MenuItem);
method public void onOptionsMenuClosed(android.view.Menu);
method public void onPause();
- method public void onPictureInPictureChanged(boolean);
+ method public void onPictureInPictureModeChanged(boolean);
method public void onPrepareOptionsMenu(android.view.Menu);
method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
method public void onResume();
@@ -4649,11 +4659,11 @@
method public void dispatchDestroy();
method public void dispatchDestroyView();
method public void dispatchLowMemory();
- method public void dispatchMultiWindowChanged(boolean);
+ method public void dispatchMultiWindowModeChanged(boolean);
method public boolean dispatchOptionsItemSelected(android.view.MenuItem);
method public void dispatchOptionsMenuClosed(android.view.Menu);
method public void dispatchPause();
- method public void dispatchPictureInPictureChanged(boolean);
+ method public void dispatchPictureInPictureModeChanged(boolean);
method public boolean dispatchPrepareOptionsMenu(android.view.Menu);
method public void dispatchResume();
method public void dispatchStart();
@@ -5102,7 +5112,7 @@
field public int ledARGB;
field public int ledOffMS;
field public int ledOnMS;
- field public int number;
+ field public deprecated int number;
field public int priority;
field public android.app.Notification publicVersion;
field public android.net.Uri sound;
@@ -5149,11 +5159,13 @@
method public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Builder);
method public java.lang.CharSequence getCancelLabel();
method public java.lang.CharSequence getConfirmLabel();
+ method public boolean getHintContentIntentLaunchesActivity();
method public java.lang.CharSequence getInProgressLabel();
method public boolean isAvailableOffline();
method public android.app.Notification.Action.WearableExtender setAvailableOffline(boolean);
method public android.app.Notification.Action.WearableExtender setCancelLabel(java.lang.CharSequence);
method public android.app.Notification.Action.WearableExtender setConfirmLabel(java.lang.CharSequence);
+ method public android.app.Notification.Action.WearableExtender setHintContentIntentLaunchesActivity(boolean);
method public android.app.Notification.Action.WearableExtender setInProgressLabel(java.lang.CharSequence);
}
@@ -5195,7 +5207,7 @@
method public android.app.Notification.Builder setChronometerCountsDown(boolean);
method public android.app.Notification.Builder setColor(int);
method public deprecated android.app.Notification.Builder setContent(android.widget.RemoteViews);
- method public android.app.Notification.Builder setContentInfo(java.lang.CharSequence);
+ method public deprecated android.app.Notification.Builder setContentInfo(java.lang.CharSequence);
method public android.app.Notification.Builder setContentIntent(android.app.PendingIntent);
method public android.app.Notification.Builder setContentText(java.lang.CharSequence);
method public android.app.Notification.Builder setContentTitle(java.lang.CharSequence);
@@ -5212,7 +5224,7 @@
method public android.app.Notification.Builder setLargeIcon(android.graphics.drawable.Icon);
method public android.app.Notification.Builder setLights(int, int, int);
method public android.app.Notification.Builder setLocalOnly(boolean);
- method public android.app.Notification.Builder setNumber(int);
+ method public deprecated android.app.Notification.Builder setNumber(int);
method public android.app.Notification.Builder setOngoing(boolean);
method public android.app.Notification.Builder setOnlyAlertOnce(boolean);
method public android.app.Notification.Builder setPriority(int);
@@ -5330,6 +5342,7 @@
method public android.app.PendingIntent getDisplayIntent();
method public int getGravity();
method public boolean getHintAvoidBackgroundClipping();
+ method public boolean getHintContentIntentLaunchesActivity();
method public boolean getHintHideIcon();
method public int getHintScreenTimeout();
method public boolean getHintShowBackgroundOnly();
@@ -5345,6 +5358,7 @@
method public android.app.Notification.WearableExtender setDisplayIntent(android.app.PendingIntent);
method public android.app.Notification.WearableExtender setGravity(int);
method public android.app.Notification.WearableExtender setHintAvoidBackgroundClipping(boolean);
+ method public android.app.Notification.WearableExtender setHintContentIntentLaunchesActivity(boolean);
method public android.app.Notification.WearableExtender setHintHideIcon(boolean);
method public android.app.Notification.WearableExtender setHintScreenTimeout(int);
method public android.app.Notification.WearableExtender setHintShowBackgroundOnly(boolean);
@@ -5886,8 +5900,8 @@
field public static final java.lang.String COMMAND_SECONDARY_TAP = "android.wallpaper.secondaryTap";
field public static final java.lang.String COMMAND_TAP = "android.wallpaper.tap";
field public static final java.lang.String EXTRA_LIVE_WALLPAPER_COMPONENT = "android.service.wallpaper.extra.LIVE_WALLPAPER_COMPONENT";
- field public static final int FLAG_SET_LOCK = 2; // 0x2
- field public static final int FLAG_SET_SYSTEM = 1; // 0x1
+ field public static final int FLAG_LOCK = 2; // 0x2
+ field public static final int FLAG_SYSTEM = 1; // 0x1
field public static final java.lang.String WALLPAPER_PREVIEW_META_DATA = "android.wallpaper.preview";
}
@@ -5990,7 +6004,7 @@
method public deprecated java.lang.String getDeviceInitializerApp();
method public deprecated android.content.ComponentName getDeviceInitializerComponent();
method public java.lang.String getDeviceOwner();
- method public java.lang.String getDeviceOwnerLockScreenInfo();
+ method public java.lang.CharSequence getDeviceOwnerLockScreenInfo();
method public java.lang.String getDeviceOwnerNameOnAnyUser();
method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName);
method public int getKeyguardDisabledFeatures(android.content.ComponentName);
@@ -5999,7 +6013,6 @@
method public long getMaximumTimeToLock(android.content.ComponentName);
method public int getOrganizationColor(android.content.ComponentName);
method public java.lang.String getOrganizationName(android.content.ComponentName);
- method public boolean getPackageSuspended(android.content.ComponentName, java.lang.String);
method public android.app.admin.DevicePolicyManager getParentProfileInstance(android.content.ComponentName);
method public long getPasswordExpiration(android.content.ComponentName);
method public long getPasswordExpirationTimeout(android.content.ComponentName);
@@ -6034,14 +6047,16 @@
method public boolean hasGrantedPolicy(android.content.ComponentName, int);
method public boolean installCaCert(android.content.ComponentName, byte[]);
method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate, java.lang.String);
- method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate, java.lang.String, boolean);
+ method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], java.lang.String, boolean);
method public boolean isActivePasswordSufficient();
method public boolean isAdminActive(android.content.ComponentName);
method public boolean isApplicationHidden(android.content.ComponentName, java.lang.String);
method public boolean isCallerApplicationRestrictionsManagingPackage();
method public boolean isDeviceOwnerApp(java.lang.String);
method public boolean isLockTaskPermitted(java.lang.String);
+ method public boolean isManagedProfile(android.content.ComponentName);
method public boolean isMasterVolumeMuted(android.content.ComponentName);
+ method public boolean isPackageSuspended(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
method public boolean isProfileOwnerApp(java.lang.String);
method public boolean isProvisioningAllowed(java.lang.String);
method public boolean isSecurityLoggingEnabled(android.content.ComponentName);
@@ -6059,7 +6074,7 @@
method public java.util.List<android.app.admin.SecurityLog.SecurityEvent> retrieveSecurityLogs(android.content.ComponentName);
method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
method public deprecated boolean setActiveProfileOwner(android.content.ComponentName, java.lang.String) throws java.lang.IllegalArgumentException;
- method public boolean setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String);
+ method public void setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException, java.lang.UnsupportedOperationException;
method public boolean setApplicationHidden(android.content.ComponentName, java.lang.String, boolean);
method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
method public void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -6069,7 +6084,7 @@
method public void setCertInstallerPackage(android.content.ComponentName, java.lang.String) throws java.lang.SecurityException;
method public void setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean);
method public void setCrossProfileContactsSearchDisabled(android.content.ComponentName, boolean);
- method public boolean setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.String);
+ method public void setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.CharSequence);
method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public boolean setKeyguardDisabled(android.content.ComponentName, boolean);
method public void setKeyguardDisabledFeatures(android.content.ComponentName, int);
@@ -8172,6 +8187,7 @@
method public static boolean isSyncPending(android.accounts.Account, java.lang.String);
method public void notifyChange(android.net.Uri, android.database.ContentObserver);
method public void notifyChange(android.net.Uri, android.database.ContentObserver, boolean);
+ method public void notifyChange(android.net.Uri, android.database.ContentObserver, int);
method public final android.content.res.AssetFileDescriptor openAssetFileDescriptor(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
method public final android.content.res.AssetFileDescriptor openAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException;
method public final android.os.ParcelFileDescriptor openFileDescriptor(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
@@ -8202,6 +8218,8 @@
field public static final java.lang.String CURSOR_DIR_BASE_TYPE = "vnd.android.cursor.dir";
field public static final java.lang.String CURSOR_ITEM_BASE_TYPE = "vnd.android.cursor.item";
field public static final java.lang.String EXTRA_SIZE = "android.content.extra.SIZE";
+ field public static final int NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS = 2; // 0x2
+ field public static final int NOTIFY_SYNC_TO_NETWORK = 1; // 0x1
field public static final java.lang.String SCHEME_ANDROID_RESOURCE = "android.resource";
field public static final java.lang.String SCHEME_CONTENT = "content";
field public static final java.lang.String SCHEME_FILE = "file";
@@ -8529,8 +8547,6 @@
method public java.lang.String getPackageResourcePath();
method public android.content.res.Resources getResources();
method public android.content.SharedPreferences getSharedPreferences(java.lang.String, int);
- method public android.content.SharedPreferences getSharedPreferences(java.io.File, int);
- method public java.io.File getSharedPreferencesPath(java.lang.String);
method public java.lang.Object getSystemService(java.lang.String);
method public java.lang.String getSystemServiceName(java.lang.Class<?>);
method public android.content.res.Resources.Theme getTheme();
@@ -8830,8 +8846,9 @@
field public static final java.lang.String ACTION_LOCKED_BOOT_COMPLETED = "android.intent.action.LOCKED_BOOT_COMPLETED";
field public static final java.lang.String ACTION_MAIN = "android.intent.action.MAIN";
field public static final java.lang.String ACTION_MANAGED_PROFILE_ADDED = "android.intent.action.MANAGED_PROFILE_ADDED";
- field public static final java.lang.String ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED = "android.intent.action.MANAGED_PROFILE_AVAILABILITY_CHANGED";
+ field public static final java.lang.String ACTION_MANAGED_PROFILE_AVAILABLE = "android.intent.action.MANAGED_PROFILE_AVAILABLE";
field public static final java.lang.String ACTION_MANAGED_PROFILE_REMOVED = "android.intent.action.MANAGED_PROFILE_REMOVED";
+ field public static final java.lang.String ACTION_MANAGED_PROFILE_UNAVAILABLE = "android.intent.action.MANAGED_PROFILE_UNAVAILABLE";
field public static final java.lang.String ACTION_MANAGED_PROFILE_UNLOCKED = "android.intent.action.MANAGED_PROFILE_UNLOCKED";
field public static final java.lang.String ACTION_MANAGE_NETWORK_USAGE = "android.intent.action.MANAGE_NETWORK_USAGE";
field public static final java.lang.String ACTION_MANAGE_PACKAGE_STORAGE = "android.intent.action.MANAGE_PACKAGE_STORAGE";
@@ -9579,7 +9596,6 @@
field public int documentLaunchMode;
field public int flags;
field public int launchMode;
- field public android.content.pm.ActivityInfo.Layout layout;
field public int maxRecents;
field public java.lang.String parentActivityName;
field public java.lang.String permission;
@@ -9590,14 +9606,16 @@
field public java.lang.String taskAffinity;
field public int theme;
field public int uiOptions;
+ field public android.content.pm.ActivityInfo.WindowLayout windowLayout;
}
- public static final class ActivityInfo.Layout {
- ctor public ActivityInfo.Layout(int, float, int, float, int, int);
+ public static final class ActivityInfo.WindowLayout {
+ ctor public ActivityInfo.WindowLayout(int, float, int, float, int, int, int);
field public final int gravity;
field public final int height;
field public final float heightFraction;
- field public final int minimalSize;
+ field public final int minimalHeight;
+ field public final int minimalWidth;
field public final int width;
field public final float widthFraction;
}
@@ -10308,6 +10326,7 @@
field public static final int PROTECTION_FLAG_PRE23 = 128; // 0x80
field public static final int PROTECTION_FLAG_PREINSTALLED = 1024; // 0x400
field public static final int PROTECTION_FLAG_PRIVILEGED = 16; // 0x10
+ field public static final int PROTECTION_FLAG_SETUP = 2048; // 0x800
field public static final deprecated int PROTECTION_FLAG_SYSTEM = 16; // 0x10
field public static final int PROTECTION_FLAG_VERIFIER = 512; // 0x200
field public static final int PROTECTION_MASK_BASE = 15; // 0xf
@@ -10385,7 +10404,7 @@
field public java.lang.String permission;
}
- public class ShortcutInfo implements android.os.Parcelable {
+ public final class ShortcutInfo implements android.os.Parcelable {
method public int describeContents();
method public android.content.ComponentName getActivityComponent();
method public android.os.PersistableBundle getExtras();
@@ -10393,6 +10412,7 @@
method public android.content.Intent getIntent();
method public long getLastChangedTimestamp();
method public java.lang.String getPackageName();
+ method public java.lang.String getText();
method public java.lang.String getTitle();
method public int getWeight();
method public boolean hasIconFile();
@@ -10420,6 +10440,7 @@
method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon);
method public android.content.pm.ShortcutInfo.Builder setId(java.lang.String);
method public android.content.pm.ShortcutInfo.Builder setIntent(android.content.Intent);
+ method public android.content.pm.ShortcutInfo.Builder setText(java.lang.String);
method public android.content.pm.ShortcutInfo.Builder setTitle(java.lang.String);
method public android.content.pm.ShortcutInfo.Builder setWeight(int);
}
@@ -13894,6 +13915,7 @@
public final class Sensor {
method public int getFifoMaxEventCount();
method public int getFifoReservedEventCount();
+ method public int getId();
method public int getMaxDelay();
method public float getMaximumRange();
method public int getMinDelay();
@@ -13906,7 +13928,9 @@
method public java.util.UUID getUuid();
method public java.lang.String getVendor();
method public int getVersion();
+ method public boolean isAdditionalInfoSupported();
method public boolean isDataInjectionSupported();
+ method public boolean isDynamicSensor();
method public boolean isWakeUpSensor();
field public static final int REPORTING_MODE_CONTINUOUS = 0; // 0x0
field public static final int REPORTING_MODE_ONE_SHOT = 2; // 0x2
@@ -14031,8 +14055,9 @@
method public deprecated int getSensors();
method public boolean initDataInjection(boolean);
method public boolean injectSensorData(android.hardware.Sensor, float[], int, long);
- method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback);
- method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback, android.os.Handler);
+ method public boolean isDynamicSensorDiscoverySupported();
+ method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorCallback);
+ method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorCallback, android.os.Handler);
method public deprecated boolean registerListener(android.hardware.SensorListener, int);
method public deprecated boolean registerListener(android.hardware.SensorListener, int, int);
method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int);
@@ -14041,7 +14066,7 @@
method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int, int, android.os.Handler);
method public static boolean remapCoordinateSystem(float[], int, int, float[]);
method public boolean requestTriggerSensor(android.hardware.TriggerEventListener, android.hardware.Sensor);
- method public void unregisterDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback);
+ method public void unregisterDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorCallback);
method public deprecated void unregisterListener(android.hardware.SensorListener);
method public deprecated void unregisterListener(android.hardware.SensorListener, int);
method public void unregisterListener(android.hardware.SensorEventListener, android.hardware.Sensor);
@@ -14106,8 +14131,8 @@
field public static final float STANDARD_GRAVITY = 9.80665f;
}
- public static abstract class SensorManager.DynamicSensorConnectionCallback {
- ctor public SensorManager.DynamicSensorConnectionCallback();
+ public static abstract class SensorManager.DynamicSensorCallback {
+ ctor public SensorManager.DynamicSensorCallback();
method public void onDynamicSensorConnected(android.hardware.Sensor);
method public void onDynamicSensorDisconnected(android.hardware.Sensor);
}
@@ -15256,6 +15281,7 @@
ctor public ContextHubInfo();
method public int describeContents();
method public int getId();
+ method public int getMaxPacketLengthBytes();
method public android.hardware.location.MemoryRegion[] getMemoryRegions();
method public java.lang.String getName();
method public float getPeakMips();
@@ -15283,10 +15309,6 @@
method public int sendMessage(int, int, android.hardware.location.ContextHubMessage);
method public int unloadNanoApp(int);
method public int unregisterCallback(android.hardware.location.ContextHubManager.Callback);
- field public static final int ANY_HUB = -1; // 0xffffffff
- field public static final int MSG_DATA_SEND = 3; // 0x3
- field public static final int MSG_LOAD_NANO_APP = 1; // 0x1
- field public static final int MSG_UNLOAD_NANO_APP = 2; // 0x2
}
public static abstract class ContextHubManager.Callback {
@@ -15480,7 +15502,7 @@
public class NanoAppInstanceInfo {
ctor public NanoAppInstanceInfo();
method public int describeContents();
- method public int getAppId();
+ method public long getAppId();
method public int getAppVersion();
method public int getContexthubId();
method public int getHandle();
@@ -15491,17 +15513,6 @@
method public int getNeededWriteMemBytes();
method public int[] getOutputEvents();
method public java.lang.String getPublisher();
- method public void setAppId(int);
- method public void setAppVersion(int);
- method public void setContexthubId(int);
- method public void setHandle(int);
- method public void setName(java.lang.String);
- method public void setNeededExecMemBytes(int);
- method public void setNeededReadMemBytes(int);
- method public void setNeededSensors(int[]);
- method public void setNeededWriteMemBytes(int);
- method public void setOutputEvents(int[]);
- method public void setPublisher(java.lang.String);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.hardware.location.NanoAppInstanceInfo> CREATOR;
}
@@ -15999,7 +16010,6 @@
public static abstract interface UCharacter.BidiPairedBracketType {
field public static final int CLOSE = 2; // 0x2
- field public static final int COUNT = 3; // 0x3
field public static final int NONE = 0; // 0x0
field public static final int OPEN = 1; // 0x1
}
@@ -16008,7 +16018,6 @@
field public static final int CANONICAL = 1; // 0x1
field public static final int CIRCLE = 3; // 0x3
field public static final int COMPAT = 2; // 0x2
- field public static final int COUNT = 18; // 0x12
field public static final int FINAL = 4; // 0x4
field public static final int FONT = 5; // 0x5
field public static final int FRACTION = 6; // 0x6
@@ -16028,7 +16037,6 @@
public static abstract interface UCharacter.EastAsianWidth {
field public static final int AMBIGUOUS = 1; // 0x1
- field public static final int COUNT = 6; // 0x6
field public static final int FULLWIDTH = 3; // 0x3
field public static final int HALFWIDTH = 2; // 0x2
field public static final int NARROW = 4; // 0x4
@@ -16038,7 +16046,6 @@
public static abstract interface UCharacter.GraphemeClusterBreak {
field public static final int CONTROL = 1; // 0x1
- field public static final int COUNT = 13; // 0xd
field public static final int CR = 2; // 0x2
field public static final int EXTEND = 3; // 0x3
field public static final int L = 4; // 0x4
@@ -16054,7 +16061,6 @@
}
public static abstract interface UCharacter.HangulSyllableType {
- field public static final int COUNT = 6; // 0x6
field public static final int LEADING_JAMO = 1; // 0x1
field public static final int LVT_SYLLABLE = 5; // 0x5
field public static final int LV_SYLLABLE = 4; // 0x4
@@ -16070,7 +16076,6 @@
field public static final int BEH = 4; // 0x4
field public static final int BETH = 5; // 0x5
field public static final int BURUSHASKI_YEH_BARREE = 54; // 0x36
- field public static final int COUNT = 86; // 0x56
field public static final int DAL = 6; // 0x6
field public static final int DALATH_RISH = 7; // 0x7
field public static final int E = 8; // 0x8
@@ -16155,7 +16160,6 @@
}
public static abstract interface UCharacter.JoiningType {
- field public static final int COUNT = 6; // 0x6
field public static final int DUAL_JOINING = 2; // 0x2
field public static final int JOIN_CAUSING = 1; // 0x1
field public static final int LEFT_JOINING = 3; // 0x3
@@ -16178,7 +16182,6 @@
field public static final int COMPLEX_CONTEXT = 24; // 0x18
field public static final int CONDITIONAL_JAPANESE_STARTER = 37; // 0x25
field public static final int CONTINGENT_BREAK = 7; // 0x7
- field public static final int COUNT = 40; // 0x28
field public static final int EXCLAMATION = 11; // 0xb
field public static final int GLUE = 12; // 0xc
field public static final int H2 = 31; // 0x1f
@@ -16210,7 +16213,6 @@
}
public static abstract interface UCharacter.NumericType {
- field public static final int COUNT = 4; // 0x4
field public static final int DECIMAL = 1; // 0x1
field public static final int DIGIT = 2; // 0x2
field public static final int NONE = 0; // 0x0
@@ -16220,7 +16222,6 @@
public static abstract interface UCharacter.SentenceBreak {
field public static final int ATERM = 1; // 0x1
field public static final int CLOSE = 2; // 0x2
- field public static final int COUNT = 15; // 0xf
field public static final int CR = 11; // 0xb
field public static final int EXTEND = 12; // 0xc
field public static final int FORMAT = 3; // 0x3
@@ -16363,7 +16364,6 @@
field public static final android.icu.lang.UCharacter.UnicodeBlock COPTIC_EPACT_NUMBERS;
field public static final int COPTIC_EPACT_NUMBERS_ID = 223; // 0xdf
field public static final int COPTIC_ID = 132; // 0x84
- field public static final int COUNT = 263; // 0x107
field public static final android.icu.lang.UCharacter.UnicodeBlock COUNTING_ROD_NUMERALS;
field public static final int COUNTING_ROD_NUMERALS_ID = 154; // 0x9a
field public static final android.icu.lang.UCharacter.UnicodeBlock CUNEIFORM;
@@ -16777,7 +16777,6 @@
public static abstract interface UCharacter.WordBreak {
field public static final int ALETTER = 1; // 0x1
- field public static final int COUNT = 17; // 0x11
field public static final int CR = 8; // 0x8
field public static final int DOUBLE_QUOTE = 16; // 0x10
field public static final int EXTEND = 9; // 0x9
@@ -16808,7 +16807,6 @@
}
public static abstract interface UCharacterEnums.ECharacterCategory {
- field public static final byte CHAR_CATEGORY_COUNT = 30; // 0x1e
field public static final byte COMBINING_SPACING_MARK = 8; // 0x8
field public static final byte CONNECTOR_PUNCTUATION = 22; // 0x16
field public static final byte CONTROL = 15; // 0xf
@@ -16848,7 +16846,6 @@
field public static final int ARABIC_NUMBER = 5; // 0x5
field public static final int BLOCK_SEPARATOR = 7; // 0x7
field public static final int BOUNDARY_NEUTRAL = 18; // 0x12
- field public static final int CHAR_DIRECTION_COUNT = 23; // 0x17
field public static final int COMMON_NUMBER_SEPARATOR = 6; // 0x6
field public static final byte DIRECTIONALITY_ARABIC_NUMBER = 5; // 0x5
field public static final byte DIRECTIONALITY_BOUNDARY_NEUTRAL = 18; // 0x12
@@ -16901,7 +16898,6 @@
field public static final int BIDI_MIRRORING_GLYPH = 16385; // 0x4001
field public static final int BIDI_PAIRED_BRACKET = 16397; // 0x400d
field public static final int BIDI_PAIRED_BRACKET_TYPE = 4117; // 0x1015
- field public static final int BINARY_LIMIT = 57; // 0x39
field public static final int BINARY_START = 0; // 0x0
field public static final int BLOCK = 4097; // 0x1001
field public static final int CANONICAL_COMBINING_CLASS = 4098; // 0x1002
@@ -16920,7 +16916,6 @@
field public static final int DEFAULT_IGNORABLE_CODE_POINT = 5; // 0x5
field public static final int DEPRECATED = 6; // 0x6
field public static final int DIACRITIC = 7; // 0x7
- field public static final int DOUBLE_LIMIT = 12289; // 0x3001
field public static final int DOUBLE_START = 12288; // 0x3000
field public static final int EAST_ASIAN_WIDTH = 4100; // 0x1004
field public static final int EXTENDER = 8; // 0x8
@@ -16939,7 +16934,6 @@
field public static final int IDS_TRINARY_OPERATOR = 19; // 0x13
field public static final int ID_CONTINUE = 15; // 0xf
field public static final int ID_START = 16; // 0x10
- field public static final int INT_LIMIT = 4118; // 0x1016
field public static final int INT_START = 4096; // 0x1000
field public static final int JOINING_GROUP = 4102; // 0x1006
field public static final int JOINING_TYPE = 4103; // 0x1007
@@ -16949,7 +16943,6 @@
field public static final int LOGICAL_ORDER_EXCEPTION = 21; // 0x15
field public static final int LOWERCASE = 22; // 0x16
field public static final int LOWERCASE_MAPPING = 16388; // 0x4004
- field public static final int MASK_LIMIT = 8193; // 0x2001
field public static final int MASK_START = 8192; // 0x2000
field public static final int MATH = 23; // 0x17
field public static final int NAME = 16389; // 0x4005
@@ -16964,7 +16957,6 @@
field public static final int NONCHARACTER_CODE_POINT = 24; // 0x18
field public static final int NUMERIC_TYPE = 4105; // 0x1009
field public static final int NUMERIC_VALUE = 12288; // 0x3000
- field public static final int OTHER_PROPERTY_LIMIT = 28673; // 0x7001
field public static final int OTHER_PROPERTY_START = 28672; // 0x7000
field public static final int PATTERN_SYNTAX = 42; // 0x2a
field public static final int PATTERN_WHITE_SPACE = 43; // 0x2b
@@ -16984,7 +16976,6 @@
field public static final int SIMPLE_TITLECASE_MAPPING = 16392; // 0x4008
field public static final int SIMPLE_UPPERCASE_MAPPING = 16393; // 0x4009
field public static final int SOFT_DOTTED = 27; // 0x1b
- field public static final int STRING_LIMIT = 16398; // 0x400e
field public static final int STRING_START = 16384; // 0x4000
field public static final int S_TERM = 35; // 0x23
field public static final int TERMINAL_PUNCTUATION = 28; // 0x1c
@@ -17001,7 +16992,6 @@
}
public static abstract interface UProperty.NameChoice {
- field public static final int COUNT = 2; // 0x2
field public static final int LONG = 1; // 0x1
field public static final int SHORT = 0; // 0x0
}
@@ -17046,7 +17036,6 @@
field public static final int CHAM = 66; // 0x42
field public static final int CHEROKEE = 6; // 0x6
field public static final int CIRTH = 67; // 0x43
- field public static final int CODE_LIMIT = 167; // 0xa7
field public static final int COMMON = 0; // 0x0
field public static final int COPTIC = 7; // 0x7
field public static final int CUNEIFORM = 101; // 0x65
@@ -17441,7 +17430,6 @@
public final class CollationKey implements java.lang.Comparable {
ctor public CollationKey(java.lang.String, byte[]);
- ctor public CollationKey(java.lang.String, android.icu.text.RawCollationKey);
method public int compareTo(android.icu.text.CollationKey);
method public boolean equals(android.icu.text.CollationKey);
method public android.icu.text.CollationKey getBound(int, int);
@@ -17451,7 +17439,6 @@
}
public static final class CollationKey.BoundMode {
- field public static final int COUNT = 3; // 0x3
field public static final int LOWER = 0; // 0x0
field public static final int UPPER = 1; // 0x1
field public static final int UPPER_LONG = 2; // 0x2
@@ -17483,7 +17470,6 @@
method public static final java.lang.String[] getKeywordValuesForLocale(java.lang.String, android.icu.util.ULocale, boolean);
method public static final java.lang.String[] getKeywords();
method public int getMaxVariable();
- method public abstract android.icu.text.RawCollationKey getRawCollationKey(java.lang.String, android.icu.text.RawCollationKey);
method public int[] getReorderCodes();
method public int getStrength();
method public android.icu.text.UnicodeSet getTailoredSet();
@@ -17523,7 +17509,6 @@
field public static final int DEFAULT = -1; // 0xffffffff
field public static final int DIGIT = 4100; // 0x1004
field public static final int FIRST = 4096; // 0x1000
- field public static final int LIMIT = 4101; // 0x1005
field public static final int NONE = 103; // 0x67
field public static final int OTHERS = 103; // 0x67
field public static final int PUNCTUATION = 4097; // 0x1001
@@ -17636,7 +17621,6 @@
field public static final int DOW_LOCAL_FIELD = 19; // 0x13
field public static final int ERA_FIELD = 0; // 0x0
field public static final int EXTENDED_YEAR_FIELD = 20; // 0x14
- field public static final int FIELD_COUNT = 36; // 0x24
field public static final int FRACTIONAL_SECOND_FIELD = 8; // 0x8
field public static final int FULL = 0; // 0x0
field public static final java.lang.String GENERIC_TZ = "vvvv";
@@ -17880,7 +17864,6 @@
field public static final int MONTH = 3; // 0x3
field public static final int QUARTER = 2; // 0x2
field public static final int SECOND = 13; // 0xd
- field public static final int TYPE_LIMIT = 16; // 0x10
field public static final int WEEKDAY = 6; // 0x6
field public static final int WEEK_OF_MONTH = 5; // 0x5
field public static final int WEEK_OF_YEAR = 4; // 0x4
@@ -18511,14 +18494,6 @@
enum_constant public static final android.icu.text.PluralRules.PluralType ORDINAL;
}
- public final class RawCollationKey extends android.icu.util.ByteArrayWrapper {
- ctor public RawCollationKey();
- ctor public RawCollationKey(int);
- ctor public RawCollationKey(byte[]);
- ctor public RawCollationKey(byte[], int);
- method public int compareTo(android.icu.text.RawCollationKey);
- }
-
public final class RelativeDateTimeFormatter {
method public java.lang.String combineDateAndTime(java.lang.String, java.lang.String);
method public java.lang.String format(double, android.icu.text.RelativeDateTimeFormatter.Direction, android.icu.text.RelativeDateTimeFormatter.RelativeUnit);
@@ -18602,7 +18577,6 @@
method public android.icu.text.CollationKey getCollationKey(java.lang.String);
method public void getContractionsAndExpansions(android.icu.text.UnicodeSet, android.icu.text.UnicodeSet, boolean) throws java.lang.Exception;
method public boolean getNumericCollation();
- method public android.icu.text.RawCollationKey getRawCollationKey(java.lang.String, android.icu.text.RawCollationKey);
method public java.lang.String getRules();
method public java.lang.String getRules(boolean);
method public android.icu.util.VersionInfo getUCAVersion();
@@ -19097,19 +19071,6 @@
field public static final int BE = 0; // 0x0
}
- public class ByteArrayWrapper implements java.lang.Comparable {
- ctor public ByteArrayWrapper();
- ctor public ByteArrayWrapper(byte[], int);
- ctor public ByteArrayWrapper(java.nio.ByteBuffer);
- method public final android.icu.util.ByteArrayWrapper append(byte[], int, int);
- method public int compareTo(android.icu.util.ByteArrayWrapper);
- method public android.icu.util.ByteArrayWrapper ensureCapacity(int);
- method public final byte[] releaseBytes();
- method public final android.icu.util.ByteArrayWrapper set(byte[], int, int);
- field public byte[] bytes;
- field public int size;
- }
-
abstract class CECalendar extends android.icu.util.Calendar {
ctor protected CECalendar();
ctor protected CECalendar(android.icu.util.TimeZone);
@@ -20422,10 +20383,19 @@
field public static final int MULTIPATH_INDICATOR_DETECTED = 1; // 0x1
field public static final int MULTIPATH_INDICATOR_NOT_USED = 2; // 0x2
field public static final int MULTIPATH_INDICATOR_UNKNOWN = 0; // 0x0
+ field public static final int STATE_BDS_D2_BIT_SYNC = 256; // 0x100
+ field public static final int STATE_BDS_D2_SUBFRAME_SYNC = 512; // 0x200
field public static final int STATE_BIT_SYNC = 2; // 0x2
field public static final int STATE_CODE_LOCK = 1; // 0x1
+ field public static final int STATE_GAL_E1BC_CODE_LOCK = 1024; // 0x400
+ field public static final int STATE_GAL_E1B_PAGE_SYNC = 4096; // 0x1000
+ field public static final int STATE_GAL_E1C_2ND_CODE_LOCK = 2048; // 0x800
+ field public static final int STATE_GLO_STRING_SYNC = 64; // 0x40
+ field public static final int STATE_GLO_TOD_DECODED = 128; // 0x80
field public static final int STATE_MSEC_AMBIGUOUS = 16; // 0x10
+ field public static final int STATE_SBAS_SYNC = 8192; // 0x2000
field public static final int STATE_SUBFRAME_SYNC = 4; // 0x4
+ field public static final int STATE_SYMBOL_SYNC = 32; // 0x20
field public static final int STATE_TOW_DECODED = 8; // 0x8
field public static final int STATE_UNKNOWN = 0; // 0x0
}
@@ -21328,7 +21298,7 @@
public static abstract class AudioManager.AudioRecordingCallback {
ctor public AudioManager.AudioRecordingCallback();
- method public void onRecordConfigChanged(android.media.AudioRecordingConfiguration[]);
+ method public void onRecordingConfigChanged(android.media.AudioRecordingConfiguration[]);
}
public static abstract interface AudioManager.OnAudioFocusChangeListener {
@@ -21589,7 +21559,6 @@
}
public abstract class DrmInitData {
- ctor public DrmInitData();
method public abstract android.media.DrmInitData.SchemeInitData get(java.util.UUID);
}
@@ -21608,6 +21577,7 @@
method public int getAttributeInt(java.lang.String, int);
method public boolean getLatLong(float[]);
method public byte[] getThumbnail();
+ method public long[] getThumbnailRange();
method public boolean hasThumbnail();
method public void saveAttributes() throws java.io.IOException;
method public void setAttribute(java.lang.String, java.lang.String);
@@ -21620,7 +21590,7 @@
field public static final int ORIENTATION_TRANSPOSE = 5; // 0x5
field public static final int ORIENTATION_TRANSVERSE = 7; // 0x7
field public static final int ORIENTATION_UNDEFINED = 0; // 0x0
- field public static final java.lang.String TAG_APERTURE = "FNumber";
+ field public static final deprecated java.lang.String TAG_APERTURE = "FNumber";
field public static final java.lang.String TAG_APERTURE_VALUE = "ApertureValue";
field public static final java.lang.String TAG_ARTIST = "Artist";
field public static final java.lang.String TAG_BITS_PER_SAMPLE = "BitsPerSample";
@@ -21691,7 +21661,7 @@
field public static final java.lang.String TAG_IMAGE_UNIQUE_ID = "ImageUniqueID";
field public static final java.lang.String TAG_IMAGE_WIDTH = "ImageWidth";
field public static final java.lang.String TAG_INTEROPERABILITY_INDEX = "InteroperabilityIndex";
- field public static final java.lang.String TAG_ISO = "ISOSpeedRatings";
+ field public static final deprecated java.lang.String TAG_ISO = "ISOSpeedRatings";
field public static final java.lang.String TAG_ISO_SPEED_RATINGS = "ISOSpeedRatings";
field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT = "JPEGInterchangeFormat";
field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = "JPEGInterchangeFormatLength";
@@ -21843,8 +21813,8 @@
public class MediaActionSound {
ctor public MediaActionSound();
- method public synchronized void load(int);
- method public synchronized void play(int);
+ method public void load(int);
+ method public void play(int);
method public void release();
field public static final int FOCUS_COMPLETE = 1; // 0x1
field public static final int SHUTTER_CLICK = 0; // 0x0
@@ -22099,12 +22069,13 @@
field public static final int DolbyVisionLevelUhd30 = 64; // 0x40
field public static final int DolbyVisionLevelUhd48 = 128; // 0x80
field public static final int DolbyVisionLevelUhd60 = 256; // 0x100
- field public static final int DolbyVisionProfileDvavDen = 2; // 0x2
- field public static final int DolbyVisionProfileDvavDer = 1; // 0x1
- field public static final int DolbyVisionProfileDvheDen = 4; // 0x4
- field public static final int DolbyVisionProfileDvheDer = 3; // 0x3
- field public static final int DolbyVisionProfileDvheDtr = 5; // 0x5
- field public static final int DolbyVisionProfileDvheStn = 6; // 0x6
+ field public static final int DolbyVisionProfileDvavPen = 2; // 0x2
+ field public static final int DolbyVisionProfileDvavPer = 1; // 0x1
+ field public static final int DolbyVisionProfileDvheDen = 8; // 0x8
+ field public static final int DolbyVisionProfileDvheDer = 4; // 0x4
+ field public static final int DolbyVisionProfileDvheDth = 64; // 0x40
+ field public static final int DolbyVisionProfileDvheDtr = 16; // 0x10
+ field public static final int DolbyVisionProfileDvheStn = 32; // 0x20
field public static final int H263Level10 = 1; // 0x1
field public static final int H263Level20 = 2; // 0x2
field public static final int H263Level30 = 4; // 0x4
@@ -23845,7 +23816,7 @@
method public void subscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback);
method public void subscribe(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SubscriptionCallback);
method public void unsubscribe(java.lang.String);
- method public void unsubscribe(java.lang.String, android.os.Bundle);
+ method public void unsubscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback);
field public static final java.lang.String EXTRA_PAGE = "android.media.browse.extra.PAGE";
field public static final java.lang.String EXTRA_PAGE_SIZE = "android.media.browse.extra.PAGE_SIZE";
}
@@ -24656,6 +24627,7 @@
method public android.media.tv.TvInputInfo.Builder setHdmiDeviceInfo(android.hardware.hdmi.HdmiDeviceInfo);
method public android.media.tv.TvInputInfo.Builder setIcon(android.graphics.drawable.Icon);
method public android.media.tv.TvInputInfo.Builder setIcon(android.graphics.drawable.Icon, int);
+ method public android.media.tv.TvInputInfo.Builder setLabel(java.lang.CharSequence);
method public android.media.tv.TvInputInfo.Builder setLabel(int);
method public android.media.tv.TvInputInfo.Builder setParentId(java.lang.String);
method public android.media.tv.TvInputInfo.Builder setTunerCount(int);
@@ -24670,7 +24642,7 @@
}
public final class TvInputManager {
- method public android.media.tv.TvInputManager.Hardware acquireTvInputHardware(int, android.media.tv.TvInputManager.HardwareCallback, android.media.tv.TvInputInfo);
+ method public android.media.tv.TvInputManager.Hardware acquireTvInputHardware(int, android.media.tv.TvInputInfo, android.media.tv.TvInputManager.HardwareCallback);
method public void addBlockedRating(android.media.tv.TvContentRating);
method public boolean captureFrame(java.lang.String, android.view.Surface, android.media.tv.TvStreamConfig);
method public java.util.List<android.media.tv.TvStreamConfig> getAvailableTvStreamConfigList(java.lang.String);
@@ -25222,6 +25194,7 @@
method public boolean isDefaultNetworkActive();
method public static deprecated boolean isNetworkTypeValid(int);
method public boolean isTetheringSupported();
+ method public void registerDefaultNetworkCallback(android.net.ConnectivityManager.NetworkCallback);
method public void registerNetworkCallback(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
method public void registerNetworkCallback(android.net.NetworkRequest, android.app.PendingIntent);
method public void releaseNetworkRequest(android.app.PendingIntent);
@@ -26802,7 +26775,9 @@
method public void configureWifiChange(android.net.wifi.WifiScanner.WifiChangeSettings);
method public boolean getScanResults();
method public void startBackgroundScan(android.net.wifi.WifiScanner.ScanSettings, android.net.wifi.WifiScanner.ScanListener);
+ method public void startBackgroundScan(android.net.wifi.WifiScanner.ScanSettings, android.net.wifi.WifiScanner.ScanListener, android.os.WorkSource);
method public void startScan(android.net.wifi.WifiScanner.ScanSettings, android.net.wifi.WifiScanner.ScanListener);
+ method public void startScan(android.net.wifi.WifiScanner.ScanSettings, android.net.wifi.WifiScanner.ScanListener, android.os.WorkSource);
method public void startTrackingBssids(android.net.wifi.WifiScanner.BssidInfo[], int, android.net.wifi.WifiScanner.BssidListener);
method public void startTrackingWifiChange(android.net.wifi.WifiScanner.WifiChangeListener);
method public void stopBackgroundScan(android.net.wifi.WifiScanner.ScanListener);
@@ -30981,6 +30956,7 @@
field public static final int TEMPERATURE_CURRENT = 0; // 0x0
field public static final int TEMPERATURE_SHUTDOWN = 2; // 0x2
field public static final int TEMPERATURE_THROTTLING = 1; // 0x1
+ field public static final int TEMPERATURE_THROTTLING_BELOW_VR_MIN = 3; // 0x3
field public static final float UNDEFINED_TEMPERATURE = -3.4028235E38f;
}
@@ -31341,6 +31317,7 @@
method public boolean isPowerSaveMode();
method public boolean isScreenBrightnessBoosted();
method public deprecated boolean isScreenOn();
+ method public boolean isSustainedPerformanceModeSupported();
method public boolean isWakeLockLevelSupported(int);
method public android.os.PowerManager.WakeLock newWakeLock(int, java.lang.String);
method public void reboot(java.lang.String);
@@ -31356,6 +31333,7 @@
field public static final int RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY = 1; // 0x1
field public static final deprecated int SCREEN_BRIGHT_WAKE_LOCK = 10; // 0xa
field public static final deprecated int SCREEN_DIM_WAKE_LOCK = 6; // 0x6
+ field public static final int SUSTAINED_PERFORMANCE_WAKE_LOCK = 256; // 0x100
field public static final int USER_ACTIVITY_EVENT_ACCESSIBILITY = 3; // 0x3
field public static final int USER_ACTIVITY_EVENT_BUTTON = 1; // 0x1
field public static final int USER_ACTIVITY_EVENT_OTHER = 0; // 0x0
@@ -31569,12 +31547,13 @@
public class UpdateEngine {
ctor public UpdateEngine();
- method public void applyPayload(java.lang.String, long, long, java.lang.String[]) throws android.os.RemoteException;
- method public boolean bind(android.os.UpdateEngineCallback, android.os.Handler) throws android.os.RemoteException;
- method public boolean bind(android.os.UpdateEngineCallback) throws android.os.RemoteException;
- method public void cancel() throws android.os.RemoteException;
- method public void resume() throws android.os.RemoteException;
- method public void suspend() throws android.os.RemoteException;
+ method public void applyPayload(java.lang.String, long, long, java.lang.String[]);
+ method public boolean bind(android.os.UpdateEngineCallback, android.os.Handler);
+ method public boolean bind(android.os.UpdateEngineCallback);
+ method public void cancel();
+ method public void resetStatus();
+ method public void resume();
+ method public void suspend();
}
public static final class UpdateEngine.ErrorCodeConstants {
@@ -31780,7 +31759,7 @@
method public android.os.health.HealthStats[] takeUidSnapshots(int[]);
}
- public class TimerStat implements android.os.Parcelable {
+ public final class TimerStat implements android.os.Parcelable {
ctor public TimerStat();
ctor public TimerStat(int, long);
ctor public TimerStat(android.os.Parcel);
@@ -31878,8 +31857,8 @@
public class StorageManager {
method public java.lang.String getMountedObbPath(java.lang.String);
- method public android.os.storage.StorageVolume getPrimaryVolume();
- method public android.os.storage.StorageVolume[] getVolumeList();
+ method public android.os.storage.StorageVolume getPrimaryStorageVolume();
+ method public java.util.List<android.os.storage.StorageVolume> getStorageVolumes();
method public boolean isEncrypted(java.io.File);
method public boolean isObbMounted(java.lang.String);
method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener);
@@ -32036,7 +32015,6 @@
method protected void onClick();
method protected android.view.View onCreateView(android.view.ViewGroup);
method public void onDependencyChanged(android.preference.Preference, boolean);
- method protected void onDetachedFromActivity();
method protected java.lang.Object onGetDefaultValue(android.content.res.TypedArray, int);
method public void onParentChanged(android.preference.Preference, boolean);
method protected void onPrepareForRemoval();
@@ -32207,6 +32185,9 @@
method public android.content.SharedPreferences getSharedPreferences();
method public int getSharedPreferencesMode();
method public java.lang.String getSharedPreferencesName();
+ method public boolean isStorageCredentialProtected();
+ method public boolean isStorageDefault();
+ method public boolean isStorageDeviceProtected();
method public static void setDefaultValues(android.content.Context, int, boolean);
method public static void setDefaultValues(android.content.Context, java.lang.String, int, int, boolean);
method public void setSharedPreferencesMode(int);
@@ -32583,7 +32564,7 @@
method public android.print.PrinterInfo build();
method public android.print.PrinterInfo.Builder setCapabilities(android.print.PrinterCapabilitiesInfo);
method public android.print.PrinterInfo.Builder setDescription(java.lang.String);
- method public android.print.PrinterInfo.Builder setHasCustomPrinterIcon();
+ method public android.print.PrinterInfo.Builder setHasCustomPrinterIcon(boolean);
method public android.print.PrinterInfo.Builder setIconResourceId(int);
method public android.print.PrinterInfo.Builder setInfoIntent(android.app.PendingIntent);
method public android.print.PrinterInfo.Builder setName(java.lang.String);
@@ -32635,6 +32616,7 @@
method public boolean isStarted();
method public void setProgress(float);
method public void setStatus(java.lang.CharSequence);
+ method public void setStatus(int);
method public boolean setTag(java.lang.String);
method public boolean start();
}
@@ -32665,7 +32647,7 @@
method public final boolean isDestroyed();
method public final boolean isPrinterDiscoveryStarted();
method public abstract void onDestroy();
- method public void onRequestCustomPrinterIcon(android.print.PrinterId, android.printservice.CustomPrinterIconCallback);
+ method public void onRequestCustomPrinterIcon(android.print.PrinterId, android.os.CancellationSignal, android.printservice.CustomPrinterIconCallback);
method public abstract void onStartPrinterDiscovery(java.util.List<android.print.PrinterId>);
method public abstract void onStartPrinterStateTracking(android.print.PrinterId);
method public abstract void onStopPrinterDiscovery();
@@ -32676,6 +32658,29 @@
}
+package android.printservice.recommendation {
+
+ public final class RecommendationInfo implements android.os.Parcelable {
+ ctor public RecommendationInfo(java.lang.CharSequence, java.lang.CharSequence, int, boolean);
+ method public int describeContents();
+ method public java.lang.CharSequence getName();
+ method public int getNumDiscoveredPrinters();
+ method public java.lang.CharSequence getPackageName();
+ method public boolean recommendsMultiVendorService();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.printservice.recommendation.RecommendationInfo> CREATOR;
+ }
+
+ public abstract class RecommendationService extends android.app.Service {
+ ctor public RecommendationService();
+ method public final android.os.IBinder onBind(android.content.Intent);
+ method public abstract void onConnected();
+ method public abstract void onDisconnected();
+ method public final void updateRecommendations(java.util.List<android.printservice.recommendation.RecommendationInfo>);
+ }
+
+}
+
package android.provider {
public final class AlarmClock {
@@ -32711,6 +32716,7 @@
public class BlockedNumberContract {
method public static boolean canCurrentUserBlockNumbers(android.content.Context);
method public static boolean isBlocked(android.content.Context, java.lang.String);
+ method public static int unblock(android.content.Context, java.lang.String);
field public static final java.lang.String AUTHORITY = "com.android.blockednumber";
field public static final android.net.Uri AUTHORITY_URI;
}
@@ -33070,6 +33076,7 @@
field public static final int REJECTED_TYPE = 5; // 0x5
field public static final java.lang.String TRANSCRIPTION = "transcription";
field public static final java.lang.String TYPE = "type";
+ field public static final java.lang.String VIA_NUMBER = "via_number";
field public static final int VOICEMAIL_TYPE = 4; // 0x4
field public static final java.lang.String VOICEMAIL_URI = "voicemail_uri";
}
@@ -34250,6 +34257,7 @@
field public static final java.lang.String EXTRA_EXCLUDE_SELF = "android.provider.extra.EXCLUDE_SELF";
field public static final java.lang.String EXTRA_INFO = "info";
field public static final java.lang.String EXTRA_LOADING = "loading";
+ field public static final java.lang.String EXTRA_ORIENTATION = "android.provider.extra.ORIENTATION";
field public static final java.lang.String EXTRA_PROMPT = "android.provider.extra.PROMPT";
field public static final java.lang.String PROVIDER_INTERFACE = "android.content.action.DOCUMENTS_PROVIDER";
}
@@ -34835,6 +34843,7 @@
field public static final java.lang.String ACTION_VOICE_CONTROL_BATTERY_SAVER_MODE = "android.settings.VOICE_CONTROL_BATTERY_SAVER_MODE";
field public static final java.lang.String ACTION_VOICE_CONTROL_DO_NOT_DISTURB_MODE = "android.settings.VOICE_CONTROL_DO_NOT_DISTURB_MODE";
field public static final java.lang.String ACTION_VOICE_INPUT_SETTINGS = "android.settings.VOICE_INPUT_SETTINGS";
+ field public static final java.lang.String ACTION_VPN_SETTINGS = "android.settings.VPN_SETTINGS";
field public static final java.lang.String ACTION_VR_LISTENER_SETTINGS = "android.settings.VR_LISTENER_SETTINGS";
field public static final java.lang.String ACTION_WIFI_IP_SETTINGS = "android.settings.WIFI_IP_SETTINGS";
field public static final java.lang.String ACTION_WIFI_SETTINGS = "android.settings.WIFI_SETTINGS";
@@ -37027,7 +37036,6 @@
method public boolean onMenuOpened(int, android.view.Menu);
method public void onPanelClosed(int, android.view.Menu);
method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
- method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu, int);
method public boolean onSearchRequested(android.view.SearchEvent);
method public boolean onSearchRequested();
method public void onWakeUp();
@@ -38712,6 +38720,7 @@
method public final java.util.List<android.telecom.Connection> getConferenceableConnections();
method public final deprecated long getConnectTimeMillis();
method public final int getConnectionCapabilities();
+ method public final int getConnectionProperties();
method public final long getConnectionTime();
method public final java.util.List<android.telecom.Connection> getConnections();
method public final android.telecom.DisconnectCause getDisconnectCause();
@@ -38742,6 +38751,7 @@
method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
method public final deprecated void setConnectTimeMillis(long);
method public final void setConnectionCapabilities(int);
+ method public final void setConnectionProperties(int);
method public final void setConnectionTime(long);
method public final void setDialing();
method public final void setDisconnected(android.telecom.DisconnectCause);
@@ -38772,6 +38782,7 @@
method public final android.telecom.Conference getConference();
method public final java.util.List<android.telecom.Conferenceable> getConferenceables();
method public final int getConnectionCapabilities();
+ method public final int getConnectionProperties();
method public final android.telecom.DisconnectCause getDisconnectCause();
method public final android.os.Bundle getExtras();
method public final int getState();
@@ -38796,6 +38807,7 @@
method public void onStateChanged(int);
method public void onStopDtmfTone();
method public void onUnhold();
+ method public static java.lang.String propertiesToString(int);
method public final void putExtras(android.os.Bundle);
method public final void removeExtras(java.util.List<java.lang.String>);
method public void sendConnectionEvent(java.lang.String, android.os.Bundle);
@@ -38806,6 +38818,7 @@
method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
method public final void setConferenceables(java.util.List<android.telecom.Conferenceable>);
method public final void setConnectionCapabilities(int);
+ method public final void setConnectionProperties(int);
method public final void setDialing();
method public final void setDisconnected(android.telecom.DisconnectCause);
method public final deprecated void setExtras(android.os.Bundle);
@@ -38822,12 +38835,11 @@
method public static java.lang.String stateToString(int);
field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 8388608; // 0x800000
field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
- field public static final int CAPABILITY_CAN_PULL_CALL = 33554432; // 0x2000000
+ field public static final int CAPABILITY_CAN_PULL_CALL = 16777216; // 0x1000000
field public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 4194304; // 0x400000
field public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 524288; // 0x80000
field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
field public static final int CAPABILITY_HOLD = 1; // 0x1
- field public static final int CAPABILITY_IS_EXTERNAL_CALL = 16777216; // 0x1000000
field public static final int CAPABILITY_MANAGE_CONFERENCE = 128; // 0x80
field public static final int CAPABILITY_MERGE_CONFERENCE = 4; // 0x4
field public static final int CAPABILITY_MUTE = 64; // 0x40
@@ -38845,6 +38857,7 @@
field public static final java.lang.String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
field public static final java.lang.String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS";
field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
+ field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
field public static final int STATE_ACTIVE = 4; // 0x4
field public static final int STATE_DIALING = 3; // 0x3
field public static final int STATE_DISCONNECTED = 6; // 0x6
@@ -39122,6 +39135,7 @@
method public void disconnect();
method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections();
method public final int getConnectionCapabilities();
+ method public final int getConnectionProperties();
method public final java.util.List<android.telecom.RemoteConnection> getConnections();
method public android.telecom.DisconnectCause getDisconnectCause();
method public final android.os.Bundle getExtras();
@@ -39145,6 +39159,7 @@
method public void onConferenceableConnectionsChanged(android.telecom.RemoteConference, java.util.List<android.telecom.RemoteConnection>);
method public void onConnectionAdded(android.telecom.RemoteConference, android.telecom.RemoteConnection);
method public void onConnectionCapabilitiesChanged(android.telecom.RemoteConference, int);
+ method public void onConnectionPropertiesChanged(android.telecom.RemoteConference, int);
method public void onConnectionRemoved(android.telecom.RemoteConference, android.telecom.RemoteConnection);
method public void onDestroyed(android.telecom.RemoteConference);
method public void onDisconnected(android.telecom.RemoteConference, android.telecom.DisconnectCause);
@@ -39163,6 +39178,7 @@
method public android.telecom.RemoteConference getConference();
method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections();
method public int getConnectionCapabilities();
+ method public int getConnectionProperties();
method public android.telecom.DisconnectCause getDisconnectCause();
method public final android.os.Bundle getExtras();
method public int getState();
@@ -39193,6 +39209,7 @@
method public void onConferenceableConnectionsChanged(android.telecom.RemoteConnection, java.util.List<android.telecom.RemoteConnection>);
method public void onConnectionCapabilitiesChanged(android.telecom.RemoteConnection, int);
method public void onConnectionEvent(android.telecom.RemoteConnection, java.lang.String, android.os.Bundle);
+ method public void onConnectionPropertiesChanged(android.telecom.RemoteConnection, int);
method public void onDestroyed(android.telecom.RemoteConnection);
method public void onDisconnected(android.telecom.RemoteConnection, android.telecom.DisconnectCause);
method public void onExtrasChanged(android.telecom.RemoteConnection, android.os.Bundle);
@@ -39280,7 +39297,6 @@
method public boolean isRinging();
method public boolean isTtySupported();
method public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, java.lang.String);
- method public deprecated void launchManageBlockedNumbersActivity();
method public void placeCall(android.net.Uri, android.os.Bundle);
method public void registerPhoneAccount(android.telecom.PhoneAccount);
method public void showInCallScreen(boolean);
@@ -39399,6 +39415,7 @@
field public static final java.lang.String KEY_CARRIER_VT_AVAILABLE_BOOL = "carrier_vt_available_bool";
field public static final java.lang.String KEY_CARRIER_VVM_PACKAGE_NAME_STRING = "carrier_vvm_package_name_string";
field public static final java.lang.String KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL = "carrier_wfc_ims_available_bool";
+ field public static final java.lang.String KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL = "carrier_wfc_supports_wifi_only_bool";
field public static final java.lang.String KEY_CDMA_DTMF_TONE_DELAY_INT = "cdma_dtmf_tone_delay_int";
field public static final java.lang.String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array";
field public static final java.lang.String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array";
@@ -39994,7 +40011,8 @@
method public java.lang.String getDeviceSoftwareVersion();
method public java.lang.String getGroupIdLevel1();
method public java.lang.String getGroupIdLevel1(int);
- method public java.lang.String getIccSimChallengeResponse(int, java.lang.String);
+ method public java.lang.String getIccAuthentication(int, int, java.lang.String);
+ method public java.lang.String getIccAuthentication(int, int, int, java.lang.String);
method public java.lang.String getLine1AlphaTag(int);
method public java.lang.String getLine1Number();
method public java.lang.String getLine1Number(int);
@@ -40084,6 +40102,13 @@
field public static final java.lang.String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
field public static final java.lang.String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
field public static final java.lang.String ACTION_SHOW_VOICEMAIL_NOTIFICATION = "android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION";
+ field public static final int APPTYPE_CSIM = 4; // 0x4
+ field public static final int APPTYPE_ISIM = 5; // 0x5
+ field public static final int APPTYPE_RUIM = 3; // 0x3
+ field public static final int APPTYPE_SIM = 1; // 0x1
+ field public static final int APPTYPE_USIM = 2; // 0x2
+ field public static final int AUTHTYPE_EAP_AKA = 129; // 0x81
+ field public static final int AUTHTYPE_EAP_SIM = 128; // 0x80
field public static final int CALL_STATE_IDLE = 0; // 0x0
field public static final int CALL_STATE_OFFHOOK = 2; // 0x2
field public static final int CALL_STATE_RINGING = 1; // 0x1
@@ -40641,8 +40666,6 @@
method public java.lang.String getPackageResourcePath();
method public android.content.res.Resources getResources();
method public android.content.SharedPreferences getSharedPreferences(java.lang.String, int);
- method public android.content.SharedPreferences getSharedPreferences(java.io.File, int);
- method public java.io.File getSharedPreferencesPath(java.lang.String);
method public java.lang.Object getSystemService(java.lang.String);
method public java.lang.String getSystemServiceName(java.lang.Class<?>);
method public android.content.res.Resources.Theme getTheme();
@@ -45007,6 +45030,7 @@
method public boolean dispatchDragEvent(android.view.DragEvent);
method protected void dispatchDraw(android.graphics.Canvas);
method public void dispatchDrawableHotspotChanged(float, float);
+ method public void dispatchFinishTemporaryDetach();
method protected boolean dispatchGenericFocusedEvent(android.view.MotionEvent);
method public boolean dispatchGenericMotionEvent(android.view.MotionEvent);
method protected boolean dispatchGenericPointerEvent(android.view.MotionEvent);
@@ -45026,6 +45050,7 @@
method protected void dispatchSetActivated(boolean);
method protected void dispatchSetPressed(boolean);
method protected void dispatchSetSelected(boolean);
+ method public void dispatchStartTemporaryDetach();
method public void dispatchSystemUiVisibilityChanged(int);
method public boolean dispatchTouchEvent(android.view.MotionEvent);
method public boolean dispatchTrackballEvent(android.view.MotionEvent);
@@ -45043,6 +45068,7 @@
method public void findViewsWithText(java.util.ArrayList<android.view.View>, java.lang.CharSequence, int);
method protected deprecated boolean fitSystemWindows(android.graphics.Rect);
method public android.view.View focusSearch(int);
+ method public void forceHasOverlappingRendering(boolean);
method public void forceLayout();
method public static int generateViewId();
method public java.lang.CharSequence getAccessibilityClassName();
@@ -45088,6 +45114,7 @@
method public boolean getGlobalVisibleRect(android.graphics.Rect, android.graphics.Point);
method public final boolean getGlobalVisibleRect(android.graphics.Rect);
method public android.os.Handler getHandler();
+ method public final boolean getHasOverlappingRendering();
method public final int getHeight();
method public void getHitRect(android.graphics.Rect);
method public int getHorizontalFadingEdgeLength();
@@ -45236,6 +45263,7 @@
method public boolean isSelected();
method public boolean isShown();
method public boolean isSoundEffectsEnabled();
+ method public final boolean isTemporarilyDetached();
method public boolean isTextAlignmentResolved();
method public boolean isTextDirectionResolved();
method public boolean isVerticalFadingEdgeEnabled();
@@ -46823,8 +46851,6 @@
method public void setVisibleToUser(boolean);
method public void writeToParcel(android.os.Parcel, int);
field public static final int ACTION_ACCESSIBILITY_FOCUS = 64; // 0x40
- field public static final java.lang.String ACTION_ARGUMENT_CLICK_CHARACTER_INDEX_INT = "android.view.accessibility.action.ARGUMENT_CLICK_CHARACTER_INDEX_INT";
- field public static final java.lang.String ACTION_ARGUMENT_CLICK_SPAN_INDEX_INT = "android.view.accessibility.action.ARGUMENT_CLICK_SPAN_INDEX_INT";
field public static final java.lang.String ACTION_ARGUMENT_COLUMN_INT = "android.view.accessibility.action.ARGUMENT_COLUMN_INT";
field public static final java.lang.String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN = "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
field public static final java.lang.String ACTION_ARGUMENT_HTML_ELEMENT_STRING = "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
@@ -46995,6 +47021,7 @@
public final class AccessibilityWindowInfo implements android.os.Parcelable {
method public int describeContents();
+ method public android.view.accessibility.AccessibilityNodeInfo getAnchor();
method public void getBoundsInScreen(android.graphics.Rect);
method public android.view.accessibility.AccessibilityWindowInfo getChild(int);
method public int getChildCount();
@@ -47002,6 +47029,7 @@
method public int getLayer();
method public android.view.accessibility.AccessibilityWindowInfo getParent();
method public android.view.accessibility.AccessibilityNodeInfo getRoot();
+ method public java.lang.CharSequence getTitle();
method public int getType();
method public boolean isAccessibilityFocused();
method public boolean isActive();
@@ -47342,6 +47370,7 @@
ctor public BaseInputConnection(android.view.View, boolean);
method public boolean beginBatchEdit();
method public boolean clearMetaKeyStates(int);
+ method public void closeConnection();
method public boolean commitCompletion(android.view.inputmethod.CompletionInfo);
method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public boolean commitText(java.lang.CharSequence, int);
@@ -47509,6 +47538,7 @@
public abstract interface InputConnection {
method public abstract boolean beginBatchEdit();
method public abstract boolean clearMetaKeyStates(int);
+ method public abstract void closeConnection();
method public abstract boolean commitCompletion(android.view.inputmethod.CompletionInfo);
method public abstract boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public abstract boolean commitText(java.lang.CharSequence, int);
@@ -47541,6 +47571,7 @@
ctor public InputConnectionWrapper(android.view.inputmethod.InputConnection, boolean);
method public boolean beginBatchEdit();
method public boolean clearMetaKeyStates(int);
+ method public void closeConnection();
method public boolean commitCompletion(android.view.inputmethod.CompletionInfo);
method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public boolean commitText(java.lang.CharSequence, int);
@@ -48634,6 +48665,7 @@
method public int getPackageId(android.content.res.Resources, java.lang.String);
method public void invokeDrawGlFunctor(android.view.View, long, boolean);
method public boolean isTraceTagEnabled();
+ method public java.lang.Runnable setDrawGlFunctionDetachedCallback(android.view.View, java.lang.Runnable);
method public void setOnTraceEnabledChangeListener(android.webkit.WebViewDelegate.OnTraceEnabledChangeListener);
}
@@ -48644,7 +48676,6 @@
public final class WebViewFactory {
ctor public WebViewFactory();
method public static android.content.pm.PackageInfo getLoadedPackageInfo();
- method public static java.lang.String getWebViewPackageName();
method public static int loadWebViewNativeLibraryFromPackage(java.lang.String, java.lang.ClassLoader);
method public static void prepareWebViewInZygote();
field public static final java.lang.String CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY = "persist.sys.webview.vmsize";
@@ -48840,6 +48871,24 @@
method public abstract boolean shouldDelayChildPressedState();
}
+ public final class WebViewProviderInfo implements android.os.Parcelable {
+ ctor public WebViewProviderInfo(java.lang.String, java.lang.String, boolean, boolean, java.lang.String[]);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.webkit.WebViewProviderInfo> CREATOR;
+ field public final boolean availableByDefault;
+ field public final java.lang.String description;
+ field public final boolean isFallback;
+ field public final java.lang.String packageName;
+ field public final java.lang.String[] signatures;
+ }
+
+ public final class WebViewUpdateService {
+ method public static android.webkit.WebViewProviderInfo[] getAllWebViewPackages();
+ method public static java.lang.String getCurrentWebViewPackageName();
+ method public static android.webkit.WebViewProviderInfo[] getValidWebViewPackages();
+ }
+
}
package android.widget {
@@ -49326,7 +49375,7 @@
method public long getMinDate();
method public deprecated android.graphics.drawable.Drawable getSelectedDateVerticalBar();
method public deprecated int getSelectedWeekBackgroundColor();
- method public boolean getShowWeekNumber();
+ method public deprecated boolean getShowWeekNumber();
method public deprecated int getShownWeekCount();
method public deprecated int getUnfocusedMonthDateColor();
method public int getWeekDayTextAppearance();
@@ -49343,7 +49392,7 @@
method public deprecated void setSelectedDateVerticalBar(int);
method public deprecated void setSelectedDateVerticalBar(android.graphics.drawable.Drawable);
method public deprecated void setSelectedWeekBackgroundColor(int);
- method public void setShowWeekNumber(boolean);
+ method public deprecated void setShowWeekNumber(boolean);
method public deprecated void setShownWeekCount(int);
method public deprecated void setUnfocusedMonthDateColor(int);
method public void setWeekDayTextAppearance(int);
@@ -49490,21 +49539,21 @@
ctor public DatePicker(android.content.Context, android.util.AttributeSet);
ctor public DatePicker(android.content.Context, android.util.AttributeSet, int);
ctor public DatePicker(android.content.Context, android.util.AttributeSet, int, int);
- method public android.widget.CalendarView getCalendarView();
- method public boolean getCalendarViewShown();
+ method public deprecated android.widget.CalendarView getCalendarView();
+ method public deprecated boolean getCalendarViewShown();
method public int getDayOfMonth();
method public int getFirstDayOfWeek();
method public long getMaxDate();
method public long getMinDate();
method public int getMonth();
- method public boolean getSpinnersShown();
+ method public deprecated boolean getSpinnersShown();
method public int getYear();
method public void init(int, int, int, android.widget.DatePicker.OnDateChangedListener);
- method public void setCalendarViewShown(boolean);
+ method public deprecated void setCalendarViewShown(boolean);
method public void setFirstDayOfWeek(int);
method public void setMaxDate(long);
method public void setMinDate(long);
- method public void setSpinnersShown(boolean);
+ method public deprecated void setSpinnersShown(boolean);
method public void updateDate(int, int, int);
}
@@ -51324,9 +51373,15 @@
method public void collapseActionView();
method public void dismissPopupMenus();
method public int getContentInsetEnd();
+ method public int getContentInsetEndWithActions();
method public int getContentInsetLeft();
method public int getContentInsetRight();
method public int getContentInsetStart();
+ method public int getContentInsetStartWithNavigation();
+ method public int getCurrentContentInsetEnd();
+ method public int getCurrentContentInsetLeft();
+ method public int getCurrentContentInsetRight();
+ method public int getCurrentContentInsetStart();
method public android.graphics.drawable.Drawable getLogo();
method public java.lang.CharSequence getLogoDescription();
method public android.view.Menu getMenu();
@@ -51345,6 +51400,8 @@
method public void inflateMenu(int);
method public boolean isOverflowMenuShowing();
method protected void onLayout(boolean, int, int, int, int);
+ method public void setContentInsetEndWithActions(int);
+ method public void setContentInsetStartWithNavigation(int);
method public void setContentInsetsAbsolute(int, int);
method public void setContentInsetsRelative(int, int);
method public void setLogo(int);
@@ -52329,9 +52386,7 @@
public final class FilePermission extends java.security.Permission implements java.io.Serializable {
ctor public FilePermission(java.lang.String, java.lang.String);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -53070,6 +53125,7 @@
method public static int compare(boolean, boolean);
method public int compareTo(java.lang.Boolean);
method public static boolean getBoolean(java.lang.String);
+ method public static int hashCode(boolean);
method public static boolean parseBoolean(java.lang.String);
method public static java.lang.String toString(boolean);
method public static java.lang.Boolean valueOf(boolean);
@@ -53087,6 +53143,7 @@
method public static java.lang.Byte decode(java.lang.String) throws java.lang.NumberFormatException;
method public double doubleValue();
method public float floatValue();
+ method public static int hashCode(byte);
method public int intValue();
method public long longValue();
method public static byte parseByte(java.lang.String, int) throws java.lang.NumberFormatException;
@@ -53095,6 +53152,7 @@
method public static java.lang.Byte valueOf(byte);
method public static java.lang.Byte valueOf(java.lang.String, int) throws java.lang.NumberFormatException;
method public static java.lang.Byte valueOf(java.lang.String) throws java.lang.NumberFormatException;
+ field public static final int BYTES = 1; // 0x1
field public static final byte MAX_VALUE = 127; // 0x7f
field public static final byte MIN_VALUE = -128; // 0xffffff80
field public static final int SIZE = 8; // 0x8
@@ -53132,6 +53190,7 @@
method public static int getNumericValue(int);
method public static int getType(char);
method public static int getType(int);
+ method public static int hashCode(char);
method public static char highSurrogate(int);
method public static boolean isAlphabetic(int);
method public static boolean isBmpCodePoint(int);
@@ -53192,6 +53251,7 @@
method public static char toUpperCase(char);
method public static int toUpperCase(int);
method public static java.lang.Character valueOf(char);
+ field public static final int BYTES = 2; // 0x2
field public static final byte COMBINING_SPACING_MARK = 8; // 0x8
field public static final byte CONNECTOR_PUNCTUATION = 23; // 0x17
field public static final byte CONTROL = 15; // 0xf
@@ -53818,6 +53878,7 @@
method public static int floatToIntBits(float);
method public static int floatToRawIntBits(float);
method public float floatValue();
+ method public static int hashCode(float);
method public static float intBitsToFloat(int);
method public int intValue();
method public static boolean isFinite(float);
@@ -53826,11 +53887,15 @@
method public static boolean isNaN(float);
method public boolean isNaN();
method public long longValue();
+ method public static float max(float, float);
+ method public static float min(float, float);
method public static float parseFloat(java.lang.String) throws java.lang.NumberFormatException;
+ method public static float sum(float, float);
method public static java.lang.String toHexString(float);
method public static java.lang.String toString(float);
method public static java.lang.Float valueOf(java.lang.String) throws java.lang.NumberFormatException;
method public static java.lang.Float valueOf(float);
+ field public static final int BYTES = 4; // 0x4
field public static final int MAX_EXPONENT = 127; // 0x7f
field public static final float MAX_VALUE = 3.4028235E38f;
field public static final int MIN_EXPONENT = -126; // 0xffffff82
@@ -54010,6 +54075,7 @@
method public static java.lang.Long valueOf(java.lang.String, int) throws java.lang.NumberFormatException;
method public static java.lang.Long valueOf(java.lang.String) throws java.lang.NumberFormatException;
method public static java.lang.Long valueOf(long);
+ field public static final int BYTES = 8; // 0x8
field public static final long MAX_VALUE = 9223372036854775807L; // 0x7fffffffffffffffL
field public static final long MIN_VALUE = -9223372036854775808L; // 0x8000000000000000L
field public static final int SIZE = 64; // 0x40
@@ -54189,41 +54255,11 @@
method public java.io.File directory();
method public java.lang.ProcessBuilder directory(java.io.File);
method public java.util.Map<java.lang.String, java.lang.String> environment();
- method public java.lang.ProcessBuilder inheritIO();
- method public java.lang.ProcessBuilder redirectError(java.lang.ProcessBuilder.Redirect);
- method public java.lang.ProcessBuilder redirectError(java.io.File);
- method public java.lang.ProcessBuilder.Redirect redirectError();
method public boolean redirectErrorStream();
method public java.lang.ProcessBuilder redirectErrorStream(boolean);
- method public java.lang.ProcessBuilder redirectInput(java.lang.ProcessBuilder.Redirect);
- method public java.lang.ProcessBuilder redirectInput(java.io.File);
- method public java.lang.ProcessBuilder.Redirect redirectInput();
- method public java.lang.ProcessBuilder redirectOutput(java.lang.ProcessBuilder.Redirect);
- method public java.lang.ProcessBuilder redirectOutput(java.io.File);
- method public java.lang.ProcessBuilder.Redirect redirectOutput();
method public java.lang.Process start() throws java.io.IOException;
}
- public static abstract class ProcessBuilder.Redirect {
- method public static java.lang.ProcessBuilder.Redirect appendTo(java.io.File);
- method public java.io.File file();
- method public static java.lang.ProcessBuilder.Redirect from(java.io.File);
- method public static java.lang.ProcessBuilder.Redirect to(java.io.File);
- method public abstract java.lang.ProcessBuilder.Redirect.Type type();
- field public static final java.lang.ProcessBuilder.Redirect INHERIT;
- field public static final java.lang.ProcessBuilder.Redirect PIPE;
- }
-
- public static final class ProcessBuilder.Redirect.Type extends java.lang.Enum {
- method public static java.lang.ProcessBuilder.Redirect.Type valueOf(java.lang.String);
- method public static final java.lang.ProcessBuilder.Redirect.Type[] values();
- enum_constant public static final java.lang.ProcessBuilder.Redirect.Type APPEND;
- enum_constant public static final java.lang.ProcessBuilder.Redirect.Type INHERIT;
- enum_constant public static final java.lang.ProcessBuilder.Redirect.Type PIPE;
- enum_constant public static final java.lang.ProcessBuilder.Redirect.Type READ;
- enum_constant public static final java.lang.ProcessBuilder.Redirect.Type WRITE;
- }
-
public abstract interface Readable {
method public abstract int read(java.nio.CharBuffer) throws java.io.IOException;
}
@@ -54343,6 +54379,7 @@
method public static java.lang.Short decode(java.lang.String) throws java.lang.NumberFormatException;
method public double doubleValue();
method public float floatValue();
+ method public static int hashCode(short);
method public int intValue();
method public long longValue();
method public static short parseShort(java.lang.String, int) throws java.lang.NumberFormatException;
@@ -54352,6 +54389,7 @@
method public static java.lang.Short valueOf(java.lang.String, int) throws java.lang.NumberFormatException;
method public static java.lang.Short valueOf(java.lang.String) throws java.lang.NumberFormatException;
method public static java.lang.Short valueOf(short);
+ field public static final int BYTES = 2; // 0x2
field public static final short MAX_VALUE = 32767; // 0x7fff
field public static final short MIN_VALUE = -32768; // 0xffff8000
field public static final int SIZE = 16; // 0x10
@@ -55875,9 +55913,7 @@
public final class SocketPermission extends java.security.Permission implements java.io.Serializable {
ctor public SocketPermission(java.lang.String, java.lang.String);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -56959,9 +56995,7 @@
public final class AllPermission extends java.security.Permission {
ctor public AllPermission();
ctor public AllPermission(java.lang.String, java.lang.String);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -56975,9 +57009,7 @@
public abstract class BasicPermission extends java.security.Permission implements java.io.Serializable {
ctor public BasicPermission(java.lang.String);
ctor public BasicPermission(java.lang.String, java.lang.String);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -57355,10 +57387,8 @@
public abstract class Permission implements java.security.Guard java.io.Serializable {
ctor public Permission(java.lang.String);
method public void checkGuard(java.lang.Object) throws java.lang.SecurityException;
- method public abstract boolean equals(java.lang.Object);
method public abstract java.lang.String getActions();
method public final java.lang.String getName();
- method public abstract int hashCode();
method public abstract boolean implies(java.security.Permission);
method public java.security.PermissionCollection newPermissionCollection();
}
@@ -57616,13 +57646,11 @@
public final class UnresolvedPermission extends java.security.Permission implements java.io.Serializable {
ctor public UnresolvedPermission(java.lang.String, java.lang.String, java.lang.String, java.security.cert.Certificate[]);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
method public java.lang.String getUnresolvedActions();
method public java.security.cert.Certificate[] getUnresolvedCerts();
method public java.lang.String getUnresolvedName();
method public java.lang.String getUnresolvedType();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -57680,8 +57708,6 @@
}
public abstract interface Permission {
- method public abstract boolean equals(java.lang.Object);
- method public abstract java.lang.String toString();
}
}
@@ -60397,6 +60423,7 @@
method public void forEach(java.util.function.Consumer<? super E>);
method public E get(int);
method public boolean removeIf(java.util.function.Predicate<? super E>);
+ method public void replaceAll(java.util.function.UnaryOperator<E>);
method public int size();
method public void sort(java.util.Comparator<? super E>);
method public java.util.Spliterator<E> spliterator();
@@ -60482,6 +60509,10 @@
method public static int hashCode(float[]);
method public static int hashCode(double[]);
method public static int hashCode(java.lang.Object[]);
+ method public static void parallelSetAll(T[], java.util.function.IntFunction<? extends T>);
+ method public static void parallelSetAll(int[], java.util.function.IntUnaryOperator);
+ method public static void parallelSetAll(long[], java.util.function.IntToLongFunction);
+ method public static void parallelSetAll(double[], java.util.function.IntToDoubleFunction);
method public static void parallelSort(byte[]);
method public static void parallelSort(byte[], int, int);
method public static void parallelSort(char[]);
@@ -61030,6 +61061,7 @@
method public java.lang.Object clone();
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
method public void forEach(java.util.function.BiConsumer<? super K, ? super V>);
+ method public boolean replace(K, V, V);
}
public class HashSet extends java.util.AbstractSet implements java.lang.Cloneable java.io.Serializable java.util.Set {
@@ -61050,6 +61082,9 @@
ctor public Hashtable(java.util.Map<? extends K, ? extends V>);
method public synchronized void clear();
method public synchronized java.lang.Object clone();
+ method public synchronized V compute(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>);
+ method public synchronized V computeIfAbsent(K, java.util.function.Function<? super K, ? extends V>);
+ method public synchronized V computeIfPresent(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>);
method public synchronized boolean contains(java.lang.Object);
method public synchronized boolean containsKey(java.lang.Object);
method public boolean containsValue(java.lang.Object);
@@ -61057,13 +61092,19 @@
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
method public synchronized void forEach(java.util.function.BiConsumer<? super K, ? super V>);
method public synchronized V get(java.lang.Object);
+ method public synchronized V getOrDefault(java.lang.Object, V);
method public synchronized boolean isEmpty();
method public java.util.Set<K> keySet();
method public synchronized java.util.Enumeration<K> keys();
+ method public synchronized V merge(K, V, java.util.function.BiFunction<? super V, ? super V, ? extends V>);
method public synchronized V put(K, V);
method public synchronized void putAll(java.util.Map<? extends K, ? extends V>);
+ method public synchronized V putIfAbsent(K, V);
method protected void rehash();
method public synchronized V remove(java.lang.Object);
+ method public synchronized boolean remove(java.lang.Object, java.lang.Object);
+ method public synchronized boolean replace(K, V, V);
+ method public synchronized V replace(K, V);
method public synchronized int size();
method public java.util.Collection<V> values();
}
@@ -61208,9 +61249,11 @@
method public abstract boolean remove(java.lang.Object);
method public abstract E remove(int);
method public abstract boolean removeAll(java.util.Collection<?>);
+ method public default void replaceAll(java.util.function.UnaryOperator<E>);
method public abstract boolean retainAll(java.util.Collection<?>);
method public abstract E set(int, E);
method public abstract int size();
+ method public default void sort(java.util.Comparator<? super E>);
method public abstract java.util.List<E> subList(int, int);
method public abstract java.lang.Object[] toArray();
method public abstract T[] toArray(T[]);
@@ -62125,9 +62168,11 @@
method public synchronized boolean removeElement(java.lang.Object);
method public synchronized void removeElementAt(int);
method public synchronized boolean removeIf(java.util.function.Predicate<? super E>);
+ method public synchronized void replaceAll(java.util.function.UnaryOperator<E>);
method public synchronized void setElementAt(E, int);
method public synchronized void setSize(int);
method public synchronized int size();
+ method public synchronized void sort(java.util.Comparator<? super E>);
method public java.util.Spliterator<E> spliterator();
method public synchronized void trimToSize();
field protected int capacityIncrement;
@@ -62593,6 +62638,7 @@
method public java.lang.Object clone();
method public boolean contains(java.lang.Object);
method public boolean containsAll(java.util.Collection<?>);
+ method public void forEach(java.util.function.Consumer<? super E>);
method public E get(int);
method public int indexOf(E, int);
method public int indexOf(java.lang.Object);
@@ -66970,11 +67016,9 @@
public final class PrivateCredentialPermission extends java.security.Permission {
ctor public PrivateCredentialPermission(java.lang.String, java.lang.String);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
method public java.lang.String getCredentialClass();
method public java.lang.String[][] getPrincipals();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
diff --git a/api/system-removed.txt b/api/system-removed.txt
index d48b9b3..03cf8b0 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -92,6 +92,10 @@
package android.media.tv {
+ public final class TvInputManager {
+ method public android.media.tv.TvInputManager.Hardware acquireTvInputHardware(int, android.media.tv.TvInputManager.HardwareCallback, android.media.tv.TvInputInfo);
+ }
+
public class TvView extends android.view.ViewGroup {
method public void requestUnblockContent(android.media.tv.TvContentRating);
}
@@ -134,6 +138,15 @@
}
+package android.os.storage {
+
+ public class StorageManager {
+ method public android.os.storage.StorageVolume getPrimaryVolume();
+ method public android.os.storage.StorageVolume[] getVolumeList();
+ }
+
+}
+
package android.preference {
public class PreferenceManager {
diff --git a/api/test-current.txt b/api/test-current.txt
index c382c3d..a4408be 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -71,6 +71,7 @@
field public static final java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS";
field public static final java.lang.String GET_ACCOUNTS_PRIVILEGED = "android.permission.GET_ACCOUNTS_PRIVILEGED";
field public static final java.lang.String GET_PACKAGE_SIZE = "android.permission.GET_PACKAGE_SIZE";
+ field public static final java.lang.String GET_PASSWORD_PRIVILEGED = "android.permission.GET_PASSWORD_PRIVILEGED";
field public static final deprecated java.lang.String GET_TASKS = "android.permission.GET_TASKS";
field public static final java.lang.String GLOBAL_SEARCH = "android.permission.GLOBAL_SEARCH";
field public static final java.lang.String INSTALL_LOCATION_PROVIDER = "android.permission.INSTALL_LOCATION_PROVIDER";
@@ -416,9 +417,11 @@
field public static final int contentAuthority = 16843408; // 0x1010290
field public static final int contentDescription = 16843379; // 0x1010273
field public static final int contentInsetEnd = 16843860; // 0x1010454
+ field public static final int contentInsetEndWithActions = 16844070; // 0x1010526
field public static final int contentInsetLeft = 16843861; // 0x1010455
field public static final int contentInsetRight = 16843862; // 0x1010456
field public static final int contentInsetStart = 16843859; // 0x1010453
+ field public static final int contentInsetStartWithNavigation = 16844069; // 0x1010525
field public static final int contextClickable = 16844007; // 0x10104e7
field public static final int contextPopupMenuStyle = 16844034; // 0x1010502
field public static final int controlX1 = 16843772; // 0x10103fc
@@ -576,6 +579,7 @@
field public static final int fontFamily = 16843692; // 0x10103ac
field public static final int fontFeatureSettings = 16843959; // 0x10104b7
field public static final int footerDividersEnabled = 16843311; // 0x101022f
+ field public static final int forceHasOverlappingRendering = 16844068; // 0x1010524
field public static final int foreground = 16843017; // 0x1010109
field public static final int foregroundGravity = 16843264; // 0x1010200
field public static final int foregroundTint = 16843885; // 0x101046d
@@ -859,7 +863,8 @@
field public static final int minResizeWidth = 16843669; // 0x1010395
field public static final int minSdkVersion = 16843276; // 0x101020c
field public static final int minWidth = 16843071; // 0x101013f
- field public static final int minimalSize = 16844022; // 0x10104f6
+ field public static final int minimalHeight = 16844067; // 0x1010523
+ field public static final int minimalWidth = 16844022; // 0x10104f6
field public static final int minimumHorizontalAngle = 16843901; // 0x101047d
field public static final int minimumVerticalAngle = 16843902; // 0x101047e
field public static final int mipMap = 16843725; // 0x10103cd
@@ -2480,6 +2485,7 @@
field public static final int Widget_Material_CompoundButton_CheckBox = 16974435; // 0x1030263
field public static final int Widget_Material_CompoundButton_RadioButton = 16974436; // 0x1030264
field public static final int Widget_Material_CompoundButton_Star = 16974437; // 0x1030265
+ field public static final int Widget_Material_CompoundButton_Switch = 16974554; // 0x10302da
field public static final int Widget_Material_DatePicker = 16974438; // 0x1030266
field public static final int Widget_Material_DropDownItem = 16974439; // 0x1030267
field public static final int Widget_Material_DropDownItem_Spinner = 16974440; // 0x1030268
@@ -2514,6 +2520,7 @@
field public static final int Widget_Material_Light_CompoundButton_CheckBox = 16974500; // 0x10302a4
field public static final int Widget_Material_Light_CompoundButton_RadioButton = 16974501; // 0x10302a5
field public static final int Widget_Material_Light_CompoundButton_Star = 16974502; // 0x10302a6
+ field public static final int Widget_Material_Light_CompoundButton_Switch = 16974555; // 0x10302db
field public static final int Widget_Material_Light_DatePicker = 16974503; // 0x10302a7
field public static final int Widget_Material_Light_DropDownItem = 16974504; // 0x10302a8
field public static final int Widget_Material_Light_DropDownItem_Spinner = 16974505; // 0x10302a9
@@ -3421,7 +3428,7 @@
method public boolean dispatchTouchEvent(android.view.MotionEvent);
method public boolean dispatchTrackballEvent(android.view.MotionEvent);
method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
- method public void enterPictureInPicture();
+ method public void enterPictureInPictureMode();
method public android.view.View findViewById(int);
method public void finish();
method public void finishActivity(int);
@@ -3461,15 +3468,16 @@
method public android.view.Window getWindow();
method public android.view.WindowManager getWindowManager();
method public boolean hasWindowFocus();
- method public boolean inMultiWindow();
- method public boolean inPictureInPicture();
method public void invalidateOptionsMenu();
method public boolean isChangingConfigurations();
method public final boolean isChild();
method public boolean isDestroyed();
method public boolean isFinishing();
method public boolean isImmersive();
+ method public boolean isInMultiWindowMode();
+ method public boolean isInPictureInPictureMode();
method public boolean isLocalVoiceInteractionSupported();
+ method public boolean isOverlayWithDecorCaptionEnabled();
method public boolean isTaskRoot();
method public boolean isVoiceInteraction();
method public boolean isVoiceInteractionRoot();
@@ -3516,7 +3524,7 @@
method public void onLowMemory();
method public boolean onMenuItemSelected(int, android.view.MenuItem);
method public boolean onMenuOpened(int, android.view.Menu);
- method public void onMultiWindowChanged(boolean);
+ method public void onMultiWindowModeChanged(boolean);
method public boolean onNavigateUp();
method public boolean onNavigateUpFromChild(android.app.Activity);
method protected void onNewIntent(android.content.Intent);
@@ -3524,7 +3532,7 @@
method public void onOptionsMenuClosed(android.view.Menu);
method public void onPanelClosed(int, android.view.Menu);
method protected void onPause();
- method public void onPictureInPictureChanged(boolean);
+ method public void onPictureInPictureModeChanged(boolean);
method protected void onPostCreate(android.os.Bundle);
method public void onPostCreate(android.os.Bundle, android.os.PersistableBundle);
method protected void onPostResume();
@@ -3562,7 +3570,6 @@
method public android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback, int);
method public void openContextMenu(android.view.View);
method public void openOptionsMenu();
- method public void overlayWithDecorCaption(boolean);
method public void overridePendingTransition(int, int);
method public void postponeEnterTransition();
method public void recreate();
@@ -3591,6 +3598,7 @@
method public void setImmersive(boolean);
method public void setIntent(android.content.Intent);
method public final void setMediaController(android.media.session.MediaController);
+ method public void setOverlayWithDecorCaptionEnabled(boolean);
method public final deprecated void setProgress(int);
method public final deprecated void setProgressBarIndeterminate(boolean);
method public final deprecated void setProgressBarIndeterminateVisibility(boolean);
@@ -4280,6 +4288,7 @@
public class DownloadManager {
method public long addCompletedDownload(java.lang.String, java.lang.String, boolean, java.lang.String, java.lang.String, long, boolean);
+ method public long addCompletedDownload(java.lang.String, java.lang.String, boolean, java.lang.String, java.lang.String, long, boolean, android.net.Uri, android.net.Uri);
method public long enqueue(android.app.DownloadManager.Request);
method public static java.lang.Long getMaxBytesOverMobile(android.content.Context);
method public java.lang.String getMimeTypeForDownloadedFile(long);
@@ -4432,11 +4441,11 @@
method public void onInflate(android.content.Context, android.util.AttributeSet, android.os.Bundle);
method public deprecated void onInflate(android.app.Activity, android.util.AttributeSet, android.os.Bundle);
method public void onLowMemory();
- method public void onMultiWindowChanged(boolean);
+ method public void onMultiWindowModeChanged(boolean);
method public boolean onOptionsItemSelected(android.view.MenuItem);
method public void onOptionsMenuClosed(android.view.Menu);
method public void onPause();
- method public void onPictureInPictureChanged(boolean);
+ method public void onPictureInPictureModeChanged(boolean);
method public void onPrepareOptionsMenu(android.view.Menu);
method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
method public void onResume();
@@ -4517,11 +4526,11 @@
method public void dispatchDestroy();
method public void dispatchDestroyView();
method public void dispatchLowMemory();
- method public void dispatchMultiWindowChanged(boolean);
+ method public void dispatchMultiWindowModeChanged(boolean);
method public boolean dispatchOptionsItemSelected(android.view.MenuItem);
method public void dispatchOptionsMenuClosed(android.view.Menu);
method public void dispatchPause();
- method public void dispatchPictureInPictureChanged(boolean);
+ method public void dispatchPictureInPictureModeChanged(boolean);
method public boolean dispatchPrepareOptionsMenu(android.view.Menu);
method public void dispatchResume();
method public void dispatchStart();
@@ -4970,7 +4979,7 @@
field public int ledARGB;
field public int ledOffMS;
field public int ledOnMS;
- field public int number;
+ field public deprecated int number;
field public int priority;
field public android.app.Notification publicVersion;
field public android.net.Uri sound;
@@ -5017,11 +5026,13 @@
method public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Builder);
method public java.lang.CharSequence getCancelLabel();
method public java.lang.CharSequence getConfirmLabel();
+ method public boolean getHintContentIntentLaunchesActivity();
method public java.lang.CharSequence getInProgressLabel();
method public boolean isAvailableOffline();
method public android.app.Notification.Action.WearableExtender setAvailableOffline(boolean);
method public android.app.Notification.Action.WearableExtender setCancelLabel(java.lang.CharSequence);
method public android.app.Notification.Action.WearableExtender setConfirmLabel(java.lang.CharSequence);
+ method public android.app.Notification.Action.WearableExtender setHintContentIntentLaunchesActivity(boolean);
method public android.app.Notification.Action.WearableExtender setInProgressLabel(java.lang.CharSequence);
}
@@ -5063,7 +5074,7 @@
method public android.app.Notification.Builder setChronometerCountsDown(boolean);
method public android.app.Notification.Builder setColor(int);
method public deprecated android.app.Notification.Builder setContent(android.widget.RemoteViews);
- method public android.app.Notification.Builder setContentInfo(java.lang.CharSequence);
+ method public deprecated android.app.Notification.Builder setContentInfo(java.lang.CharSequence);
method public android.app.Notification.Builder setContentIntent(android.app.PendingIntent);
method public android.app.Notification.Builder setContentText(java.lang.CharSequence);
method public android.app.Notification.Builder setContentTitle(java.lang.CharSequence);
@@ -5080,7 +5091,7 @@
method public android.app.Notification.Builder setLargeIcon(android.graphics.drawable.Icon);
method public android.app.Notification.Builder setLights(int, int, int);
method public android.app.Notification.Builder setLocalOnly(boolean);
- method public android.app.Notification.Builder setNumber(int);
+ method public deprecated android.app.Notification.Builder setNumber(int);
method public android.app.Notification.Builder setOngoing(boolean);
method public android.app.Notification.Builder setOnlyAlertOnce(boolean);
method public android.app.Notification.Builder setPriority(int);
@@ -5198,6 +5209,7 @@
method public android.app.PendingIntent getDisplayIntent();
method public int getGravity();
method public boolean getHintAvoidBackgroundClipping();
+ method public boolean getHintContentIntentLaunchesActivity();
method public boolean getHintHideIcon();
method public int getHintScreenTimeout();
method public boolean getHintShowBackgroundOnly();
@@ -5213,6 +5225,7 @@
method public android.app.Notification.WearableExtender setDisplayIntent(android.app.PendingIntent);
method public android.app.Notification.WearableExtender setGravity(int);
method public android.app.Notification.WearableExtender setHintAvoidBackgroundClipping(boolean);
+ method public android.app.Notification.WearableExtender setHintContentIntentLaunchesActivity(boolean);
method public android.app.Notification.WearableExtender setHintHideIcon(boolean);
method public android.app.Notification.WearableExtender setHintScreenTimeout(int);
method public android.app.Notification.WearableExtender setHintShowBackgroundOnly(boolean);
@@ -5753,8 +5766,8 @@
field public static final java.lang.String COMMAND_SECONDARY_TAP = "android.wallpaper.secondaryTap";
field public static final java.lang.String COMMAND_TAP = "android.wallpaper.tap";
field public static final java.lang.String EXTRA_LIVE_WALLPAPER_COMPONENT = "android.service.wallpaper.extra.LIVE_WALLPAPER_COMPONENT";
- field public static final int FLAG_SET_LOCK = 2; // 0x2
- field public static final int FLAG_SET_SYSTEM = 1; // 0x1
+ field public static final int FLAG_LOCK = 2; // 0x2
+ field public static final int FLAG_SYSTEM = 1; // 0x1
field public static final java.lang.String WALLPAPER_PREVIEW_META_DATA = "android.wallpaper.preview";
}
@@ -5854,7 +5867,7 @@
method public boolean getCrossProfileContactsSearchDisabled(android.content.ComponentName);
method public java.util.List<java.lang.String> getCrossProfileWidgetProviders(android.content.ComponentName);
method public int getCurrentFailedPasswordAttempts();
- method public java.lang.String getDeviceOwnerLockScreenInfo();
+ method public java.lang.CharSequence getDeviceOwnerLockScreenInfo();
method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName);
method public int getKeyguardDisabledFeatures(android.content.ComponentName);
method public java.lang.String getLongSupportMessage(android.content.ComponentName);
@@ -5862,7 +5875,6 @@
method public long getMaximumTimeToLock(android.content.ComponentName);
method public int getOrganizationColor(android.content.ComponentName);
method public java.lang.String getOrganizationName(android.content.ComponentName);
- method public boolean getPackageSuspended(android.content.ComponentName, java.lang.String);
method public android.app.admin.DevicePolicyManager getParentProfileInstance(android.content.ComponentName);
method public long getPasswordExpiration(android.content.ComponentName);
method public long getPasswordExpirationTimeout(android.content.ComponentName);
@@ -5892,14 +5904,16 @@
method public boolean hasGrantedPolicy(android.content.ComponentName, int);
method public boolean installCaCert(android.content.ComponentName, byte[]);
method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate, java.lang.String);
- method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate, java.lang.String, boolean);
+ method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], java.lang.String, boolean);
method public boolean isActivePasswordSufficient();
method public boolean isAdminActive(android.content.ComponentName);
method public boolean isApplicationHidden(android.content.ComponentName, java.lang.String);
method public boolean isCallerApplicationRestrictionsManagingPackage();
method public boolean isDeviceOwnerApp(java.lang.String);
method public boolean isLockTaskPermitted(java.lang.String);
+ method public boolean isManagedProfile(android.content.ComponentName);
method public boolean isMasterVolumeMuted(android.content.ComponentName);
+ method public boolean isPackageSuspended(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
method public boolean isProfileOwnerApp(java.lang.String);
method public boolean isProvisioningAllowed(java.lang.String);
method public boolean isSecurityLoggingEnabled(android.content.ComponentName);
@@ -5915,7 +5929,7 @@
method public java.util.List<android.app.admin.SecurityLog.SecurityEvent> retrievePreRebootSecurityLogs(android.content.ComponentName);
method public java.util.List<android.app.admin.SecurityLog.SecurityEvent> retrieveSecurityLogs(android.content.ComponentName);
method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
- method public boolean setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String);
+ method public void setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException, java.lang.UnsupportedOperationException;
method public boolean setApplicationHidden(android.content.ComponentName, java.lang.String, boolean);
method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
method public void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -5925,7 +5939,7 @@
method public void setCertInstallerPackage(android.content.ComponentName, java.lang.String) throws java.lang.SecurityException;
method public void setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean);
method public void setCrossProfileContactsSearchDisabled(android.content.ComponentName, boolean);
- method public boolean setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.String);
+ method public void setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.CharSequence);
method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public boolean setKeyguardDisabled(android.content.ComponentName, boolean);
method public void setKeyguardDisabledFeatures(android.content.ComponentName, int);
@@ -7880,6 +7894,7 @@
method public static boolean isSyncPending(android.accounts.Account, java.lang.String);
method public void notifyChange(android.net.Uri, android.database.ContentObserver);
method public void notifyChange(android.net.Uri, android.database.ContentObserver, boolean);
+ method public void notifyChange(android.net.Uri, android.database.ContentObserver, int);
method public final android.content.res.AssetFileDescriptor openAssetFileDescriptor(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
method public final android.content.res.AssetFileDescriptor openAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException;
method public final android.os.ParcelFileDescriptor openFileDescriptor(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
@@ -7910,6 +7925,8 @@
field public static final java.lang.String CURSOR_DIR_BASE_TYPE = "vnd.android.cursor.dir";
field public static final java.lang.String CURSOR_ITEM_BASE_TYPE = "vnd.android.cursor.item";
field public static final java.lang.String EXTRA_SIZE = "android.content.extra.SIZE";
+ field public static final int NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS = 2; // 0x2
+ field public static final int NOTIFY_SYNC_TO_NETWORK = 1; // 0x1
field public static final java.lang.String SCHEME_ANDROID_RESOURCE = "android.resource";
field public static final java.lang.String SCHEME_CONTENT = "content";
field public static final java.lang.String SCHEME_FILE = "file";
@@ -8225,8 +8242,6 @@
method public java.lang.String getPackageResourcePath();
method public android.content.res.Resources getResources();
method public android.content.SharedPreferences getSharedPreferences(java.lang.String, int);
- method public android.content.SharedPreferences getSharedPreferences(java.io.File, int);
- method public java.io.File getSharedPreferencesPath(java.lang.String);
method public java.lang.Object getSystemService(java.lang.String);
method public java.lang.String getSystemServiceName(java.lang.Class<?>);
method public android.content.res.Resources.Theme getTheme();
@@ -8522,8 +8537,9 @@
field public static final java.lang.String ACTION_LOCKED_BOOT_COMPLETED = "android.intent.action.LOCKED_BOOT_COMPLETED";
field public static final java.lang.String ACTION_MAIN = "android.intent.action.MAIN";
field public static final java.lang.String ACTION_MANAGED_PROFILE_ADDED = "android.intent.action.MANAGED_PROFILE_ADDED";
- field public static final java.lang.String ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED = "android.intent.action.MANAGED_PROFILE_AVAILABILITY_CHANGED";
+ field public static final java.lang.String ACTION_MANAGED_PROFILE_AVAILABLE = "android.intent.action.MANAGED_PROFILE_AVAILABLE";
field public static final java.lang.String ACTION_MANAGED_PROFILE_REMOVED = "android.intent.action.MANAGED_PROFILE_REMOVED";
+ field public static final java.lang.String ACTION_MANAGED_PROFILE_UNAVAILABLE = "android.intent.action.MANAGED_PROFILE_UNAVAILABLE";
field public static final java.lang.String ACTION_MANAGED_PROFILE_UNLOCKED = "android.intent.action.MANAGED_PROFILE_UNLOCKED";
field public static final java.lang.String ACTION_MANAGE_NETWORK_USAGE = "android.intent.action.MANAGE_NETWORK_USAGE";
field public static final java.lang.String ACTION_MANAGE_PACKAGE_STORAGE = "android.intent.action.MANAGE_PACKAGE_STORAGE";
@@ -9263,7 +9279,6 @@
field public int documentLaunchMode;
field public int flags;
field public int launchMode;
- field public android.content.pm.ActivityInfo.Layout layout;
field public int maxRecents;
field public java.lang.String parentActivityName;
field public java.lang.String permission;
@@ -9274,14 +9289,16 @@
field public java.lang.String taskAffinity;
field public int theme;
field public int uiOptions;
+ field public android.content.pm.ActivityInfo.WindowLayout windowLayout;
}
- public static final class ActivityInfo.Layout {
- ctor public ActivityInfo.Layout(int, float, int, float, int, int);
+ public static final class ActivityInfo.WindowLayout {
+ ctor public ActivityInfo.WindowLayout(int, float, int, float, int, int, int);
field public final int gravity;
field public final int height;
field public final float heightFraction;
- field public final int minimalSize;
+ field public final int minimalHeight;
+ field public final int minimalWidth;
field public final int width;
field public final float widthFraction;
}
@@ -9920,6 +9937,7 @@
field public static final int PROTECTION_FLAG_PRE23 = 128; // 0x80
field public static final int PROTECTION_FLAG_PREINSTALLED = 1024; // 0x400
field public static final int PROTECTION_FLAG_PRIVILEGED = 16; // 0x10
+ field public static final int PROTECTION_FLAG_SETUP = 2048; // 0x800
field public static final deprecated int PROTECTION_FLAG_SYSTEM = 16; // 0x10
field public static final int PROTECTION_FLAG_VERIFIER = 512; // 0x200
field public static final int PROTECTION_MASK_BASE = 15; // 0xf
@@ -9997,7 +10015,7 @@
field public java.lang.String permission;
}
- public class ShortcutInfo implements android.os.Parcelable {
+ public final class ShortcutInfo implements android.os.Parcelable {
method public int describeContents();
method public android.content.ComponentName getActivityComponent();
method public android.os.PersistableBundle getExtras();
@@ -10005,6 +10023,7 @@
method public android.content.Intent getIntent();
method public long getLastChangedTimestamp();
method public java.lang.String getPackageName();
+ method public java.lang.String getText();
method public java.lang.String getTitle();
method public int getWeight();
method public boolean hasIconFile();
@@ -10032,6 +10051,7 @@
method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon);
method public android.content.pm.ShortcutInfo.Builder setId(java.lang.String);
method public android.content.pm.ShortcutInfo.Builder setIntent(android.content.Intent);
+ method public android.content.pm.ShortcutInfo.Builder setText(java.lang.String);
method public android.content.pm.ShortcutInfo.Builder setTitle(java.lang.String);
method public android.content.pm.ShortcutInfo.Builder setWeight(int);
}
@@ -13506,6 +13526,7 @@
public final class Sensor {
method public int getFifoMaxEventCount();
method public int getFifoReservedEventCount();
+ method public int getId();
method public int getMaxDelay();
method public float getMaximumRange();
method public int getMinDelay();
@@ -13515,9 +13536,10 @@
method public float getResolution();
method public java.lang.String getStringType();
method public int getType();
- method public java.util.UUID getUuid();
method public java.lang.String getVendor();
method public int getVersion();
+ method public boolean isAdditionalInfoSupported();
+ method public boolean isDynamicSensor();
method public boolean isWakeUpSensor();
field public static final int REPORTING_MODE_CONTINUOUS = 0; // 0x0
field public static final int REPORTING_MODE_ONE_SHOT = 2; // 0x2
@@ -13636,8 +13658,9 @@
method public static void getRotationMatrixFromVector(float[], float[]);
method public java.util.List<android.hardware.Sensor> getSensorList(int);
method public deprecated int getSensors();
- method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback);
- method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback, android.os.Handler);
+ method public boolean isDynamicSensorDiscoverySupported();
+ method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorCallback);
+ method public void registerDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorCallback, android.os.Handler);
method public deprecated boolean registerListener(android.hardware.SensorListener, int);
method public deprecated boolean registerListener(android.hardware.SensorListener, int, int);
method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int);
@@ -13646,7 +13669,7 @@
method public boolean registerListener(android.hardware.SensorEventListener, android.hardware.Sensor, int, int, android.os.Handler);
method public static boolean remapCoordinateSystem(float[], int, int, float[]);
method public boolean requestTriggerSensor(android.hardware.TriggerEventListener, android.hardware.Sensor);
- method public void unregisterDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorConnectionCallback);
+ method public void unregisterDynamicSensorCallback(android.hardware.SensorManager.DynamicSensorCallback);
method public deprecated void unregisterListener(android.hardware.SensorListener);
method public deprecated void unregisterListener(android.hardware.SensorListener, int);
method public void unregisterListener(android.hardware.SensorEventListener, android.hardware.Sensor);
@@ -13711,8 +13734,8 @@
field public static final float STANDARD_GRAVITY = 9.80665f;
}
- public static abstract class SensorManager.DynamicSensorConnectionCallback {
- ctor public SensorManager.DynamicSensorConnectionCallback();
+ public static abstract class SensorManager.DynamicSensorCallback {
+ ctor public SensorManager.DynamicSensorCallback();
method public void onDynamicSensorConnected(android.hardware.Sensor);
method public void onDynamicSensorDisconnected(android.hardware.Sensor);
}
@@ -14837,7 +14860,6 @@
public static abstract interface UCharacter.BidiPairedBracketType {
field public static final int CLOSE = 2; // 0x2
- field public static final int COUNT = 3; // 0x3
field public static final int NONE = 0; // 0x0
field public static final int OPEN = 1; // 0x1
}
@@ -14846,7 +14868,6 @@
field public static final int CANONICAL = 1; // 0x1
field public static final int CIRCLE = 3; // 0x3
field public static final int COMPAT = 2; // 0x2
- field public static final int COUNT = 18; // 0x12
field public static final int FINAL = 4; // 0x4
field public static final int FONT = 5; // 0x5
field public static final int FRACTION = 6; // 0x6
@@ -14866,7 +14887,6 @@
public static abstract interface UCharacter.EastAsianWidth {
field public static final int AMBIGUOUS = 1; // 0x1
- field public static final int COUNT = 6; // 0x6
field public static final int FULLWIDTH = 3; // 0x3
field public static final int HALFWIDTH = 2; // 0x2
field public static final int NARROW = 4; // 0x4
@@ -14876,7 +14896,6 @@
public static abstract interface UCharacter.GraphemeClusterBreak {
field public static final int CONTROL = 1; // 0x1
- field public static final int COUNT = 13; // 0xd
field public static final int CR = 2; // 0x2
field public static final int EXTEND = 3; // 0x3
field public static final int L = 4; // 0x4
@@ -14892,7 +14911,6 @@
}
public static abstract interface UCharacter.HangulSyllableType {
- field public static final int COUNT = 6; // 0x6
field public static final int LEADING_JAMO = 1; // 0x1
field public static final int LVT_SYLLABLE = 5; // 0x5
field public static final int LV_SYLLABLE = 4; // 0x4
@@ -14908,7 +14926,6 @@
field public static final int BEH = 4; // 0x4
field public static final int BETH = 5; // 0x5
field public static final int BURUSHASKI_YEH_BARREE = 54; // 0x36
- field public static final int COUNT = 86; // 0x56
field public static final int DAL = 6; // 0x6
field public static final int DALATH_RISH = 7; // 0x7
field public static final int E = 8; // 0x8
@@ -14993,7 +15010,6 @@
}
public static abstract interface UCharacter.JoiningType {
- field public static final int COUNT = 6; // 0x6
field public static final int DUAL_JOINING = 2; // 0x2
field public static final int JOIN_CAUSING = 1; // 0x1
field public static final int LEFT_JOINING = 3; // 0x3
@@ -15016,7 +15032,6 @@
field public static final int COMPLEX_CONTEXT = 24; // 0x18
field public static final int CONDITIONAL_JAPANESE_STARTER = 37; // 0x25
field public static final int CONTINGENT_BREAK = 7; // 0x7
- field public static final int COUNT = 40; // 0x28
field public static final int EXCLAMATION = 11; // 0xb
field public static final int GLUE = 12; // 0xc
field public static final int H2 = 31; // 0x1f
@@ -15048,7 +15063,6 @@
}
public static abstract interface UCharacter.NumericType {
- field public static final int COUNT = 4; // 0x4
field public static final int DECIMAL = 1; // 0x1
field public static final int DIGIT = 2; // 0x2
field public static final int NONE = 0; // 0x0
@@ -15058,7 +15072,6 @@
public static abstract interface UCharacter.SentenceBreak {
field public static final int ATERM = 1; // 0x1
field public static final int CLOSE = 2; // 0x2
- field public static final int COUNT = 15; // 0xf
field public static final int CR = 11; // 0xb
field public static final int EXTEND = 12; // 0xc
field public static final int FORMAT = 3; // 0x3
@@ -15201,7 +15214,6 @@
field public static final android.icu.lang.UCharacter.UnicodeBlock COPTIC_EPACT_NUMBERS;
field public static final int COPTIC_EPACT_NUMBERS_ID = 223; // 0xdf
field public static final int COPTIC_ID = 132; // 0x84
- field public static final int COUNT = 263; // 0x107
field public static final android.icu.lang.UCharacter.UnicodeBlock COUNTING_ROD_NUMERALS;
field public static final int COUNTING_ROD_NUMERALS_ID = 154; // 0x9a
field public static final android.icu.lang.UCharacter.UnicodeBlock CUNEIFORM;
@@ -15615,7 +15627,6 @@
public static abstract interface UCharacter.WordBreak {
field public static final int ALETTER = 1; // 0x1
- field public static final int COUNT = 17; // 0x11
field public static final int CR = 8; // 0x8
field public static final int DOUBLE_QUOTE = 16; // 0x10
field public static final int EXTEND = 9; // 0x9
@@ -15646,7 +15657,6 @@
}
public static abstract interface UCharacterEnums.ECharacterCategory {
- field public static final byte CHAR_CATEGORY_COUNT = 30; // 0x1e
field public static final byte COMBINING_SPACING_MARK = 8; // 0x8
field public static final byte CONNECTOR_PUNCTUATION = 22; // 0x16
field public static final byte CONTROL = 15; // 0xf
@@ -15686,7 +15696,6 @@
field public static final int ARABIC_NUMBER = 5; // 0x5
field public static final int BLOCK_SEPARATOR = 7; // 0x7
field public static final int BOUNDARY_NEUTRAL = 18; // 0x12
- field public static final int CHAR_DIRECTION_COUNT = 23; // 0x17
field public static final int COMMON_NUMBER_SEPARATOR = 6; // 0x6
field public static final byte DIRECTIONALITY_ARABIC_NUMBER = 5; // 0x5
field public static final byte DIRECTIONALITY_BOUNDARY_NEUTRAL = 18; // 0x12
@@ -15739,7 +15748,6 @@
field public static final int BIDI_MIRRORING_GLYPH = 16385; // 0x4001
field public static final int BIDI_PAIRED_BRACKET = 16397; // 0x400d
field public static final int BIDI_PAIRED_BRACKET_TYPE = 4117; // 0x1015
- field public static final int BINARY_LIMIT = 57; // 0x39
field public static final int BINARY_START = 0; // 0x0
field public static final int BLOCK = 4097; // 0x1001
field public static final int CANONICAL_COMBINING_CLASS = 4098; // 0x1002
@@ -15758,7 +15766,6 @@
field public static final int DEFAULT_IGNORABLE_CODE_POINT = 5; // 0x5
field public static final int DEPRECATED = 6; // 0x6
field public static final int DIACRITIC = 7; // 0x7
- field public static final int DOUBLE_LIMIT = 12289; // 0x3001
field public static final int DOUBLE_START = 12288; // 0x3000
field public static final int EAST_ASIAN_WIDTH = 4100; // 0x1004
field public static final int EXTENDER = 8; // 0x8
@@ -15777,7 +15784,6 @@
field public static final int IDS_TRINARY_OPERATOR = 19; // 0x13
field public static final int ID_CONTINUE = 15; // 0xf
field public static final int ID_START = 16; // 0x10
- field public static final int INT_LIMIT = 4118; // 0x1016
field public static final int INT_START = 4096; // 0x1000
field public static final int JOINING_GROUP = 4102; // 0x1006
field public static final int JOINING_TYPE = 4103; // 0x1007
@@ -15787,7 +15793,6 @@
field public static final int LOGICAL_ORDER_EXCEPTION = 21; // 0x15
field public static final int LOWERCASE = 22; // 0x16
field public static final int LOWERCASE_MAPPING = 16388; // 0x4004
- field public static final int MASK_LIMIT = 8193; // 0x2001
field public static final int MASK_START = 8192; // 0x2000
field public static final int MATH = 23; // 0x17
field public static final int NAME = 16389; // 0x4005
@@ -15802,7 +15807,6 @@
field public static final int NONCHARACTER_CODE_POINT = 24; // 0x18
field public static final int NUMERIC_TYPE = 4105; // 0x1009
field public static final int NUMERIC_VALUE = 12288; // 0x3000
- field public static final int OTHER_PROPERTY_LIMIT = 28673; // 0x7001
field public static final int OTHER_PROPERTY_START = 28672; // 0x7000
field public static final int PATTERN_SYNTAX = 42; // 0x2a
field public static final int PATTERN_WHITE_SPACE = 43; // 0x2b
@@ -15822,7 +15826,6 @@
field public static final int SIMPLE_TITLECASE_MAPPING = 16392; // 0x4008
field public static final int SIMPLE_UPPERCASE_MAPPING = 16393; // 0x4009
field public static final int SOFT_DOTTED = 27; // 0x1b
- field public static final int STRING_LIMIT = 16398; // 0x400e
field public static final int STRING_START = 16384; // 0x4000
field public static final int S_TERM = 35; // 0x23
field public static final int TERMINAL_PUNCTUATION = 28; // 0x1c
@@ -15839,7 +15842,6 @@
}
public static abstract interface UProperty.NameChoice {
- field public static final int COUNT = 2; // 0x2
field public static final int LONG = 1; // 0x1
field public static final int SHORT = 0; // 0x0
}
@@ -15884,7 +15886,6 @@
field public static final int CHAM = 66; // 0x42
field public static final int CHEROKEE = 6; // 0x6
field public static final int CIRTH = 67; // 0x43
- field public static final int CODE_LIMIT = 167; // 0xa7
field public static final int COMMON = 0; // 0x0
field public static final int COPTIC = 7; // 0x7
field public static final int CUNEIFORM = 101; // 0x65
@@ -16279,7 +16280,6 @@
public final class CollationKey implements java.lang.Comparable {
ctor public CollationKey(java.lang.String, byte[]);
- ctor public CollationKey(java.lang.String, android.icu.text.RawCollationKey);
method public int compareTo(android.icu.text.CollationKey);
method public boolean equals(android.icu.text.CollationKey);
method public android.icu.text.CollationKey getBound(int, int);
@@ -16289,7 +16289,6 @@
}
public static final class CollationKey.BoundMode {
- field public static final int COUNT = 3; // 0x3
field public static final int LOWER = 0; // 0x0
field public static final int UPPER = 1; // 0x1
field public static final int UPPER_LONG = 2; // 0x2
@@ -16321,7 +16320,6 @@
method public static final java.lang.String[] getKeywordValuesForLocale(java.lang.String, android.icu.util.ULocale, boolean);
method public static final java.lang.String[] getKeywords();
method public int getMaxVariable();
- method public abstract android.icu.text.RawCollationKey getRawCollationKey(java.lang.String, android.icu.text.RawCollationKey);
method public int[] getReorderCodes();
method public int getStrength();
method public android.icu.text.UnicodeSet getTailoredSet();
@@ -16361,7 +16359,6 @@
field public static final int DEFAULT = -1; // 0xffffffff
field public static final int DIGIT = 4100; // 0x1004
field public static final int FIRST = 4096; // 0x1000
- field public static final int LIMIT = 4101; // 0x1005
field public static final int NONE = 103; // 0x67
field public static final int OTHERS = 103; // 0x67
field public static final int PUNCTUATION = 4097; // 0x1001
@@ -16474,7 +16471,6 @@
field public static final int DOW_LOCAL_FIELD = 19; // 0x13
field public static final int ERA_FIELD = 0; // 0x0
field public static final int EXTENDED_YEAR_FIELD = 20; // 0x14
- field public static final int FIELD_COUNT = 36; // 0x24
field public static final int FRACTIONAL_SECOND_FIELD = 8; // 0x8
field public static final int FULL = 0; // 0x0
field public static final java.lang.String GENERIC_TZ = "vvvv";
@@ -16718,7 +16714,6 @@
field public static final int MONTH = 3; // 0x3
field public static final int QUARTER = 2; // 0x2
field public static final int SECOND = 13; // 0xd
- field public static final int TYPE_LIMIT = 16; // 0x10
field public static final int WEEKDAY = 6; // 0x6
field public static final int WEEK_OF_MONTH = 5; // 0x5
field public static final int WEEK_OF_YEAR = 4; // 0x4
@@ -17349,14 +17344,6 @@
enum_constant public static final android.icu.text.PluralRules.PluralType ORDINAL;
}
- public final class RawCollationKey extends android.icu.util.ByteArrayWrapper {
- ctor public RawCollationKey();
- ctor public RawCollationKey(int);
- ctor public RawCollationKey(byte[]);
- ctor public RawCollationKey(byte[], int);
- method public int compareTo(android.icu.text.RawCollationKey);
- }
-
public final class RelativeDateTimeFormatter {
method public java.lang.String combineDateAndTime(java.lang.String, java.lang.String);
method public java.lang.String format(double, android.icu.text.RelativeDateTimeFormatter.Direction, android.icu.text.RelativeDateTimeFormatter.RelativeUnit);
@@ -17440,7 +17427,6 @@
method public android.icu.text.CollationKey getCollationKey(java.lang.String);
method public void getContractionsAndExpansions(android.icu.text.UnicodeSet, android.icu.text.UnicodeSet, boolean) throws java.lang.Exception;
method public boolean getNumericCollation();
- method public android.icu.text.RawCollationKey getRawCollationKey(java.lang.String, android.icu.text.RawCollationKey);
method public java.lang.String getRules();
method public java.lang.String getRules(boolean);
method public android.icu.util.VersionInfo getUCAVersion();
@@ -17935,19 +17921,6 @@
field public static final int BE = 0; // 0x0
}
- public class ByteArrayWrapper implements java.lang.Comparable {
- ctor public ByteArrayWrapper();
- ctor public ByteArrayWrapper(byte[], int);
- ctor public ByteArrayWrapper(java.nio.ByteBuffer);
- method public final android.icu.util.ByteArrayWrapper append(byte[], int, int);
- method public int compareTo(android.icu.util.ByteArrayWrapper);
- method public android.icu.util.ByteArrayWrapper ensureCapacity(int);
- method public final byte[] releaseBytes();
- method public final android.icu.util.ByteArrayWrapper set(byte[], int, int);
- field public byte[] bytes;
- field public int size;
- }
-
abstract class CECalendar extends android.icu.util.Calendar {
ctor protected CECalendar();
ctor protected CECalendar(android.icu.util.TimeZone);
@@ -19305,10 +19278,19 @@
field public static final int MULTIPATH_INDICATOR_DETECTED = 1; // 0x1
field public static final int MULTIPATH_INDICATOR_NOT_USED = 2; // 0x2
field public static final int MULTIPATH_INDICATOR_UNKNOWN = 0; // 0x0
+ field public static final int STATE_BDS_D2_BIT_SYNC = 256; // 0x100
+ field public static final int STATE_BDS_D2_SUBFRAME_SYNC = 512; // 0x200
field public static final int STATE_BIT_SYNC = 2; // 0x2
field public static final int STATE_CODE_LOCK = 1; // 0x1
+ field public static final int STATE_GAL_E1BC_CODE_LOCK = 1024; // 0x400
+ field public static final int STATE_GAL_E1B_PAGE_SYNC = 4096; // 0x1000
+ field public static final int STATE_GAL_E1C_2ND_CODE_LOCK = 2048; // 0x800
+ field public static final int STATE_GLO_STRING_SYNC = 64; // 0x40
+ field public static final int STATE_GLO_TOD_DECODED = 128; // 0x80
field public static final int STATE_MSEC_AMBIGUOUS = 16; // 0x10
+ field public static final int STATE_SBAS_SYNC = 8192; // 0x2000
field public static final int STATE_SUBFRAME_SYNC = 4; // 0x4
+ field public static final int STATE_SYMBOL_SYNC = 32; // 0x20
field public static final int STATE_TOW_DECODED = 8; // 0x8
field public static final int STATE_UNKNOWN = 0; // 0x0
}
@@ -19905,7 +19887,7 @@
public static abstract class AudioManager.AudioRecordingCallback {
ctor public AudioManager.AudioRecordingCallback();
- method public void onRecordConfigChanged(android.media.AudioRecordingConfiguration[]);
+ method public void onRecordingConfigChanged(android.media.AudioRecordingConfiguration[]);
}
public static abstract interface AudioManager.OnAudioFocusChangeListener {
@@ -20163,7 +20145,6 @@
}
public abstract class DrmInitData {
- ctor public DrmInitData();
method public abstract android.media.DrmInitData.SchemeInitData get(java.util.UUID);
}
@@ -20182,6 +20163,7 @@
method public int getAttributeInt(java.lang.String, int);
method public boolean getLatLong(float[]);
method public byte[] getThumbnail();
+ method public long[] getThumbnailRange();
method public boolean hasThumbnail();
method public void saveAttributes() throws java.io.IOException;
method public void setAttribute(java.lang.String, java.lang.String);
@@ -20194,7 +20176,7 @@
field public static final int ORIENTATION_TRANSPOSE = 5; // 0x5
field public static final int ORIENTATION_TRANSVERSE = 7; // 0x7
field public static final int ORIENTATION_UNDEFINED = 0; // 0x0
- field public static final java.lang.String TAG_APERTURE = "FNumber";
+ field public static final deprecated java.lang.String TAG_APERTURE = "FNumber";
field public static final java.lang.String TAG_APERTURE_VALUE = "ApertureValue";
field public static final java.lang.String TAG_ARTIST = "Artist";
field public static final java.lang.String TAG_BITS_PER_SAMPLE = "BitsPerSample";
@@ -20265,7 +20247,7 @@
field public static final java.lang.String TAG_IMAGE_UNIQUE_ID = "ImageUniqueID";
field public static final java.lang.String TAG_IMAGE_WIDTH = "ImageWidth";
field public static final java.lang.String TAG_INTEROPERABILITY_INDEX = "InteroperabilityIndex";
- field public static final java.lang.String TAG_ISO = "ISOSpeedRatings";
+ field public static final deprecated java.lang.String TAG_ISO = "ISOSpeedRatings";
field public static final java.lang.String TAG_ISO_SPEED_RATINGS = "ISOSpeedRatings";
field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT = "JPEGInterchangeFormat";
field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = "JPEGInterchangeFormatLength";
@@ -20417,8 +20399,8 @@
public class MediaActionSound {
ctor public MediaActionSound();
- method public synchronized void load(int);
- method public synchronized void play(int);
+ method public void load(int);
+ method public void play(int);
method public void release();
field public static final int FOCUS_COMPLETE = 1; // 0x1
field public static final int SHUTTER_CLICK = 0; // 0x0
@@ -20673,12 +20655,13 @@
field public static final int DolbyVisionLevelUhd30 = 64; // 0x40
field public static final int DolbyVisionLevelUhd48 = 128; // 0x80
field public static final int DolbyVisionLevelUhd60 = 256; // 0x100
- field public static final int DolbyVisionProfileDvavDen = 2; // 0x2
- field public static final int DolbyVisionProfileDvavDer = 1; // 0x1
- field public static final int DolbyVisionProfileDvheDen = 4; // 0x4
- field public static final int DolbyVisionProfileDvheDer = 3; // 0x3
- field public static final int DolbyVisionProfileDvheDtr = 5; // 0x5
- field public static final int DolbyVisionProfileDvheStn = 6; // 0x6
+ field public static final int DolbyVisionProfileDvavPen = 2; // 0x2
+ field public static final int DolbyVisionProfileDvavPer = 1; // 0x1
+ field public static final int DolbyVisionProfileDvheDen = 8; // 0x8
+ field public static final int DolbyVisionProfileDvheDer = 4; // 0x4
+ field public static final int DolbyVisionProfileDvheDth = 64; // 0x40
+ field public static final int DolbyVisionProfileDvheDtr = 16; // 0x10
+ field public static final int DolbyVisionProfileDvheStn = 32; // 0x20
field public static final int H263Level10 = 1; // 0x1
field public static final int H263Level20 = 2; // 0x2
field public static final int H263Level30 = 4; // 0x4
@@ -22345,7 +22328,7 @@
method public void subscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback);
method public void subscribe(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SubscriptionCallback);
method public void unsubscribe(java.lang.String);
- method public void unsubscribe(java.lang.String, android.os.Bundle);
+ method public void unsubscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback);
field public static final java.lang.String EXTRA_PAGE = "android.media.browse.extra.PAGE";
field public static final java.lang.String EXTRA_PAGE_SIZE = "android.media.browse.extra.PAGE_SIZE";
}
@@ -23118,6 +23101,7 @@
method public abstract void onStartRecording(android.net.Uri);
method public abstract void onStopRecording();
method public abstract void onTune(android.net.Uri);
+ method public void onTune(android.net.Uri, android.os.Bundle);
}
public static abstract class TvInputService.Session implements android.view.KeyEvent.Callback {
@@ -23167,6 +23151,7 @@
method public void startRecording(android.net.Uri);
method public void stopRecording();
method public void tune(java.lang.String, android.net.Uri);
+ method public void tune(java.lang.String, android.net.Uri, android.os.Bundle);
}
public static abstract class TvRecordingClient.RecordingCallback {
@@ -23538,6 +23523,7 @@
method public boolean isActiveNetworkMetered();
method public boolean isDefaultNetworkActive();
method public static deprecated boolean isNetworkTypeValid(int);
+ method public void registerDefaultNetworkCallback(android.net.ConnectivityManager.NetworkCallback);
method public void registerNetworkCallback(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
method public void registerNetworkCallback(android.net.NetworkRequest, android.app.PendingIntent);
method public void releaseNetworkRequest(android.app.PendingIntent);
@@ -28803,6 +28789,7 @@
field public static final int TEMPERATURE_CURRENT = 0; // 0x0
field public static final int TEMPERATURE_SHUTDOWN = 2; // 0x2
field public static final int TEMPERATURE_THROTTLING = 1; // 0x1
+ field public static final int TEMPERATURE_THROTTLING_BELOW_VR_MIN = 3; // 0x3
field public static final float UNDEFINED_TEMPERATURE = -3.4028235E38f;
}
@@ -29162,6 +29149,7 @@
method public boolean isInteractive();
method public boolean isPowerSaveMode();
method public deprecated boolean isScreenOn();
+ method public boolean isSustainedPerformanceModeSupported();
method public boolean isWakeLockLevelSupported(int);
method public android.os.PowerManager.WakeLock newWakeLock(int, java.lang.String);
method public void reboot(java.lang.String);
@@ -29175,6 +29163,7 @@
field public static final int RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY = 1; // 0x1
field public static final deprecated int SCREEN_BRIGHT_WAKE_LOCK = 10; // 0xa
field public static final deprecated int SCREEN_DIM_WAKE_LOCK = 6; // 0x6
+ field public static final int SUSTAINED_PERFORMANCE_WAKE_LOCK = 256; // 0x100
}
public final class PowerManager.WakeLock {
@@ -29532,7 +29521,7 @@
method public android.os.health.HealthStats[] takeUidSnapshots(int[]);
}
- public class TimerStat implements android.os.Parcelable {
+ public final class TimerStat implements android.os.Parcelable {
ctor public TimerStat();
ctor public TimerStat(int, long);
ctor public TimerStat(android.os.Parcel);
@@ -29630,8 +29619,8 @@
public class StorageManager {
method public java.lang.String getMountedObbPath(java.lang.String);
- method public android.os.storage.StorageVolume getPrimaryVolume();
- method public android.os.storage.StorageVolume[] getVolumeList();
+ method public android.os.storage.StorageVolume getPrimaryStorageVolume();
+ method public java.util.List<android.os.storage.StorageVolume> getStorageVolumes();
method public boolean isEncrypted(java.io.File);
method public boolean isObbMounted(java.lang.String);
method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener);
@@ -29788,7 +29777,6 @@
method protected void onClick();
method protected android.view.View onCreateView(android.view.ViewGroup);
method public void onDependencyChanged(android.preference.Preference, boolean);
- method protected void onDetachedFromActivity();
method protected java.lang.Object onGetDefaultValue(android.content.res.TypedArray, int);
method public void onParentChanged(android.preference.Preference, boolean);
method protected void onPrepareForRemoval();
@@ -29959,6 +29947,8 @@
method public android.content.SharedPreferences getSharedPreferences();
method public int getSharedPreferencesMode();
method public java.lang.String getSharedPreferencesName();
+ method public boolean isStorageDefault();
+ method public boolean isStorageDeviceProtected();
method public static void setDefaultValues(android.content.Context, int, boolean);
method public static void setDefaultValues(android.content.Context, java.lang.String, int, int, boolean);
method public void setSharedPreferencesMode(int);
@@ -30260,7 +30250,7 @@
method public android.print.PrinterId getPrinterId();
method public float getProgress();
method public int getState();
- method public java.lang.CharSequence getStatus();
+ method public java.lang.CharSequence getStatus(android.content.pm.PackageManager);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.print.PrintJobInfo> CREATOR;
field public static final int STATE_BLOCKED = 4; // 0x4
@@ -30337,7 +30327,7 @@
method public android.print.PrinterInfo build();
method public android.print.PrinterInfo.Builder setCapabilities(android.print.PrinterCapabilitiesInfo);
method public android.print.PrinterInfo.Builder setDescription(java.lang.String);
- method public android.print.PrinterInfo.Builder setHasCustomPrinterIcon();
+ method public android.print.PrinterInfo.Builder setHasCustomPrinterIcon(boolean);
method public android.print.PrinterInfo.Builder setIconResourceId(int);
method public android.print.PrinterInfo.Builder setInfoIntent(android.app.PendingIntent);
method public android.print.PrinterInfo.Builder setName(java.lang.String);
@@ -30389,6 +30379,7 @@
method public boolean isStarted();
method public void setProgress(float);
method public void setStatus(java.lang.CharSequence);
+ method public void setStatus(int);
method public boolean setTag(java.lang.String);
method public boolean start();
}
@@ -30419,7 +30410,7 @@
method public final boolean isDestroyed();
method public final boolean isPrinterDiscoveryStarted();
method public abstract void onDestroy();
- method public void onRequestCustomPrinterIcon(android.print.PrinterId, android.printservice.CustomPrinterIconCallback);
+ method public void onRequestCustomPrinterIcon(android.print.PrinterId, android.os.CancellationSignal, android.printservice.CustomPrinterIconCallback);
method public abstract void onStartPrinterDiscovery(java.util.List<android.print.PrinterId>);
method public abstract void onStartPrinterStateTracking(android.print.PrinterId);
method public abstract void onStopPrinterDiscovery();
@@ -30465,6 +30456,7 @@
public class BlockedNumberContract {
method public static boolean canCurrentUserBlockNumbers(android.content.Context);
method public static boolean isBlocked(android.content.Context, java.lang.String);
+ method public static int unblock(android.content.Context, java.lang.String);
field public static final java.lang.String AUTHORITY = "com.android.blockednumber";
field public static final android.net.Uri AUTHORITY_URI;
}
@@ -30824,6 +30816,7 @@
field public static final int REJECTED_TYPE = 5; // 0x5
field public static final java.lang.String TRANSCRIPTION = "transcription";
field public static final java.lang.String TYPE = "type";
+ field public static final java.lang.String VIA_NUMBER = "via_number";
field public static final int VOICEMAIL_TYPE = 4; // 0x4
field public static final java.lang.String VOICEMAIL_URI = "voicemail_uri";
}
@@ -31974,6 +31967,7 @@
field public static final java.lang.String EXTRA_EXCLUDE_SELF = "android.provider.extra.EXCLUDE_SELF";
field public static final java.lang.String EXTRA_INFO = "info";
field public static final java.lang.String EXTRA_LOADING = "loading";
+ field public static final java.lang.String EXTRA_ORIENTATION = "android.provider.extra.ORIENTATION";
field public static final java.lang.String EXTRA_PROMPT = "android.provider.extra.PROMPT";
field public static final java.lang.String PROVIDER_INTERFACE = "android.content.action.DOCUMENTS_PROVIDER";
}
@@ -32457,6 +32451,7 @@
field public static final java.lang.String ACTION_VOICE_CONTROL_BATTERY_SAVER_MODE = "android.settings.VOICE_CONTROL_BATTERY_SAVER_MODE";
field public static final java.lang.String ACTION_VOICE_CONTROL_DO_NOT_DISTURB_MODE = "android.settings.VOICE_CONTROL_DO_NOT_DISTURB_MODE";
field public static final java.lang.String ACTION_VOICE_INPUT_SETTINGS = "android.settings.VOICE_INPUT_SETTINGS";
+ field public static final java.lang.String ACTION_VPN_SETTINGS = "android.settings.VPN_SETTINGS";
field public static final java.lang.String ACTION_VR_LISTENER_SETTINGS = "android.settings.VR_LISTENER_SETTINGS";
field public static final java.lang.String ACTION_WIFI_IP_SETTINGS = "android.settings.WIFI_IP_SETTINGS";
field public static final java.lang.String ACTION_WIFI_SETTINGS = "android.settings.WIFI_SETTINGS";
@@ -34336,6 +34331,7 @@
public class NetworkSecurityPolicy {
method public static android.security.NetworkSecurityPolicy getInstance();
+ method public void handleTrustStorageUpdate();
method public boolean isCleartextTrafficPermitted();
method public boolean isCleartextTrafficPermitted(java.lang.String);
}
@@ -34648,7 +34644,6 @@
method public boolean onMenuOpened(int, android.view.Menu);
method public void onPanelClosed(int, android.view.Menu);
method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
- method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu, int);
method public boolean onSearchRequested(android.view.SearchEvent);
method public boolean onSearchRequested();
method public void onWakeUp();
@@ -36212,6 +36207,7 @@
method public final android.telecom.CallAudioState getCallAudioState();
method public final java.util.List<android.telecom.Connection> getConferenceableConnections();
method public final int getConnectionCapabilities();
+ method public final int getConnectionProperties();
method public final long getConnectionTime();
method public final java.util.List<android.telecom.Connection> getConnections();
method public final android.telecom.DisconnectCause getDisconnectCause();
@@ -36239,6 +36235,7 @@
method public final void setActive();
method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
method public final void setConnectionCapabilities(int);
+ method public final void setConnectionProperties(int);
method public final void setConnectionTime(long);
method public final void setDialing();
method public final void setDisconnected(android.telecom.DisconnectCause);
@@ -36268,6 +36265,7 @@
method public final android.telecom.Conference getConference();
method public final java.util.List<android.telecom.Conferenceable> getConferenceables();
method public final int getConnectionCapabilities();
+ method public final int getConnectionProperties();
method public final android.telecom.DisconnectCause getDisconnectCause();
method public final android.os.Bundle getExtras();
method public final int getState();
@@ -36291,6 +36289,7 @@
method public void onStateChanged(int);
method public void onStopDtmfTone();
method public void onUnhold();
+ method public static java.lang.String propertiesToString(int);
method public final void putExtras(android.os.Bundle);
method public final void removeExtras(java.util.List<java.lang.String>);
method public void sendConnectionEvent(java.lang.String, android.os.Bundle);
@@ -36301,6 +36300,7 @@
method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
method public final void setConferenceables(java.util.List<android.telecom.Conferenceable>);
method public final void setConnectionCapabilities(int);
+ method public final void setConnectionProperties(int);
method public final void setDialing();
method public final void setDisconnected(android.telecom.DisconnectCause);
method public final deprecated void setExtras(android.os.Bundle);
@@ -36317,12 +36317,11 @@
method public static java.lang.String stateToString(int);
field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 8388608; // 0x800000
field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
- field public static final int CAPABILITY_CAN_PULL_CALL = 33554432; // 0x2000000
+ field public static final int CAPABILITY_CAN_PULL_CALL = 16777216; // 0x1000000
field public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 4194304; // 0x400000
field public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 524288; // 0x80000
field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
field public static final int CAPABILITY_HOLD = 1; // 0x1
- field public static final int CAPABILITY_IS_EXTERNAL_CALL = 16777216; // 0x1000000
field public static final int CAPABILITY_MANAGE_CONFERENCE = 128; // 0x80
field public static final int CAPABILITY_MERGE_CONFERENCE = 4; // 0x4
field public static final int CAPABILITY_MUTE = 64; // 0x40
@@ -36340,6 +36339,7 @@
field public static final java.lang.String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
field public static final java.lang.String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS";
field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
+ field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
field public static final int STATE_ACTIVE = 4; // 0x4
field public static final int STATE_DIALING = 3; // 0x3
field public static final int STATE_DISCONNECTED = 6; // 0x6
@@ -36562,6 +36562,7 @@
method public void disconnect();
method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections();
method public final int getConnectionCapabilities();
+ method public final int getConnectionProperties();
method public final java.util.List<android.telecom.RemoteConnection> getConnections();
method public android.telecom.DisconnectCause getDisconnectCause();
method public final android.os.Bundle getExtras();
@@ -36584,6 +36585,7 @@
method public void onConferenceableConnectionsChanged(android.telecom.RemoteConference, java.util.List<android.telecom.RemoteConnection>);
method public void onConnectionAdded(android.telecom.RemoteConference, android.telecom.RemoteConnection);
method public void onConnectionCapabilitiesChanged(android.telecom.RemoteConference, int);
+ method public void onConnectionPropertiesChanged(android.telecom.RemoteConference, int);
method public void onConnectionRemoved(android.telecom.RemoteConference, android.telecom.RemoteConnection);
method public void onDestroyed(android.telecom.RemoteConference);
method public void onDisconnected(android.telecom.RemoteConference, android.telecom.DisconnectCause);
@@ -36602,6 +36604,7 @@
method public android.telecom.RemoteConference getConference();
method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections();
method public int getConnectionCapabilities();
+ method public int getConnectionProperties();
method public android.telecom.DisconnectCause getDisconnectCause();
method public final android.os.Bundle getExtras();
method public int getState();
@@ -36631,6 +36634,7 @@
method public void onConferenceableConnectionsChanged(android.telecom.RemoteConnection, java.util.List<android.telecom.RemoteConnection>);
method public void onConnectionCapabilitiesChanged(android.telecom.RemoteConnection, int);
method public void onConnectionEvent(android.telecom.RemoteConnection, java.lang.String, android.os.Bundle);
+ method public void onConnectionPropertiesChanged(android.telecom.RemoteConnection, int);
method public void onDestroyed(android.telecom.RemoteConnection);
method public void onDisconnected(android.telecom.RemoteConnection, android.telecom.DisconnectCause);
method public void onExtrasChanged(android.telecom.RemoteConnection, android.os.Bundle);
@@ -36696,7 +36700,6 @@
method public boolean handleMmi(java.lang.String, android.telecom.PhoneAccountHandle);
method public boolean isInCall();
method public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, java.lang.String);
- method public deprecated void launchManageBlockedNumbersActivity();
method public void placeCall(android.net.Uri, android.os.Bundle);
method public void registerPhoneAccount(android.telecom.PhoneAccount);
method public void showInCallScreen(boolean);
@@ -36808,6 +36811,7 @@
field public static final java.lang.String KEY_CARRIER_VT_AVAILABLE_BOOL = "carrier_vt_available_bool";
field public static final java.lang.String KEY_CARRIER_VVM_PACKAGE_NAME_STRING = "carrier_vvm_package_name_string";
field public static final java.lang.String KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL = "carrier_wfc_ims_available_bool";
+ field public static final java.lang.String KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL = "carrier_wfc_supports_wifi_only_bool";
field public static final java.lang.String KEY_CDMA_DTMF_TONE_DELAY_INT = "cdma_dtmf_tone_delay_int";
field public static final java.lang.String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array";
field public static final java.lang.String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array";
@@ -37384,7 +37388,8 @@
method public java.lang.String getDeviceSoftwareVersion();
method public java.lang.String getGroupIdLevel1();
method public java.lang.String getGroupIdLevel1(int);
- method public java.lang.String getIccSimChallengeResponse(int, java.lang.String);
+ method public java.lang.String getIccAuthentication(int, int, java.lang.String);
+ method public java.lang.String getIccAuthentication(int, int, int, java.lang.String);
method public java.lang.String getLine1AlphaTag(int);
method public java.lang.String getLine1Number();
method public java.lang.String getLine1Number(int);
@@ -37453,6 +37458,13 @@
field public static final java.lang.String ACTION_CONFIGURE_VOICEMAIL = "android.telephony.action.CONFIGURE_VOICEMAIL";
field public static final java.lang.String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
field public static final java.lang.String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
+ field public static final int APPTYPE_CSIM = 4; // 0x4
+ field public static final int APPTYPE_ISIM = 5; // 0x5
+ field public static final int APPTYPE_RUIM = 3; // 0x3
+ field public static final int APPTYPE_SIM = 1; // 0x1
+ field public static final int APPTYPE_USIM = 2; // 0x2
+ field public static final int AUTHTYPE_EAP_AKA = 129; // 0x81
+ field public static final int AUTHTYPE_EAP_SIM = 128; // 0x80
field public static final int CALL_STATE_IDLE = 0; // 0x0
field public static final int CALL_STATE_OFFHOOK = 2; // 0x2
field public static final int CALL_STATE_RINGING = 1; // 0x1
@@ -37996,8 +38008,6 @@
method public java.lang.String getPackageResourcePath();
method public android.content.res.Resources getResources();
method public android.content.SharedPreferences getSharedPreferences(java.lang.String, int);
- method public android.content.SharedPreferences getSharedPreferences(java.io.File, int);
- method public java.io.File getSharedPreferencesPath(java.lang.String);
method public java.lang.Object getSystemService(java.lang.String);
method public java.lang.String getSystemServiceName(java.lang.Class<?>);
method public android.content.res.Resources.Theme getTheme();
@@ -42354,6 +42364,7 @@
method public boolean dispatchDragEvent(android.view.DragEvent);
method protected void dispatchDraw(android.graphics.Canvas);
method public void dispatchDrawableHotspotChanged(float, float);
+ method public void dispatchFinishTemporaryDetach();
method protected boolean dispatchGenericFocusedEvent(android.view.MotionEvent);
method public boolean dispatchGenericMotionEvent(android.view.MotionEvent);
method protected boolean dispatchGenericPointerEvent(android.view.MotionEvent);
@@ -42373,6 +42384,7 @@
method protected void dispatchSetActivated(boolean);
method protected void dispatchSetPressed(boolean);
method protected void dispatchSetSelected(boolean);
+ method public void dispatchStartTemporaryDetach();
method public void dispatchSystemUiVisibilityChanged(int);
method public boolean dispatchTouchEvent(android.view.MotionEvent);
method public boolean dispatchTrackballEvent(android.view.MotionEvent);
@@ -42390,6 +42402,7 @@
method public void findViewsWithText(java.util.ArrayList<android.view.View>, java.lang.CharSequence, int);
method protected deprecated boolean fitSystemWindows(android.graphics.Rect);
method public android.view.View focusSearch(int);
+ method public void forceHasOverlappingRendering(boolean);
method public void forceLayout();
method public static int generateViewId();
method public java.lang.CharSequence getAccessibilityClassName();
@@ -42435,6 +42448,7 @@
method public boolean getGlobalVisibleRect(android.graphics.Rect, android.graphics.Point);
method public final boolean getGlobalVisibleRect(android.graphics.Rect);
method public android.os.Handler getHandler();
+ method public final boolean getHasOverlappingRendering();
method public final int getHeight();
method public void getHitRect(android.graphics.Rect);
method public int getHorizontalFadingEdgeLength();
@@ -42583,6 +42597,7 @@
method public boolean isSelected();
method public boolean isShown();
method public boolean isSoundEffectsEnabled();
+ method public final boolean isTemporarilyDetached();
method public boolean isTextAlignmentResolved();
method public boolean isTextDirectionResolved();
method public boolean isVerticalFadingEdgeEnabled();
@@ -44167,8 +44182,6 @@
method public void setVisibleToUser(boolean);
method public void writeToParcel(android.os.Parcel, int);
field public static final int ACTION_ACCESSIBILITY_FOCUS = 64; // 0x40
- field public static final java.lang.String ACTION_ARGUMENT_CLICK_CHARACTER_INDEX_INT = "android.view.accessibility.action.ARGUMENT_CLICK_CHARACTER_INDEX_INT";
- field public static final java.lang.String ACTION_ARGUMENT_CLICK_SPAN_INDEX_INT = "android.view.accessibility.action.ARGUMENT_CLICK_SPAN_INDEX_INT";
field public static final java.lang.String ACTION_ARGUMENT_COLUMN_INT = "android.view.accessibility.action.ARGUMENT_COLUMN_INT";
field public static final java.lang.String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN = "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
field public static final java.lang.String ACTION_ARGUMENT_HTML_ELEMENT_STRING = "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
@@ -44339,6 +44352,7 @@
public final class AccessibilityWindowInfo implements android.os.Parcelable {
method public int describeContents();
+ method public android.view.accessibility.AccessibilityNodeInfo getAnchor();
method public void getBoundsInScreen(android.graphics.Rect);
method public android.view.accessibility.AccessibilityWindowInfo getChild(int);
method public int getChildCount();
@@ -44346,6 +44360,7 @@
method public int getLayer();
method public android.view.accessibility.AccessibilityWindowInfo getParent();
method public android.view.accessibility.AccessibilityNodeInfo getRoot();
+ method public java.lang.CharSequence getTitle();
method public int getType();
method public boolean isAccessibilityFocused();
method public boolean isActive();
@@ -44686,6 +44701,7 @@
ctor public BaseInputConnection(android.view.View, boolean);
method public boolean beginBatchEdit();
method public boolean clearMetaKeyStates(int);
+ method public void closeConnection();
method public boolean commitCompletion(android.view.inputmethod.CompletionInfo);
method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public boolean commitText(java.lang.CharSequence, int);
@@ -44853,6 +44869,7 @@
public abstract interface InputConnection {
method public abstract boolean beginBatchEdit();
method public abstract boolean clearMetaKeyStates(int);
+ method public abstract void closeConnection();
method public abstract boolean commitCompletion(android.view.inputmethod.CompletionInfo);
method public abstract boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public abstract boolean commitText(java.lang.CharSequence, int);
@@ -44885,6 +44902,7 @@
ctor public InputConnectionWrapper(android.view.inputmethod.InputConnection, boolean);
method public boolean beginBatchEdit();
method public boolean clearMetaKeyStates(int);
+ method public void closeConnection();
method public boolean commitCompletion(android.view.inputmethod.CompletionInfo);
method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public boolean commitText(java.lang.CharSequence, int);
@@ -46334,7 +46352,7 @@
method public long getMinDate();
method public deprecated android.graphics.drawable.Drawable getSelectedDateVerticalBar();
method public deprecated int getSelectedWeekBackgroundColor();
- method public boolean getShowWeekNumber();
+ method public deprecated boolean getShowWeekNumber();
method public deprecated int getShownWeekCount();
method public deprecated int getUnfocusedMonthDateColor();
method public int getWeekDayTextAppearance();
@@ -46351,7 +46369,7 @@
method public deprecated void setSelectedDateVerticalBar(int);
method public deprecated void setSelectedDateVerticalBar(android.graphics.drawable.Drawable);
method public deprecated void setSelectedWeekBackgroundColor(int);
- method public void setShowWeekNumber(boolean);
+ method public deprecated void setShowWeekNumber(boolean);
method public deprecated void setShownWeekCount(int);
method public deprecated void setUnfocusedMonthDateColor(int);
method public void setWeekDayTextAppearance(int);
@@ -46498,21 +46516,21 @@
ctor public DatePicker(android.content.Context, android.util.AttributeSet);
ctor public DatePicker(android.content.Context, android.util.AttributeSet, int);
ctor public DatePicker(android.content.Context, android.util.AttributeSet, int, int);
- method public android.widget.CalendarView getCalendarView();
- method public boolean getCalendarViewShown();
+ method public deprecated android.widget.CalendarView getCalendarView();
+ method public deprecated boolean getCalendarViewShown();
method public int getDayOfMonth();
method public int getFirstDayOfWeek();
method public long getMaxDate();
method public long getMinDate();
method public int getMonth();
- method public boolean getSpinnersShown();
+ method public deprecated boolean getSpinnersShown();
method public int getYear();
method public void init(int, int, int, android.widget.DatePicker.OnDateChangedListener);
- method public void setCalendarViewShown(boolean);
+ method public deprecated void setCalendarViewShown(boolean);
method public void setFirstDayOfWeek(int);
method public void setMaxDate(long);
method public void setMinDate(long);
- method public void setSpinnersShown(boolean);
+ method public deprecated void setSpinnersShown(boolean);
method public void updateDate(int, int, int);
}
@@ -48332,14 +48350,21 @@
method public void collapseActionView();
method public void dismissPopupMenus();
method public int getContentInsetEnd();
+ method public int getContentInsetEndWithActions();
method public int getContentInsetLeft();
method public int getContentInsetRight();
method public int getContentInsetStart();
+ method public int getContentInsetStartWithNavigation();
+ method public int getCurrentContentInsetEnd();
+ method public int getCurrentContentInsetLeft();
+ method public int getCurrentContentInsetRight();
+ method public int getCurrentContentInsetStart();
method public android.graphics.drawable.Drawable getLogo();
method public java.lang.CharSequence getLogoDescription();
method public android.view.Menu getMenu();
method public java.lang.CharSequence getNavigationContentDescription();
method public android.graphics.drawable.Drawable getNavigationIcon();
+ method public android.view.View getNavigationView();
method public android.graphics.drawable.Drawable getOverflowIcon();
method public int getPopupTheme();
method public java.lang.CharSequence getSubtitle();
@@ -48353,6 +48378,8 @@
method public void inflateMenu(int);
method public boolean isOverflowMenuShowing();
method protected void onLayout(boolean, int, int, int, int);
+ method public void setContentInsetEndWithActions(int);
+ method public void setContentInsetStartWithNavigation(int);
method public void setContentInsetsAbsolute(int, int);
method public void setContentInsetsRelative(int, int);
method public void setLogo(int);
@@ -49337,9 +49364,7 @@
public final class FilePermission extends java.security.Permission implements java.io.Serializable {
ctor public FilePermission(java.lang.String, java.lang.String);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -50078,6 +50103,7 @@
method public static int compare(boolean, boolean);
method public int compareTo(java.lang.Boolean);
method public static boolean getBoolean(java.lang.String);
+ method public static int hashCode(boolean);
method public static boolean parseBoolean(java.lang.String);
method public static java.lang.String toString(boolean);
method public static java.lang.Boolean valueOf(boolean);
@@ -50095,6 +50121,7 @@
method public static java.lang.Byte decode(java.lang.String) throws java.lang.NumberFormatException;
method public double doubleValue();
method public float floatValue();
+ method public static int hashCode(byte);
method public int intValue();
method public long longValue();
method public static byte parseByte(java.lang.String, int) throws java.lang.NumberFormatException;
@@ -50103,6 +50130,7 @@
method public static java.lang.Byte valueOf(byte);
method public static java.lang.Byte valueOf(java.lang.String, int) throws java.lang.NumberFormatException;
method public static java.lang.Byte valueOf(java.lang.String) throws java.lang.NumberFormatException;
+ field public static final int BYTES = 1; // 0x1
field public static final byte MAX_VALUE = 127; // 0x7f
field public static final byte MIN_VALUE = -128; // 0xffffff80
field public static final int SIZE = 8; // 0x8
@@ -50140,6 +50168,7 @@
method public static int getNumericValue(int);
method public static int getType(char);
method public static int getType(int);
+ method public static int hashCode(char);
method public static char highSurrogate(int);
method public static boolean isAlphabetic(int);
method public static boolean isBmpCodePoint(int);
@@ -50200,6 +50229,7 @@
method public static char toUpperCase(char);
method public static int toUpperCase(int);
method public static java.lang.Character valueOf(char);
+ field public static final int BYTES = 2; // 0x2
field public static final byte COMBINING_SPACING_MARK = 8; // 0x8
field public static final byte CONNECTOR_PUNCTUATION = 23; // 0x17
field public static final byte CONTROL = 15; // 0xf
@@ -50826,6 +50856,7 @@
method public static int floatToIntBits(float);
method public static int floatToRawIntBits(float);
method public float floatValue();
+ method public static int hashCode(float);
method public static float intBitsToFloat(int);
method public int intValue();
method public static boolean isFinite(float);
@@ -50834,11 +50865,15 @@
method public static boolean isNaN(float);
method public boolean isNaN();
method public long longValue();
+ method public static float max(float, float);
+ method public static float min(float, float);
method public static float parseFloat(java.lang.String) throws java.lang.NumberFormatException;
+ method public static float sum(float, float);
method public static java.lang.String toHexString(float);
method public static java.lang.String toString(float);
method public static java.lang.Float valueOf(java.lang.String) throws java.lang.NumberFormatException;
method public static java.lang.Float valueOf(float);
+ field public static final int BYTES = 4; // 0x4
field public static final int MAX_EXPONENT = 127; // 0x7f
field public static final float MAX_VALUE = 3.4028235E38f;
field public static final int MIN_EXPONENT = -126; // 0xffffff82
@@ -51018,6 +51053,7 @@
method public static java.lang.Long valueOf(java.lang.String, int) throws java.lang.NumberFormatException;
method public static java.lang.Long valueOf(java.lang.String) throws java.lang.NumberFormatException;
method public static java.lang.Long valueOf(long);
+ field public static final int BYTES = 8; // 0x8
field public static final long MAX_VALUE = 9223372036854775807L; // 0x7fffffffffffffffL
field public static final long MIN_VALUE = -9223372036854775808L; // 0x8000000000000000L
field public static final int SIZE = 64; // 0x40
@@ -51197,41 +51233,11 @@
method public java.io.File directory();
method public java.lang.ProcessBuilder directory(java.io.File);
method public java.util.Map<java.lang.String, java.lang.String> environment();
- method public java.lang.ProcessBuilder inheritIO();
- method public java.lang.ProcessBuilder redirectError(java.lang.ProcessBuilder.Redirect);
- method public java.lang.ProcessBuilder redirectError(java.io.File);
- method public java.lang.ProcessBuilder.Redirect redirectError();
method public boolean redirectErrorStream();
method public java.lang.ProcessBuilder redirectErrorStream(boolean);
- method public java.lang.ProcessBuilder redirectInput(java.lang.ProcessBuilder.Redirect);
- method public java.lang.ProcessBuilder redirectInput(java.io.File);
- method public java.lang.ProcessBuilder.Redirect redirectInput();
- method public java.lang.ProcessBuilder redirectOutput(java.lang.ProcessBuilder.Redirect);
- method public java.lang.ProcessBuilder redirectOutput(java.io.File);
- method public java.lang.ProcessBuilder.Redirect redirectOutput();
method public java.lang.Process start() throws java.io.IOException;
}
- public static abstract class ProcessBuilder.Redirect {
- method public static java.lang.ProcessBuilder.Redirect appendTo(java.io.File);
- method public java.io.File file();
- method public static java.lang.ProcessBuilder.Redirect from(java.io.File);
- method public static java.lang.ProcessBuilder.Redirect to(java.io.File);
- method public abstract java.lang.ProcessBuilder.Redirect.Type type();
- field public static final java.lang.ProcessBuilder.Redirect INHERIT;
- field public static final java.lang.ProcessBuilder.Redirect PIPE;
- }
-
- public static final class ProcessBuilder.Redirect.Type extends java.lang.Enum {
- method public static java.lang.ProcessBuilder.Redirect.Type valueOf(java.lang.String);
- method public static final java.lang.ProcessBuilder.Redirect.Type[] values();
- enum_constant public static final java.lang.ProcessBuilder.Redirect.Type APPEND;
- enum_constant public static final java.lang.ProcessBuilder.Redirect.Type INHERIT;
- enum_constant public static final java.lang.ProcessBuilder.Redirect.Type PIPE;
- enum_constant public static final java.lang.ProcessBuilder.Redirect.Type READ;
- enum_constant public static final java.lang.ProcessBuilder.Redirect.Type WRITE;
- }
-
public abstract interface Readable {
method public abstract int read(java.nio.CharBuffer) throws java.io.IOException;
}
@@ -51351,6 +51357,7 @@
method public static java.lang.Short decode(java.lang.String) throws java.lang.NumberFormatException;
method public double doubleValue();
method public float floatValue();
+ method public static int hashCode(short);
method public int intValue();
method public long longValue();
method public static short parseShort(java.lang.String, int) throws java.lang.NumberFormatException;
@@ -51360,6 +51367,7 @@
method public static java.lang.Short valueOf(java.lang.String, int) throws java.lang.NumberFormatException;
method public static java.lang.Short valueOf(java.lang.String) throws java.lang.NumberFormatException;
method public static java.lang.Short valueOf(short);
+ field public static final int BYTES = 2; // 0x2
field public static final short MAX_VALUE = 32767; // 0x7fff
field public static final short MIN_VALUE = -32768; // 0xffff8000
field public static final int SIZE = 16; // 0x10
@@ -52883,9 +52891,7 @@
public final class SocketPermission extends java.security.Permission implements java.io.Serializable {
ctor public SocketPermission(java.lang.String, java.lang.String);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -53967,9 +53973,7 @@
public final class AllPermission extends java.security.Permission {
ctor public AllPermission();
ctor public AllPermission(java.lang.String, java.lang.String);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -53983,9 +53987,7 @@
public abstract class BasicPermission extends java.security.Permission implements java.io.Serializable {
ctor public BasicPermission(java.lang.String);
ctor public BasicPermission(java.lang.String, java.lang.String);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -54363,10 +54365,8 @@
public abstract class Permission implements java.security.Guard java.io.Serializable {
ctor public Permission(java.lang.String);
method public void checkGuard(java.lang.Object) throws java.lang.SecurityException;
- method public abstract boolean equals(java.lang.Object);
method public abstract java.lang.String getActions();
method public final java.lang.String getName();
- method public abstract int hashCode();
method public abstract boolean implies(java.security.Permission);
method public java.security.PermissionCollection newPermissionCollection();
}
@@ -54624,13 +54624,11 @@
public final class UnresolvedPermission extends java.security.Permission implements java.io.Serializable {
ctor public UnresolvedPermission(java.lang.String, java.lang.String, java.lang.String, java.security.cert.Certificate[]);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
method public java.lang.String getUnresolvedActions();
method public java.security.cert.Certificate[] getUnresolvedCerts();
method public java.lang.String getUnresolvedName();
method public java.lang.String getUnresolvedType();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
@@ -54688,8 +54686,6 @@
}
public abstract interface Permission {
- method public abstract boolean equals(java.lang.Object);
- method public abstract java.lang.String toString();
}
}
@@ -57405,6 +57401,7 @@
method public void forEach(java.util.function.Consumer<? super E>);
method public E get(int);
method public boolean removeIf(java.util.function.Predicate<? super E>);
+ method public void replaceAll(java.util.function.UnaryOperator<E>);
method public int size();
method public void sort(java.util.Comparator<? super E>);
method public java.util.Spliterator<E> spliterator();
@@ -57490,6 +57487,10 @@
method public static int hashCode(float[]);
method public static int hashCode(double[]);
method public static int hashCode(java.lang.Object[]);
+ method public static void parallelSetAll(T[], java.util.function.IntFunction<? extends T>);
+ method public static void parallelSetAll(int[], java.util.function.IntUnaryOperator);
+ method public static void parallelSetAll(long[], java.util.function.IntToLongFunction);
+ method public static void parallelSetAll(double[], java.util.function.IntToDoubleFunction);
method public static void parallelSort(byte[]);
method public static void parallelSort(byte[], int, int);
method public static void parallelSort(char[]);
@@ -58038,6 +58039,7 @@
method public java.lang.Object clone();
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
method public void forEach(java.util.function.BiConsumer<? super K, ? super V>);
+ method public boolean replace(K, V, V);
}
public class HashSet extends java.util.AbstractSet implements java.lang.Cloneable java.io.Serializable java.util.Set {
@@ -58058,6 +58060,9 @@
ctor public Hashtable(java.util.Map<? extends K, ? extends V>);
method public synchronized void clear();
method public synchronized java.lang.Object clone();
+ method public synchronized V compute(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>);
+ method public synchronized V computeIfAbsent(K, java.util.function.Function<? super K, ? extends V>);
+ method public synchronized V computeIfPresent(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>);
method public synchronized boolean contains(java.lang.Object);
method public synchronized boolean containsKey(java.lang.Object);
method public boolean containsValue(java.lang.Object);
@@ -58065,13 +58070,19 @@
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
method public synchronized void forEach(java.util.function.BiConsumer<? super K, ? super V>);
method public synchronized V get(java.lang.Object);
+ method public synchronized V getOrDefault(java.lang.Object, V);
method public synchronized boolean isEmpty();
method public java.util.Set<K> keySet();
method public synchronized java.util.Enumeration<K> keys();
+ method public synchronized V merge(K, V, java.util.function.BiFunction<? super V, ? super V, ? extends V>);
method public synchronized V put(K, V);
method public synchronized void putAll(java.util.Map<? extends K, ? extends V>);
+ method public synchronized V putIfAbsent(K, V);
method protected void rehash();
method public synchronized V remove(java.lang.Object);
+ method public synchronized boolean remove(java.lang.Object, java.lang.Object);
+ method public synchronized boolean replace(K, V, V);
+ method public synchronized V replace(K, V);
method public synchronized int size();
method public java.util.Collection<V> values();
}
@@ -58216,9 +58227,11 @@
method public abstract boolean remove(java.lang.Object);
method public abstract E remove(int);
method public abstract boolean removeAll(java.util.Collection<?>);
+ method public default void replaceAll(java.util.function.UnaryOperator<E>);
method public abstract boolean retainAll(java.util.Collection<?>);
method public abstract E set(int, E);
method public abstract int size();
+ method public default void sort(java.util.Comparator<? super E>);
method public abstract java.util.List<E> subList(int, int);
method public abstract java.lang.Object[] toArray();
method public abstract T[] toArray(T[]);
@@ -59133,9 +59146,11 @@
method public synchronized boolean removeElement(java.lang.Object);
method public synchronized void removeElementAt(int);
method public synchronized boolean removeIf(java.util.function.Predicate<? super E>);
+ method public synchronized void replaceAll(java.util.function.UnaryOperator<E>);
method public synchronized void setElementAt(E, int);
method public synchronized void setSize(int);
method public synchronized int size();
+ method public synchronized void sort(java.util.Comparator<? super E>);
method public java.util.Spliterator<E> spliterator();
method public synchronized void trimToSize();
field protected int capacityIncrement;
@@ -59601,6 +59616,7 @@
method public java.lang.Object clone();
method public boolean contains(java.lang.Object);
method public boolean containsAll(java.util.Collection<?>);
+ method public void forEach(java.util.function.Consumer<? super E>);
method public E get(int);
method public int indexOf(E, int);
method public int indexOf(java.lang.Object);
@@ -63978,11 +63994,9 @@
public final class PrivateCredentialPermission extends java.security.Permission {
ctor public PrivateCredentialPermission(java.lang.String, java.lang.String);
- method public boolean equals(java.lang.Object);
method public java.lang.String getActions();
method public java.lang.String getCredentialClass();
method public java.lang.String[][] getPrincipals();
- method public int hashCode();
method public boolean implies(java.security.Permission);
}
diff --git a/api/test-removed.txt b/api/test-removed.txt
index 36c8ce5..3f16bca 100644
--- a/api/test-removed.txt
+++ b/api/test-removed.txt
@@ -94,6 +94,10 @@
package android.media.tv {
+ public final class TvInputManager {
+ method public android.media.tv.TvInputManager.Hardware acquireTvInputHardware(int, android.media.tv.TvInputManager.HardwareCallback, android.media.tv.TvInputInfo);
+ }
+
public class TvView extends android.view.ViewGroup {
method public void requestUnblockContent(android.media.tv.TvContentRating);
}
@@ -136,6 +140,15 @@
}
+package android.os.storage {
+
+ public class StorageManager {
+ method public android.os.storage.StorageVolume getPrimaryVolume();
+ method public android.os.storage.StorageVolume[] getVolumeList();
+ }
+
+}
+
package android.preference {
public class PreferenceManager {
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 86734b1..456be02 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -44,6 +44,7 @@
import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.pm.IPackageManager;
+import android.content.pm.InstrumentationInfo;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
@@ -96,6 +97,7 @@
private static final int STACK_BOUNDS_INSET = 10;
private IActivityManager mAm;
+ private IPackageManager mPm;
private int mStartFlags = 0;
private boolean mWaitOption = false;
@@ -224,7 +226,8 @@
" --receiver-permission <PERMISSION>: Require receiver to hold permission.\n" +
"\n" +
"am instrument: start an Instrumentation. Typically this target <COMPONENT>\n" +
- " is the form <TEST_PACKAGE>/<RUNNER_CLASS>. Options are:\n" +
+ " is the form <TEST_PACKAGE>/<RUNNER_CLASS> or only <TEST_PACKAGE> if there \n" +
+ " is only one instrumentation. Options are:\n" +
" -r: print raw results (otherwise decode REPORT_KEY_STREAMRESULT). Use with\n" +
" [-e perf true] to generate raw output for performance measurements.\n" +
" -e <NAME> <VALUE>: set argument <NAME> to <VALUE>. For test runners a\n" +
@@ -373,6 +376,12 @@
throw new AndroidException("Can't connect to activity manager; is the system running?");
}
+ mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
+ if (mPm == null) {
+ System.err.println(NO_SYSTEM_ERROR_CODE);
+ throw new AndroidException("Can't connect to package manager; is the system running?");
+ }
+
String op = nextArgRequired();
if (op.equals("start")) {
@@ -570,13 +579,7 @@
if (intent.getComponent() != null) {
packageName = intent.getComponent().getPackageName();
} else {
- IPackageManager pm = IPackageManager.Stub.asInterface(
- ServiceManager.getService("package"));
- if (pm == null) {
- System.err.println("Error: Package manager not running; aborting");
- return;
- }
- List<ResolveInfo> activities = pm.queryIntentActivities(intent, mimeType, 0,
+ List<ResolveInfo> activities = mPm.queryIntentActivities(intent, mimeType, 0,
mUserId).getList();
if (activities == null || activities.size() <= 0) {
System.err.println("Error: Intent does not match any activities: "
@@ -813,8 +816,44 @@
}
String cnArg = nextArgRequired();
- ComponentName cn = ComponentName.unflattenFromString(cnArg);
- if (cn == null) throw new IllegalArgumentException("Bad component name: " + cnArg);
+
+ ComponentName cn;
+ if (cnArg.contains("/")) {
+ cn = ComponentName.unflattenFromString(cnArg);
+ if (cn == null) throw new IllegalArgumentException("Bad component name: " + cnArg);
+ } else {
+ List<InstrumentationInfo> infos = mPm.queryInstrumentation(null, 0).getList();
+
+ final int numInfos = infos == null ? 0: infos.size();
+ List<ComponentName> cns = new ArrayList<>();
+ for (int i = 0; i < numInfos; i++) {
+ InstrumentationInfo info = infos.get(i);
+
+ ComponentName c = new ComponentName(info.packageName, info.name);
+ if (cnArg.equals(info.packageName)) {
+ cns.add(c);
+ }
+ }
+
+ if (cns.size() == 0) {
+ throw new IllegalArgumentException("No instrumentation found for: " + cnArg);
+ } else if (cns.size() == 1) {
+ cn = cns.get(0);
+ } else {
+ StringBuilder cnsStr = new StringBuilder();
+ final int numCns = cns.size();
+ for (int i = 0; i < numCns; i++) {
+ cnsStr.append(cns.get(i).flattenToString());
+ cnsStr.append(", ");
+ }
+
+ // Remove last ", "
+ cnsStr.setLength(cnsStr.length() - 2);
+
+ throw new IllegalArgumentException("Found multiple instrumentations: "
+ + cnsStr.toString());
+ }
+ }
InstrumentationWatcher watcher = null;
UiAutomationConnection connection = null;
@@ -1141,7 +1180,7 @@
int userId = Integer.parseInt(nextArgRequired());
byte[] token = argToBytes(nextArgRequired());
byte[] secret = argToBytes(nextArgRequired());
- boolean success = mAm.unlockUser(userId, token, secret);
+ boolean success = mAm.unlockUser(userId, token, secret, null);
if (success) {
System.out.println("Success: user unlocked");
} else {
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index ea53e59..c597ed2 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -68,14 +68,11 @@
// ---------------------------------------------------------------------------
-BootAnimation::BootAnimation() : Thread(false), mZip(NULL), mClockEnabled(true) {
+BootAnimation::BootAnimation() : Thread(false), mClockEnabled(true) {
mSession = new SurfaceComposerClient();
}
BootAnimation::~BootAnimation() {
- if (mZip != NULL) {
- delete mZip;
- }
}
void BootAnimation::onFirstRef() {
@@ -288,19 +285,15 @@
bool encryptedAnimation = atoi(decrypt) != 0 || !strcmp("trigger_restart_min_framework", decrypt);
- ZipFileRO* zipFile = NULL;
- if ((encryptedAnimation &&
- (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0) &&
- ((zipFile = ZipFileRO::open(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE)) != NULL)) ||
-
- ((access(OEM_BOOTANIMATION_FILE, R_OK) == 0) &&
- ((zipFile = ZipFileRO::open(OEM_BOOTANIMATION_FILE)) != NULL)) ||
-
- ((access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) &&
- ((zipFile = ZipFileRO::open(SYSTEM_BOOTANIMATION_FILE)) != NULL))) {
- mZip = zipFile;
+ if (encryptedAnimation && (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0)) {
+ mZipFileName = SYSTEM_ENCRYPTED_BOOTANIMATION_FILE;
}
-
+ else if (access(OEM_BOOTANIMATION_FILE, R_OK) == 0) {
+ mZipFileName = OEM_BOOTANIMATION_FILE;
+ }
+ else if (access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) {
+ mZipFileName = SYSTEM_BOOTANIMATION_FILE;
+ }
return NO_ERROR;
}
@@ -309,7 +302,7 @@
bool r;
// We have no bootanimation file, so we use the stock android logo
// animation.
- if (mZip == NULL) {
+ if (mZipFileName.isEmpty()) {
r = android();
} else {
r = movie();
@@ -429,16 +422,17 @@
return true;
}
-bool BootAnimation::readFile(const char* name, String8& outString)
+
+static bool readFile(ZipFileRO* zip, const char* name, String8& outString)
{
- ZipEntryRO entry = mZip->findEntryByName(name);
+ ZipEntryRO entry = zip->findEntryByName(name);
ALOGE_IF(!entry, "couldn't find %s", name);
if (!entry) {
return false;
}
- FileMap* entryMap = mZip->createEntryFileMap(entry);
- mZip->releaseEntry(entry);
+ FileMap* entryMap = zip->createEntryFileMap(entry);
+ zip->releaseEntry(entry);
ALOGE_IF(!entryMap, "entryMap is null");
if (!entryMap) {
return false;
@@ -512,18 +506,18 @@
glBindTexture(GL_TEXTURE_2D, 0);
}
-bool BootAnimation::movie()
+bool BootAnimation::parseAnimationDesc(Animation& animation)
{
String8 desString;
- if (!readFile("desc.txt", desString)) {
+ if (!readFile(animation.zip, "desc.txt", desString)) {
return false;
}
char const* s = desString.string();
// Create and initialize an AudioPlayer if we have an audio_conf.txt file
String8 audioConf;
- if (readFile("audio_conf.txt", audioConf)) {
+ if (readFile(animation.zip, "audio_conf.txt", audioConf)) {
mAudioPlayer = new AudioPlayer;
if (!mAudioPlayer->init(audioConf.string())) {
ALOGE("mAudioPlayer.init failed");
@@ -531,8 +525,6 @@
}
}
- Animation animation;
-
// Parse the description file
for (;;) {
const char* endl = strstr(s, "\n");
@@ -564,6 +556,7 @@
part.path = path;
part.clockPosY = clockPosY;
part.audioFile = NULL;
+ part.animation = NULL;
if (!parseColor(color, part.backgroundColor)) {
ALOGE("> invalid color '#%s'", color);
part.backgroundColor[0] = 0.0f;
@@ -572,13 +565,29 @@
}
animation.parts.add(part);
}
-
+ else if (strcmp(l, "$SYSTEM") == 0) {
+ // ALOGD("> SYSTEM");
+ Animation::Part part;
+ part.playUntilComplete = false;
+ part.count = 1;
+ part.pause = 0;
+ part.audioFile = NULL;
+ part.animation = loadAnimation(String8(SYSTEM_BOOTANIMATION_FILE));
+ if (part.animation != NULL)
+ animation.parts.add(part);
+ }
s = ++endl;
}
+ return true;
+}
+
+bool BootAnimation::preloadZip(Animation& animation)
+{
// read all the data structures
const size_t pcount = animation.parts.size();
void *cookie = NULL;
+ ZipFileRO* mZip = animation.zip;
if (!mZip->startIteration(&cookie)) {
return false;
}
@@ -624,6 +633,16 @@
mZip->endIteration(cookie);
+ return true;
+}
+
+bool BootAnimation::movie()
+{
+
+ Animation* animation = loadAnimation(mZipFileName);
+ if (animation == NULL)
+ return false;
+
// Blend required to draw time on top of animation frames.
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glShadeModel(GL_FLAT);
@@ -645,6 +664,19 @@
mClockEnabled = clockTextureInitialized;
}
+ playAnimation(*animation);
+ releaseAnimation(animation);
+
+ if (clockTextureInitialized) {
+ glDeleteTextures(1, &mClock.name);
+ }
+
+ return false;
+}
+
+bool BootAnimation::playAnimation(const Animation& animation)
+{
+ const size_t pcount = animation.parts.size();
const int xc = (mWidth - animation.width) / 2;
const int yc = ((mHeight - animation.height) / 2);
nsecs_t frameDuration = s2ns(1) / animation.fps;
@@ -657,6 +689,14 @@
const size_t fcount = part.frames.size();
glBindTexture(GL_TEXTURE_2D, 0);
+ // Handle animation package
+ if (part.animation != NULL) {
+ playAnimation(*part.animation);
+ if (exitPending())
+ break;
+ continue; //to next part
+ }
+
for (int r=0 ; !part.count || r<part.count ; r++) {
// Exit any non playuntil complete parts immediately
if(exitPending() && !part.playUntilComplete)
@@ -744,14 +784,46 @@
}
}
}
-
- if (clockTextureInitialized) {
- glDeleteTextures(1, &mClock.name);
- }
-
- return false;
+ return true;
}
+void BootAnimation::releaseAnimation(Animation* animation) const
+{
+ for (Vector<Animation::Part>::iterator it = animation->parts.begin(),
+ e = animation->parts.end(); it != e; ++it) {
+ if (it->animation)
+ releaseAnimation(it->animation);
+ }
+ if (animation->zip)
+ delete animation->zip;
+ delete animation;
+}
+
+BootAnimation::Animation* BootAnimation::loadAnimation(const String8& fn)
+{
+ if (mLoadedFiles.indexOf(fn) >= 0) {
+ ALOGE("File \"%s\" is already loaded. Cyclic ref is not allowed",
+ fn.string());
+ return NULL;
+ }
+ ZipFileRO *zip = ZipFileRO::open(fn);
+ if (zip == NULL) {
+ ALOGE("Failed to open animation zip \"%s\": %s",
+ fn.string(), strerror(errno));
+ return NULL;
+ }
+
+ Animation *animation = new Animation;
+ animation->fileName = fn;
+ animation->zip = zip;
+ mLoadedFiles.add(animation->fileName);
+
+ parseAnimationDesc(*animation);
+ preloadZip(*animation);
+
+ mLoadedFiles.remove(fn);
+ return animation;
+}
// ---------------------------------------------------------------------------
}
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index 83e2b38..d49e1ec 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -76,19 +76,27 @@
bool playUntilComplete;
float backgroundColor[3];
FileMap* audioFile;
+ Animation* animation;
};
int fps;
int width;
int height;
Vector<Part> parts;
+ String8 audioConf;
+ String8 fileName;
+ ZipFileRO* zip;
};
status_t initTexture(Texture* texture, AssetManager& asset, const char* name);
status_t initTexture(const Animation::Frame& frame);
bool android();
- bool readFile(const char* name, String8& outString);
bool movie();
void drawTime(const Texture& clockTex, const int yPos);
+ Animation* loadAnimation(const String8&);
+ bool playAnimation(const Animation&);
+ void releaseAnimation(Animation*) const;
+ bool parseAnimationDesc(Animation&);
+ bool preloadZip(Animation &animation);
void checkExit();
@@ -104,8 +112,9 @@
EGLDisplay mSurface;
sp<SurfaceControl> mFlingerSurfaceControl;
sp<Surface> mFlingerSurface;
- ZipFileRO *mZip;
bool mClockEnabled;
+ String8 mZipFileName;
+ SortedVector<String8> mLoadedFiles;
};
// ---------------------------------------------------------------------------
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 4025553..d44a1df 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -930,7 +930,7 @@
// In non-split user mode, userId can only be SYSTEM
int parentUserId = userId >= 0 ? userId : UserHandle.USER_SYSTEM;
info = mUm.createRestrictedProfile(name, parentUserId);
- mAm.addSharedAccountsFromParentUser(userId, parentUserId);
+ mAm.addSharedAccountsFromParentUser(parentUserId, userId);
} else if (userId < 0) {
info = mUm.createUser(name, flags);
} else {
diff --git a/cmds/sm/src/com/android/commands/sm/Sm.java b/cmds/sm/src/com/android/commands/sm/Sm.java
index b208e43..0e674c8 100644
--- a/cmds/sm/src/com/android/commands/sm/Sm.java
+++ b/cmds/sm/src/com/android/commands/sm/Sm.java
@@ -88,6 +88,8 @@
runForget();
} else if ("set-emulate-fbe".equals(op)) {
runSetEmulateFbe();
+ } else if ("get-fbe-mode".equals(op)) {
+ runGetFbeMode();
} else {
throw new IllegalArgumentException();
}
@@ -145,6 +147,16 @@
StorageManager.DEBUG_EMULATE_FBE);
}
+ public void runGetFbeMode() {
+ if (StorageManager.isFileEncryptedNativeOnly()) {
+ System.out.println("native");
+ } else if (StorageManager.isFileEncryptedEmulatedOnly()) {
+ System.out.println("emulated");
+ } else {
+ System.out.println("none");
+ }
+ }
+
public void runPartition() throws RemoteException {
final String diskId = nextArg();
final String type = nextArg();
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 4019a56..ee03280 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -36,11 +36,11 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
+import com.android.internal.R;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import com.android.internal.R;
-
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
@@ -319,6 +319,9 @@
*/
public static final int FLAG_RETRIEVE_INTERACTIVE_WINDOWS = 0x00000040;
+ /** {@hide} */
+ public static final int FLAG_FORCE_DIRECT_BOOT_AWARE = 0x00010000;
+
/**
* The event types an {@link AccessibilityService} is interested in.
* <p>
@@ -687,8 +690,9 @@
}
/** {@hide} */
- public boolean isEncryptionAware() {
- return mResolveInfo.serviceInfo.directBootAware;
+ public boolean isDirectBootAware() {
+ return ((flags & FLAG_FORCE_DIRECT_BOOT_AWARE) != 0)
+ || mResolveInfo.serviceInfo.directBootAware;
}
/**
diff --git a/core/java/android/accessibilityservice/GestureDescription.java b/core/java/android/accessibilityservice/GestureDescription.java
index 7a0c89b..e18a34d 100644
--- a/core/java/android/accessibilityservice/GestureDescription.java
+++ b/core/java/android/accessibilityservice/GestureDescription.java
@@ -36,8 +36,7 @@
* Accessibility services with the
* {@link android.R.styleable#AccessibilityService_canPerformGestures} property can dispatch
* gestures. This class describes those gestures. Gestures are made up of one or more strokes.
- * Gestures are immutable; use the {@code create} methods to get common gesture, or a
- * {@code Builder} to create a new one.
+ * Gestures are immutable once built.
* <p>
* Spatial dimensions throughout are in screen pixels. Time is measured in milliseconds.
*/
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index e520b40..7465ed9 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -2798,6 +2798,15 @@
if (account == null) {
throw new IllegalArgumentException("account is null");
}
+
+ // Always include the calling package name. This just makes life easier
+ // down stream.
+ final Bundle optionsIn = new Bundle();
+ if (options != null) {
+ optionsIn.putAll(options);
+ }
+ optionsIn.putString(KEY_ANDROID_PACKAGE_NAME, mContext.getPackageName());
+
return new AmsTask(activity, handler, callback) {
@Override
public void doWork() throws RemoteException {
@@ -2806,7 +2815,7 @@
account,
authTokenType,
activity != null,
- options);
+ optionsIn);
}
}.start();
}
diff --git a/core/java/android/animation/KeyframeSet.java b/core/java/android/animation/KeyframeSet.java
index 32edd4d..77df151 100644
--- a/core/java/android/animation/KeyframeSet.java
+++ b/core/java/android/animation/KeyframeSet.java
@@ -29,8 +29,9 @@
* This class holds a collection of Keyframe objects and is called by ValueAnimator to calculate
* values between those keyframes for a given animation. The class internal to the animation
* package because it is an implementation detail of how Keyframes are stored and used.
+ * @hide
*/
-class KeyframeSet implements Keyframes {
+public class KeyframeSet implements Keyframes {
int mNumKeyframes;
diff --git a/core/java/android/animation/Keyframes.java b/core/java/android/animation/Keyframes.java
index c149bed..e40a86c 100644
--- a/core/java/android/animation/Keyframes.java
+++ b/core/java/android/animation/Keyframes.java
@@ -20,8 +20,9 @@
/**
* This interface abstracts a collection of Keyframe objects and is called by
* ValueAnimator to calculate values between those keyframes for a given animation.
+ * @hide
*/
-interface Keyframes extends Cloneable {
+public interface Keyframes extends Cloneable {
/**
* Sets the TypeEvaluator to be used when calculating animated values. This object
diff --git a/core/java/android/animation/PathKeyframes.java b/core/java/android/animation/PathKeyframes.java
index 8230ac5..50a490e 100644
--- a/core/java/android/animation/PathKeyframes.java
+++ b/core/java/android/animation/PathKeyframes.java
@@ -34,8 +34,9 @@
* Typically, the returned type is a PointF, but the individual components can be extracted
* as either an IntKeyframes or FloatKeyframes.
* </p>
+ * @hide
*/
-class PathKeyframes implements Keyframes {
+public class PathKeyframes implements Keyframes {
private static final int FRACTION_OFFSET = 0;
private static final int X_OFFSET = 1;
private static final int Y_OFFSET = 2;
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 663f297..c6a5152 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -967,7 +967,7 @@
AnimationHandler animationHandler = AnimationHandler.getInstance();
animationHandler.addAnimationFrameCallback(this, (long) (mStartDelay * sDurationScale));
- if (mStartDelay == 0) {
+ if (mStartDelay == 0 || mSeekFraction >= 0) {
// If there's no start delay, init the animation and notify start listeners right away
// to be consistent with the previous behavior. Otherwise, postpone this until the first
// frame after the start delay.
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index ee17e8a..0410a6e 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -51,12 +51,7 @@
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.InsetDrawable;
-import android.graphics.drawable.LayerDrawable;
-import android.graphics.drawable.ShapeDrawable;
import android.hardware.input.InputManager;
import android.media.AudioManager;
import android.media.session.MediaController;
@@ -121,7 +116,6 @@
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.app.ToolbarActionBar;
import com.android.internal.app.WindowDecorActionBar;
-import com.android.internal.policy.DecorView;
import com.android.internal.policy.PhoneWindow;
import java.io.FileDescriptor;
@@ -1249,7 +1243,7 @@
protected void onResume() {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onResume " + this);
getApplication().dispatchActivityResumed(this);
- mActivityTransitionState.onResume();
+ mActivityTransitionState.onResume(this, isTopOfTask());
mCalled = true;
}
@@ -1855,15 +1849,15 @@
* visa-versa.
* @see android.R.attr#resizeableActivity
*
- * @param inMultiWindow True if the activity is in multi-window mode.
+ * @param isInMultiWindowMode True if the activity is in multi-window mode.
*/
@CallSuper
- public void onMultiWindowChanged(boolean inMultiWindow) {
+ public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {
if (DEBUG_LIFECYCLE) Slog.v(TAG,
- "onMultiWindowChanged " + this + ": " + inMultiWindow);
- mFragments.dispatchMultiWindowChanged(inMultiWindow);
+ "onMultiWindowModeChanged " + this + ": " + isInMultiWindowMode);
+ mFragments.dispatchMultiWindowModeChanged(isInMultiWindowMode);
if (mWindow != null) {
- mWindow.onMultiWindowChanged();
+ mWindow.onMultiWindowModeChanged();
}
}
@@ -1873,9 +1867,9 @@
*
* @return True if the activity is in multi-window mode.
*/
- public boolean inMultiWindow() {
+ public boolean isInMultiWindowMode() {
try {
- return ActivityManagerNative.getDefault().inMultiWindow(mToken);
+ return ActivityManagerNative.getDefault().isInMultiWindowMode(mToken);
} catch (RemoteException e) {
}
return false;
@@ -1885,13 +1879,13 @@
* Called by the system when the activity changes to and from picture-in-picture mode.
* @see android.R.attr#supportsPictureInPicture
*
- * @param inPictureInPicture True if the activity is in picture-in-picture mode.
+ * @param isInPictureInPictureMode True if the activity is in picture-in-picture mode.
*/
@CallSuper
- public void onPictureInPictureChanged(boolean inPictureInPicture) {
+ public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
if (DEBUG_LIFECYCLE) Slog.v(TAG,
- "onPictureInPictureChanged " + this + ": " + inPictureInPicture);
- mFragments.dispatchPictureInPictureChanged(inPictureInPicture);
+ "onPictureInPictureModeChanged " + this + ": " + isInPictureInPictureMode);
+ mFragments.dispatchPictureInPictureModeChanged(isInPictureInPictureMode);
}
/**
@@ -1900,9 +1894,9 @@
*
* @return True if the activity is in picture-in-picture mode.
*/
- public boolean inPictureInPicture() {
+ public boolean isInPictureInPictureMode() {
try {
- return ActivityManagerNative.getDefault().inPictureInPicture(mToken);
+ return ActivityManagerNative.getDefault().isInPictureInPictureMode(mToken);
} catch (RemoteException e) {
}
return false;
@@ -1912,9 +1906,9 @@
* Puts the activity in picture-in-picture mode.
* @see android.R.attr#supportsPictureInPicture
*/
- public void enterPictureInPicture() {
+ public void enterPictureInPictureMode() {
try {
- ActivityManagerNative.getDefault().enterPictureInPicture(mToken);
+ ActivityManagerNative.getDefault().enterPictureInPictureMode(mToken);
} catch (RemoteException e) {
}
}
@@ -5920,6 +5914,9 @@
* @return true if this is the topmost, non-finishing activity in its task.
*/
private boolean isTopOfTask() {
+ if (mToken == null || mWindow == null || !mWindowAdded) {
+ return false;
+ }
try {
return ActivityManagerNative.getDefault().isTopOfTask(mToken);
} catch (RemoteException e) {
@@ -6912,14 +6909,25 @@
}
/**
+ * Check whether the caption on freeform windows is displayed directly on the content.
+ *
+ * @return True if caption is displayed on content, false if it pushes the content down.
+ *
+ * @see {@link #setOverlayWithDecorCaptionEnabled(boolean)}
+ */
+ public boolean isOverlayWithDecorCaptionEnabled() {
+ return mWindow.isOverlayWithDecorCaptionEnabled();
+ }
+
+ /**
* Set whether the caption should displayed directly on the content rather than push it down.
*
* This affects only freeform windows since they display the caption and only the main
* window of the activity. The caption is used to drag the window around and also shows
* maximize and close action buttons.
*/
- public void overlayWithDecorCaption(boolean overlay) {
- mWindow.setOverlayDecorCaption(overlay);
+ public void setOverlayWithDecorCaptionEnabled(boolean enabled) {
+ mWindow.setOverlayWithDecorCaptionEnabled(enabled);
}
/**
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 2d33a2c..d1f5143 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -22,6 +22,7 @@
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
+import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Point;
@@ -30,7 +31,7 @@
import android.os.ParcelFileDescriptor;
import android.util.Log;
-import com.android.internal.app.ProcessStats;
+import com.android.internal.app.procstats.ProcessStats;
import com.android.internal.os.TransferPipe;
import com.android.internal.util.FastPrintWriter;
@@ -376,6 +377,12 @@
/** @hide Process is being cached for later use and is empty. */
public static final int PROCESS_STATE_CACHED_EMPTY = 16;
+ /** @hide The lowest process state number */
+ public static final int MIN_PROCESS_STATE = PROCESS_STATE_NONEXISTENT;
+
+ /** @hide The highest process state number */
+ public static final int MAX_PROCESS_STATE = PROCESS_STATE_CACHED_EMPTY;
+
/** @hide Should this process state be considered a background state? */
public static final boolean isProcStateBackground(int procState) {
return procState >= PROCESS_STATE_BACKUP;
@@ -1409,10 +1416,10 @@
public static final int RECENT_IGNORE_HOME_STACK_TASKS = 0x0008;
/**
- * Ignores all tasks that are on the docked stack.
+ * Ignores the top task in the docked stack.
* @hide
*/
- public static final int RECENT_INGORE_DOCKED_STACK_TASKS = 0x0010;
+ public static final int RECENT_INGORE_DOCKED_STACK_TOP_TASK = 0x0010;
/**
* Ignores all tasks that are on the pinned stack.
@@ -1790,7 +1797,7 @@
public int taskWidth;
public int taskHeight;
- public int screenOrientation;
+ public int screenOrientation = Configuration.ORIENTATION_UNDEFINED;
public TaskThumbnailInfo() {
// Do nothing
@@ -1807,7 +1814,16 @@
public void reset() {
taskWidth = 0;
taskHeight = 0;
- screenOrientation = 0;
+ screenOrientation = Configuration.ORIENTATION_UNDEFINED;
+ }
+
+ /**
+ * Copies from another ThumbnailInfo.
+ */
+ public void copyFrom(TaskThumbnailInfo o) {
+ taskWidth = o.taskWidth;
+ taskHeight = o.taskHeight;
+ screenOrientation = o.screenOrientation;
}
/** @hide */
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 7310d67..5116634 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -114,4 +114,15 @@
* values.
*/
public abstract void notifyAppTransitionStarting(int reason);
+
+ /**
+ * Callback for window manager to let activity manager know that the app transition was
+ * cancelled.
+ */
+ public abstract void notifyAppTransitionCancelled();
+
+ /**
+ * Callback for window manager to let activity manager know that the app transition is finished.
+ */
+ public abstract void notifyAppTransitionFinished();
}
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index f5d7e7e..65d48e6 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -41,6 +41,7 @@
import android.os.Bundle;
import android.os.Debug;
import android.os.IBinder;
+import android.os.IProgressListener;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
@@ -94,7 +95,7 @@
}
return sSystemReady;
}
- static boolean sSystemReady = false;
+ static volatile boolean sSystemReady = false;
static public void broadcastStickyIntent(Intent intent, String permission, int userId) {
broadcastStickyIntent(intent, permission, AppOpsManager.OP_NONE, userId);
@@ -2122,7 +2123,9 @@
int userId = data.readInt();
byte[] token = data.createByteArray();
byte[] secret = data.createByteArray();
- boolean result = unlockUser(userId, token, secret);
+ IProgressListener listener = IProgressListener.Stub
+ .asInterface(data.readStrongBinder());
+ boolean result = unlockUser(userId, token, secret, listener);
reply.writeNoException();
reply.writeInt(result ? 1 : 0);
return true;
@@ -2887,7 +2890,7 @@
case IN_MULTI_WINDOW_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
final IBinder token = data.readStrongBinder();
- final boolean inMultiWindow = inMultiWindow(token);
+ final boolean inMultiWindow = isInMultiWindowMode(token);
reply.writeNoException();
reply.writeInt(inMultiWindow ? 1 : 0);
return true;
@@ -2895,7 +2898,7 @@
case IN_PICTURE_IN_PICTURE_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
final IBinder token = data.readStrongBinder();
- final boolean inPip = inPictureInPicture(token);
+ final boolean inPip = isInPictureInPictureMode(token);
reply.writeNoException();
reply.writeInt(inPip ? 1 : 0);
return true;
@@ -2903,7 +2906,7 @@
case ENTER_PICTURE_IN_PICTURE_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
final IBinder token = data.readStrongBinder();
- enterPictureInPicture(token);
+ enterPictureInPictureMode(token);
reply.writeNoException();
return true;
}
@@ -5707,13 +5710,15 @@
return result;
}
- public boolean unlockUser(int userId, byte[] token, byte[] secret) throws RemoteException {
+ public boolean unlockUser(int userId, byte[] token, byte[] secret, IProgressListener listener)
+ throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeInt(userId);
data.writeByteArray(token);
data.writeByteArray(secret);
+ data.writeStrongInterface(listener);
mRemote.transact(IActivityManager.UNLOCK_USER_TRANSACTION, data, reply, 0);
reply.readException();
boolean result = reply.readInt() != 0;
@@ -6832,7 +6837,7 @@
}
@Override
- public boolean inMultiWindow(IBinder token) throws RemoteException {
+ public boolean isInMultiWindowMode(IBinder token) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
@@ -6846,7 +6851,7 @@
}
@Override
- public boolean inPictureInPicture(IBinder token) throws RemoteException {
+ public boolean isInPictureInPictureMode(IBinder token) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
@@ -6860,7 +6865,7 @@
}
@Override
- public void enterPictureInPicture(IBinder token) throws RemoteException {
+ public void enterPictureInPictureMode(IBinder token) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 167e6cb..2846798 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -154,6 +154,12 @@
private static final String KEY_LAUNCH_STACK_ID = "android.activity.launchStackId";
/**
+ * The task id the activity should be launched into.
+ * @hide
+ */
+ private static final String KEY_LAUNCH_TASK_ID = "android.activity.launchTaskId";
+
+ /**
* Where the docked stack should be positioned.
* @hide
*/
@@ -224,6 +230,7 @@
private int mExitCoordinatorIndex;
private PendingIntent mUsageTimeReport;
private int mLaunchStackId = INVALID_STACK_ID;
+ private int mLaunchTaskId = -1;
private int mDockCreateMode = DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
private AppTransitionAnimationSpec mAnimSpecs[];
@@ -766,6 +773,7 @@
break;
}
mLaunchStackId = opts.getInt(KEY_LAUNCH_STACK_ID, INVALID_STACK_ID);
+ mLaunchTaskId = opts.getInt(KEY_LAUNCH_TASK_ID, -1);
mDockCreateMode = opts.getInt(KEY_DOCK_CREATE_MODE, DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT);
if (opts.containsKey(KEY_ANIM_SPECS)) {
Parcelable[] specs = opts.getParcelableArray(KEY_ANIM_SPECS);
@@ -927,6 +935,21 @@
mLaunchStackId = launchStackId;
}
+ /**
+ * Sets the task the activity will be launched in.
+ * @hide
+ */
+ public void setLaunchTaskId(int taskId) {
+ mLaunchTaskId = taskId;
+ }
+
+ /**
+ * @hide
+ */
+ public int getLaunchTaskId() {
+ return mLaunchTaskId;
+ }
+
/** @hide */
public int getDockCreateMode() {
return mDockCreateMode;
@@ -1079,6 +1102,7 @@
break;
}
b.putInt(KEY_LAUNCH_STACK_ID, mLaunchStackId);
+ b.putInt(KEY_LAUNCH_TASK_ID, mLaunchTaskId);
b.putInt(KEY_DOCK_CREATE_MODE, mDockCreateMode);
if (mAnimSpecs != null) {
b.putParcelableArray(KEY_ANIM_SPECS, mAnimSpecs);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index dbc6c84..14c4fc6 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -91,6 +91,7 @@
import android.util.Slog;
import android.util.SparseIntArray;
import android.util.SuperNotCalledException;
+import android.view.ContextThemeWrapper;
import android.view.Display;
import android.view.ThreadedRenderer;
import android.view.View;
@@ -1284,15 +1285,15 @@
}
@Override
- public void scheduleMultiWindowChanged(IBinder token, boolean inMultiWindow)
+ public void scheduleMultiWindowModeChanged(IBinder token, boolean isInMultiWindowMode)
throws RemoteException {
- sendMessage(H.MULTI_WINDOW_CHANGED, token, inMultiWindow ? 1 : 0);
+ sendMessage(H.MULTI_WINDOW_MODE_CHANGED, token, isInMultiWindowMode ? 1 : 0);
}
@Override
- public void schedulePictureInPictureChanged(IBinder token, boolean inPip)
+ public void schedulePictureInPictureModeChanged(IBinder token, boolean isInPipMode)
throws RemoteException {
- sendMessage(H.PICTURE_IN_PICTURE_CHANGED, token, inPip ? 1 : 0);
+ sendMessage(H.PICTURE_IN_PICTURE_MODE_CHANGED, token, isInPipMode ? 1 : 0);
}
@Override
@@ -1364,8 +1365,8 @@
public static final int ENTER_ANIMATION_COMPLETE = 149;
public static final int START_BINDER_TRACKING = 150;
public static final int STOP_BINDER_TRACKING_AND_DUMP = 151;
- public static final int MULTI_WINDOW_CHANGED = 152;
- public static final int PICTURE_IN_PICTURE_CHANGED = 153;
+ public static final int MULTI_WINDOW_MODE_CHANGED = 152;
+ public static final int PICTURE_IN_PICTURE_MODE_CHANGED = 153;
public static final int LOCAL_VOICE_INTERACTION_STARTED = 154;
String codeToString(int code) {
@@ -1420,8 +1421,8 @@
case CANCEL_VISIBLE_BEHIND: return "CANCEL_VISIBLE_BEHIND";
case BACKGROUND_VISIBLE_BEHIND_CHANGED: return "BACKGROUND_VISIBLE_BEHIND_CHANGED";
case ENTER_ANIMATION_COMPLETE: return "ENTER_ANIMATION_COMPLETE";
- case MULTI_WINDOW_CHANGED: return "MULTI_WINDOW_CHANGED";
- case PICTURE_IN_PICTURE_CHANGED: return "PICTURE_IN_PICTURE_CHANGED";
+ case MULTI_WINDOW_MODE_CHANGED: return "MULTI_WINDOW_MODE_CHANGED";
+ case PICTURE_IN_PICTURE_MODE_CHANGED: return "PICTURE_IN_PICTURE_MODE_CHANGED";
case LOCAL_VOICE_INTERACTION_STARTED: return "LOCAL_VOICE_INTERACTION_STARTED";
}
}
@@ -1666,11 +1667,11 @@
case STOP_BINDER_TRACKING_AND_DUMP:
handleStopBinderTrackingAndDump((ParcelFileDescriptor) msg.obj);
break;
- case MULTI_WINDOW_CHANGED:
- handleMultiWindowChanged((IBinder) msg.obj, msg.arg1 == 1);
+ case MULTI_WINDOW_MODE_CHANGED:
+ handleMultiWindowModeChanged((IBinder) msg.obj, msg.arg1 == 1);
break;
- case PICTURE_IN_PICTURE_CHANGED:
- handlePictureInPictureChanged((IBinder) msg.obj, msg.arg1 == 1);
+ case PICTURE_IN_PICTURE_MODE_CHANGED:
+ handlePictureInPictureModeChanged((IBinder) msg.obj, msg.arg1 == 1);
break;
case LOCAL_VOICE_INTERACTION_STARTED:
handleLocalVoiceInteractionStarted((IBinder) ((SomeArgs) msg.obj).arg1,
@@ -2924,17 +2925,17 @@
}
}
- private void handleMultiWindowChanged(IBinder token, boolean inMultiWindow) {
+ private void handleMultiWindowModeChanged(IBinder token, boolean isInMultiWindowMode) {
final ActivityClientRecord r = mActivities.get(token);
if (r != null) {
- r.activity.onMultiWindowChanged(inMultiWindow);
+ r.activity.onMultiWindowModeChanged(isInMultiWindowMode);
}
}
- private void handlePictureInPictureChanged(IBinder token, boolean inPip) {
+ private void handlePictureInPictureModeChanged(IBinder token, boolean isInPipMode) {
final ActivityClientRecord r = mActivities.get(token);
if (r != null) {
- r.activity.onPictureInPictureChanged(inPip);
+ r.activity.onPictureInPictureModeChanged(isInPipMode);
}
}
@@ -4632,7 +4633,21 @@
}
if (reportToActivity) {
- cb.onConfigurationChanged(newConfig);
+ Configuration configToReport = newConfig;
+
+ if (cb instanceof ContextThemeWrapper) {
+ // ContextThemeWrappers may override the configuration for that context.
+ // We must check and apply any overrides defined.
+ ContextThemeWrapper contextThemeWrapper = (ContextThemeWrapper) cb;
+ final Configuration localOverrideConfig =
+ contextThemeWrapper.getOverrideConfiguration();
+ if (localOverrideConfig != null) {
+ configToReport = new Configuration(newConfig);
+ configToReport.updateFrom(localOverrideConfig);
+ }
+ }
+
+ cb.onConfigurationChanged(configToReport);
}
if (activity != null) {
@@ -5077,26 +5092,6 @@
*/
TimeZone.setDefault(null);
- synchronized (mResourcesManager) {
- /*
- * Initialize the default locales in this process for the reasons we set the time zone.
- *
- * We do this through ResourcesManager, since we need to do locale negotiation.
- */
- mResourcesManager.setDefaultLocalesLocked(data.config.getLocales());
-
- /*
- * Update the system configuration since its preloaded and might not
- * reflect configuration changes. The configuration object passed
- * in AppBindData can be safely assumed to be up to date
- */
- mResourcesManager.applyConfigurationToResourcesLocked(data.config, data.compatInfo);
- mCurDefaultDisplayDpi = data.config.densityDpi;
-
- // This calls mResourcesManager so keep it within the synchronized block.
- applyCompatConfiguration(mCurDefaultDisplayDpi);
- }
-
data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
/**
@@ -5221,6 +5216,26 @@
}
final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
+ synchronized (mResourcesManager) {
+ /*
+ * Initialize the default locales in this process for the reasons we set the time zone.
+ *
+ * We do this through ResourcesManager, since we need to do locale negotiation.
+ */
+ mResourcesManager.setDefaultLocalesLocked(data.config.getLocales());
+
+ /*
+ * Update the system configuration since its preloaded and might not
+ * reflect configuration changes. The configuration object passed
+ * in AppBindData can be safely assumed to be up to date
+ */
+ mResourcesManager.applyConfigurationToResourcesLocked(data.config, data.compatInfo);
+ mCurDefaultDisplayDpi = data.config.densityDpi;
+
+ // This calls mResourcesManager so keep it within the synchronized block.
+ applyCompatConfiguration(mCurDefaultDisplayDpi);
+ }
+
if (!Process.isIsolated() && !"android".equals(appContext.getPackageName())) {
// This cache location probably points at credential-encrypted
// storage which may not be accessible yet; assign it anyway instead
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index 198bfb0a..e589e7c 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -25,6 +25,7 @@
import android.os.ResultReceiver;
import android.transition.Transition;
import android.transition.TransitionSet;
+import android.transition.Visibility;
import android.util.ArrayMap;
import android.view.GhostView;
import android.view.View;
@@ -378,6 +379,7 @@
transition.setEpicenterCallback(mEpicenterCallback);
transition = setTargets(transition, includeTransitioningViews);
}
+ noLayoutSuppressionForVisibilityTransitions(transition);
return transition;
}
@@ -944,6 +946,24 @@
}
}
+ /**
+ * Blocks suppressLayout from Visibility transitions. It is ok to suppress the layout,
+ * but we don't want to force the layout when suppressLayout becomes false. This leads
+ * to visual glitches.
+ */
+ private static void noLayoutSuppressionForVisibilityTransitions(Transition transition) {
+ if (transition instanceof Visibility) {
+ final Visibility visibility = (Visibility) transition;
+ visibility.setSuppressLayout(false);
+ } else if (transition instanceof TransitionSet) {
+ final TransitionSet set = (TransitionSet) transition;
+ final int count = set.getTransitionCount();
+ for (int i = 0; i < count; i++) {
+ noLayoutSuppressionForVisibilityTransitions(set.getTransitionAt(i));
+ }
+ }
+ }
+
private static class FixedEpicenterCallback extends Transition.EpicenterCallback {
private Rect mEpicenter;
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java
index bf0bd79..4a1aff7 100644
--- a/core/java/android/app/ActivityTransitionState.java
+++ b/core/java/android/app/ActivityTransitionState.java
@@ -236,9 +236,24 @@
}
}
- public void onResume() {
- restoreExitedViews();
- restoreReenteringViews();
+ public void onResume(Activity activity, boolean isTopOfTask) {
+ // After orientation change, the onResume can come in before the top Activity has
+ // left, so if the Activity is not top, wait a second for the top Activity to exit.
+ if (isTopOfTask || mEnterTransitionCoordinator == null) {
+ restoreExitedViews();
+ restoreReenteringViews();
+ } else {
+ activity.mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ if (mEnterTransitionCoordinator == null ||
+ mEnterTransitionCoordinator.isWaitingForRemoteExit()) {
+ restoreExitedViews();
+ restoreReenteringViews();
+ }
+ }
+ }, 1000);
+ }
}
public void clear() {
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java
index cb2130c..e4fff9d 100644
--- a/core/java/android/app/AlarmManager.java
+++ b/core/java/android/app/AlarmManager.java
@@ -905,10 +905,12 @@
ListenerWrapper wrapper = null;
synchronized (AlarmManager.class) {
- final WeakReference<ListenerWrapper> wrapperRef;
- wrapperRef = sWrappers.get(listener);
- if (wrapperRef != null) {
- wrapper = wrapperRef.get();
+ if (sWrappers != null) {
+ final WeakReference<ListenerWrapper> wrapperRef;
+ wrapperRef = sWrappers.get(listener);
+ if (wrapperRef != null) {
+ wrapper = wrapperRef.get();
+ }
}
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 5b7dae6..ed590e6 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -272,12 +272,17 @@
}
@Override
+ @SuppressWarnings("unchecked")
public List<PermissionInfo> queryPermissionsByGroup(String group, int flags)
throws NameNotFoundException {
try {
- List<PermissionInfo> pi = mPM.queryPermissionsByGroup(group, flags).getList();
- if (pi != null) {
- return pi;
+ ParceledListSlice<PermissionInfo> parceledList =
+ mPM.queryPermissionsByGroup(group, flags);
+ if (parceledList != null) {
+ List<PermissionInfo> pi = parceledList.getList();
+ if (pi != null) {
+ return pi;
+ }
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -288,7 +293,7 @@
@Override
public PermissionGroupInfo getPermissionGroupInfo(String name,
- int flags) throws NameNotFoundException {
+ int flags) throws NameNotFoundException {
try {
PermissionGroupInfo pgi = mPM.getPermissionGroupInfo(name, flags);
if (pgi != null) {
@@ -302,9 +307,15 @@
}
@Override
+ @SuppressWarnings("unchecked")
public List<PermissionGroupInfo> getAllPermissionGroups(int flags) {
try {
- return mPM.getAllPermissionGroups(flags).getList();
+ ParceledListSlice<PermissionGroupInfo> parceledList =
+ mPM.getAllPermissionGroups(flags);
+ if (parceledList == null) {
+ return Collections.emptyList();
+ }
+ return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -439,9 +450,15 @@
}
@Override
+ @SuppressWarnings("unchecked")
public FeatureInfo[] getSystemAvailableFeatures() {
try {
- final List<FeatureInfo> list = mPM.getSystemAvailableFeatures().getList();
+ ParceledListSlice<FeatureInfo> parceledList =
+ mPM.getSystemAvailableFeatures();
+ if (parceledList == null) {
+ return new FeatureInfo[0];
+ }
+ final List<FeatureInfo> list = parceledList.getList();
final FeatureInfo[] res = new FeatureInfo[list.size()];
for (int i = 0; i < res.length; i++) {
res[i] = list.get(i);
@@ -636,10 +653,15 @@
/** @hide */
@Override
+ @SuppressWarnings("unchecked")
public List<PackageInfo> getInstalledPackagesAsUser(int flags, int userId) {
try {
- ParceledListSlice<PackageInfo> slice = mPM.getInstalledPackages(flags, userId);
- return slice.getList();
+ ParceledListSlice<PackageInfo> parceledList =
+ mPM.getInstalledPackages(flags, userId);
+ if (parceledList == null) {
+ return Collections.emptyList();
+ }
+ return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -651,9 +673,12 @@
String[] permissions, int flags) {
final int userId = mContext.getUserId();
try {
- ParceledListSlice<PackageInfo> slice = mPM.getPackagesHoldingPermissions(
- permissions, flags, userId);
- return slice.getList();
+ ParceledListSlice<PackageInfo> parceledList =
+ mPM.getPackagesHoldingPermissions(permissions, flags, userId);
+ if (parceledList == null) {
+ return Collections.emptyList();
+ }
+ return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -664,8 +689,12 @@
public List<ApplicationInfo> getInstalledApplications(int flags) {
final int userId = mContext.getUserId();
try {
- ParceledListSlice<ApplicationInfo> slice = mPM.getInstalledApplications(flags, userId);
- return slice.getList();
+ ParceledListSlice<ApplicationInfo> parceledList =
+ mPM.getInstalledApplications(flags, userId);
+ if (parceledList == null) {
+ return Collections.emptyList();
+ }
+ return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -770,20 +799,25 @@
/** @hide Same as above but for a specific user */
@Override
+ @SuppressWarnings("unchecked")
public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent,
- int flags, int userId) {
+ int flags, int userId) {
try {
- return mPM.queryIntentActivities(
- intent,
- intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- flags,
- userId).getList();
+ ParceledListSlice<ResolveInfo> parceledList =
+ mPM.queryIntentActivities(intent,
+ intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+ flags, userId);
+ if (parceledList == null) {
+ return Collections.emptyList();
+ }
+ return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
@Override
+ @SuppressWarnings("unchecked")
public List<ResolveInfo> queryIntentActivityOptions(
ComponentName caller, Intent[] specifics, Intent intent,
int flags) {
@@ -807,10 +841,13 @@
}
try {
- return mPM
- .queryIntentActivityOptions(caller, specifics, specificTypes, intent,
- intent.resolveTypeIfNeeded(resolver), flags, mContext.getUserId())
- .getList();
+ ParceledListSlice<ResolveInfo> parceledList =
+ mPM.queryIntentActivityOptions(caller, specifics, specificTypes, intent,
+ intent.resolveTypeIfNeeded(resolver), flags, mContext.getUserId());
+ if (parceledList == null) {
+ return Collections.emptyList();
+ }
+ return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -820,13 +857,17 @@
* @hide
*/
@Override
+ @SuppressWarnings("unchecked")
public List<ResolveInfo> queryBroadcastReceiversAsUser(Intent intent, int flags, int userId) {
try {
- return mPM.queryIntentReceivers(
- intent,
- intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- flags,
- userId).getList();
+ ParceledListSlice<ResolveInfo> parceledList =
+ mPM.queryIntentReceivers(intent,
+ intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+ flags, userId);
+ if (parceledList == null) {
+ return Collections.emptyList();
+ }
+ return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -851,13 +892,17 @@
}
@Override
+ @SuppressWarnings("unchecked")
public List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int flags, int userId) {
try {
- return mPM.queryIntentServices(
- intent,
- intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- flags,
- userId).getList();
+ ParceledListSlice<ResolveInfo> parceledList =
+ mPM.queryIntentServices(intent,
+ intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+ flags, userId);
+ if (parceledList == null) {
+ return Collections.emptyList();
+ }
+ return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -869,12 +914,18 @@
}
@Override
+ @SuppressWarnings("unchecked")
public List<ResolveInfo> queryIntentContentProvidersAsUser(
Intent intent, int flags, int userId) {
try {
- return mPM.queryIntentContentProviders(intent,
- intent.resolveTypeIfNeeded(mContext.getContentResolver()), flags, userId)
- .getList();
+ ParceledListSlice<ResolveInfo> parceledList =
+ mPM.queryIntentContentProviders(intent,
+ intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+ flags, userId);
+ if (parceledList == null) {
+ return Collections.emptyList();
+ }
+ return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -901,12 +952,13 @@
}
@Override
+ @SuppressWarnings("unchecked")
public List<ProviderInfo> queryContentProviders(String processName,
- int uid, int flags) {
+ int uid, int flags) {
try {
- ParceledListSlice<ProviderInfo> slice
- = mPM.queryContentProviders(processName, uid, flags);
- return slice != null ? slice.getList() : null;
+ ParceledListSlice<ProviderInfo> slice =
+ mPM.queryContentProviders(processName, uid, flags);
+ return slice != null ? slice.getList() : Collections.<ProviderInfo>emptyList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -930,10 +982,16 @@
}
@Override
+ @SuppressWarnings("unchecked")
public List<InstrumentationInfo> queryInstrumentation(
String targetPackage, int flags) {
try {
- return mPM.queryInstrumentation(targetPackage, flags).getList();
+ ParceledListSlice<InstrumentationInfo> parceledList =
+ mPM.queryInstrumentation(targetPackage, flags);
+ if (parceledList == null) {
+ return Collections.emptyList();
+ }
+ return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1575,18 +1633,30 @@
}
@Override
+ @SuppressWarnings("unchecked")
public List<IntentFilterVerificationInfo> getIntentFilterVerifications(String packageName) {
try {
- return mPM.getIntentFilterVerifications(packageName).getList();
+ ParceledListSlice<IntentFilterVerificationInfo> parceledList =
+ mPM.getIntentFilterVerifications(packageName);
+ if (parceledList == null) {
+ return Collections.emptyList();
+ }
+ return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
@Override
+ @SuppressWarnings("unchecked")
public List<IntentFilter> getAllIntentFilters(String packageName) {
try {
- return mPM.getAllIntentFilters(packageName).getList();
+ ParceledListSlice<IntentFilter> parceledList =
+ mPM.getAllIntentFilters(packageName);
+ if (parceledList == null) {
+ return Collections.emptyList();
+ }
+ return parceledList.getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index 744ddf7..ea86dd0 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -736,7 +736,7 @@
data.enforceInterface(IApplicationThread.descriptor);
final IBinder b = data.readStrongBinder();
final boolean inMultiWindow = data.readInt() != 0;
- scheduleMultiWindowChanged(b, inMultiWindow);
+ scheduleMultiWindowModeChanged(b, inMultiWindow);
return true;
}
@@ -745,7 +745,7 @@
data.enforceInterface(IApplicationThread.descriptor);
final IBinder b = data.readStrongBinder();
final boolean inPip = data.readInt() != 0;
- schedulePictureInPictureChanged(b, inPip);
+ schedulePictureInPictureModeChanged(b, inPip);
return true;
}
@@ -1498,24 +1498,24 @@
}
@Override
- public final void scheduleMultiWindowChanged(
- IBinder token, boolean inMultiWindow) throws RemoteException {
+ public final void scheduleMultiWindowModeChanged(
+ IBinder token, boolean isInMultiWindowMode) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
data.writeStrongBinder(token);
- data.writeInt(inMultiWindow ? 1 : 0);
+ data.writeInt(isInMultiWindowMode ? 1 : 0);
mRemote.transact(SCHEDULE_MULTI_WINDOW_CHANGED_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
data.recycle();
}
@Override
- public final void schedulePictureInPictureChanged(IBinder token, boolean inPip)
+ public final void schedulePictureInPictureModeChanged(IBinder token, boolean isInPipMode)
throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
data.writeStrongBinder(token);
- data.writeInt(inPip ? 1 : 0);
+ data.writeInt(isInPipMode ? 1 : 0);
mRemote.transact(SCHEDULE_PICTURE_IN_PICTURE_CHANGED_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
data.recycle();
diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java
index 3a51aff..1e2cc26 100644
--- a/core/java/android/app/BackStackRecord.java
+++ b/core/java/android/app/BackStackRecord.java
@@ -146,6 +146,10 @@
op.removed.add(r);
}
}
+ bse.mEnterAnim = op.enterAnim;
+ bse.mExitAnim = op.exitAnim;
+ bse.mPopEnterAnim = op.popEnterAnim;
+ bse.mPopExitAnim = op.popExitAnim;
bse.addOp(op);
num++;
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 63a6829..6bb853a 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -803,7 +803,12 @@
@Override
public void startActivity(Intent intent, Bundle options) {
warnIfCallingFromSystemProcess();
- if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
+
+ // Calling start activity from outside an activity without FLAG_ACTIVITY_NEW_TASK is
+ // generally not allowed, except if the caller specifies the task id the activity should
+ // be launched in.
+ if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0
+ && options != null && ActivityOptions.fromBundle(options).getLaunchTaskId() == -1) {
throw new AndroidRuntimeException(
"Calling startActivity() from outside of an Activity "
+ " context requires the FLAG_ACTIVITY_NEW_TASK flag."
@@ -1988,7 +1993,7 @@
ContextImpl context = new ContextImpl(null, mainThread,
packageInfo, null, null, 0, null, null, Display.INVALID_DISPLAY);
context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(),
- context.mResourcesManager.getDisplayMetricsLocked());
+ context.mResourcesManager.getDisplayMetrics());
return context;
}
@@ -2060,16 +2065,34 @@
|| overrideConfiguration != null
|| (compatInfo != null && compatInfo.applicationScale
!= resources.getCompatibilityInfo().applicationScale)) {
- resources = mResourcesManager.getResources(
- activityToken,
- packageInfo.getResDir(),
- packageInfo.getSplitResDirs(),
- packageInfo.getOverlayDirs(),
- packageInfo.getApplicationInfo().sharedLibraryFiles,
- displayId,
- overrideConfiguration,
- compatInfo,
- packageInfo.getClassLoader());
+
+ if (container != null) {
+ // This is a nested Context, so it can't be a base Activity context.
+ // Just create a regular Resources object associated with the Activity.
+ resources = mResourcesManager.getResources(
+ activityToken,
+ packageInfo.getResDir(),
+ packageInfo.getSplitResDirs(),
+ packageInfo.getOverlayDirs(),
+ packageInfo.getApplicationInfo().sharedLibraryFiles,
+ displayId,
+ overrideConfiguration,
+ compatInfo,
+ packageInfo.getClassLoader());
+ } else {
+ // This is not a nested Context, so it must be the root Activity context.
+ // All other nested Contexts will inherit the configuration set here.
+ resources = mResourcesManager.createBaseActivityResources(
+ activityToken,
+ packageInfo.getResDir(),
+ packageInfo.getSplitResDirs(),
+ packageInfo.getOverlayDirs(),
+ packageInfo.getApplicationInfo().sharedLibraryFiles,
+ displayId,
+ overrideConfiguration,
+ compatInfo,
+ packageInfo.getClassLoader());
+ }
}
}
mResources = resources;
diff --git a/core/java/android/app/DatePickerDialog.java b/core/java/android/app/DatePickerDialog.java
index 83dc506..bd55a06 100644
--- a/core/java/android/app/DatePickerDialog.java
+++ b/core/java/android/app/DatePickerDialog.java
@@ -58,7 +58,7 @@
* @param context the parent context
*/
public DatePickerDialog(@NonNull Context context) {
- this(context, 0);
+ this(context, 0, null, Calendar.getInstance(), -1, -1, -1);
}
/**
@@ -70,24 +70,7 @@
* {@code context}'s default alert dialog theme
*/
public DatePickerDialog(@NonNull Context context, @StyleRes int themeResId) {
- super(context, resolveDialogTheme(context, themeResId));
-
- final Context themeContext = getContext();
- final LayoutInflater inflater = LayoutInflater.from(themeContext);
- final View view = inflater.inflate(R.layout.date_picker_dialog, null);
- setView(view);
-
- setButton(BUTTON_POSITIVE, themeContext.getString(R.string.ok), this);
- setButton(BUTTON_NEGATIVE, themeContext.getString(R.string.cancel), this);
- setButtonPanelLayoutHint(LAYOUT_HINT_SIDE);
-
- final Calendar calendar = Calendar.getInstance();
- final int year = calendar.get(Calendar.YEAR);
- final int monthOfYear = calendar.get(Calendar.MONTH);
- final int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
- mDatePicker = (DatePicker) view.findViewById(R.id.datePicker);
- mDatePicker.init(year, monthOfYear, dayOfMonth, this);
- mDatePicker.setValidationCallback(mValidationCallback);
+ this(context, themeResId, null, Calendar.getInstance(), -1, -1, -1);
}
/**
@@ -104,7 +87,7 @@
*/
public DatePickerDialog(@NonNull Context context, @Nullable OnDateSetListener listener,
int year, int month, int dayOfMonth) {
- this(context, 0, listener, year, month, dayOfMonth);
+ this(context, 0, listener, null, year, month, dayOfMonth);
}
/**
@@ -116,16 +99,40 @@
* {@code context}'s default alert dialog theme
* @param listener the listener to call when the user sets the date
* @param year the initially selected year
- * @param month the initially selected month (0-11 for compatibility with
- * {@link Calendar#MONTH})
+ * @param monthOfYear the initially selected month of the year (0-11 for
+ * compatibility with {@link Calendar#MONTH})
* @param dayOfMonth the initially selected day of month (1-31, depending
* on month)
*/
public DatePickerDialog(@NonNull Context context, @StyleRes int themeResId,
- @Nullable OnDateSetListener listener, int year, int month, int dayOfMonth) {
- this(context, themeResId);
+ @Nullable OnDateSetListener listener, int year, int monthOfYear, int dayOfMonth) {
+ this(context, themeResId, listener, null, year, monthOfYear, dayOfMonth);
+ }
- mDatePicker.updateDate(year, month, dayOfMonth);
+ private DatePickerDialog(@NonNull Context context, @StyleRes int themeResId,
+ @Nullable OnDateSetListener listener, @Nullable Calendar calendar, int year,
+ int monthOfYear, int dayOfMonth) {
+ super(context, resolveDialogTheme(context, themeResId));
+
+ final Context themeContext = getContext();
+ final LayoutInflater inflater = LayoutInflater.from(themeContext);
+ final View view = inflater.inflate(R.layout.date_picker_dialog, null);
+ setView(view);
+
+ setButton(BUTTON_POSITIVE, themeContext.getString(R.string.ok), this);
+ setButton(BUTTON_NEGATIVE, themeContext.getString(R.string.cancel), this);
+ setButtonPanelLayoutHint(LAYOUT_HINT_SIDE);
+
+ if (calendar != null) {
+ year = calendar.get(Calendar.YEAR);
+ monthOfYear = calendar.get(Calendar.MONTH);
+ dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
+ }
+
+ mDatePicker = (DatePicker) view.findViewById(R.id.datePicker);
+ mDatePicker.init(year, monthOfYear, dayOfMonth, this);
+ mDatePicker.setValidationCallback(mValidationCallback);
+
mDateSetListener = listener;
}
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 0bb1097..85a0403 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -16,6 +16,10 @@
package android.app;
+import com.android.internal.R;
+import com.android.internal.app.WindowDecorActionBar;
+import com.android.internal.policy.PhoneWindow;
+
import android.annotation.CallSuper;
import android.annotation.DrawableRes;
import android.annotation.IdRes;
@@ -43,7 +47,6 @@
import android.view.ContextThemeWrapper;
import android.view.Gravity;
import android.view.KeyEvent;
-import android.view.KeyboardShortcutGroup;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
@@ -57,12 +60,7 @@
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
-import com.android.internal.R;
-import com.android.internal.app.WindowDecorActionBar;
-import com.android.internal.policy.PhoneWindow;
-
import java.lang.ref.WeakReference;
-import java.util.List;
/**
* Base class for Dialogs.
@@ -94,11 +92,14 @@
KeyEvent.Callback, OnCreateContextMenuListener, Window.OnWindowDismissedCallback {
private static final String TAG = "Dialog";
private Activity mOwnerActivity;
-
+
+ private final WindowManager mWindowManager;
+
final Context mContext;
- final WindowManager mWindowManager;
- Window mWindow;
+ final Window mWindow;
+
View mDecor;
+
private ActionBar mActionBar;
/**
* This field should be made private, so it is hidden from the SDK.
@@ -123,7 +124,7 @@
private static final int CANCEL = 0x44;
private static final int SHOW = 0x45;
- private Handler mListenersHandler;
+ private final Handler mListenersHandler;
private SearchEvent mSearchEvent;
@@ -131,11 +132,7 @@
private int mActionModeTypeStarting = ActionMode.TYPE_PRIMARY;
- private final Runnable mDismissAction = new Runnable() {
- public void run() {
- dismissDialog();
- }
- };
+ private final Runnable mDismissAction = this::dismissDialog;
/**
* Creates a dialog window that uses the default dialog theme.
@@ -198,14 +195,15 @@
* @hide
*/
@Deprecated
- protected Dialog(@NonNull Context context, boolean cancelable, Message cancelCallback) {
+ protected Dialog(@NonNull Context context, boolean cancelable,
+ @Nullable Message cancelCallback) {
this(context);
mCancelable = cancelable;
mCancelMessage = cancelCallback;
}
protected Dialog(@NonNull Context context, boolean cancelable,
- OnCancelListener cancelListener) {
+ @Nullable OnCancelListener cancelListener) {
this(context);
mCancelable = cancelable;
setOnCancelListener(cancelListener);
@@ -216,8 +214,7 @@
*
* @return Context The Context used by the Dialog.
*/
- @NonNull
- public final Context getContext() {
+ public final @NonNull Context getContext() {
return mContext;
}
@@ -226,7 +223,7 @@
*
* @return The ActionBar attached to the dialog or null if no ActionBar is present.
*/
- public ActionBar getActionBar() {
+ public @Nullable ActionBar getActionBar() {
return mActionBar;
}
@@ -236,7 +233,7 @@
*
* @param activity The Activity that owns this dialog.
*/
- public final void setOwnerActivity(Activity activity) {
+ public final void setOwnerActivity(@NonNull Activity activity) {
mOwnerActivity = activity;
getWindow().setVolumeControlStream(mOwnerActivity.getVolumeControlStream());
@@ -250,7 +247,7 @@
*
* @return The Activity that owns this Dialog.
*/
- public final Activity getOwnerActivity() {
+ public final @Nullable Activity getOwnerActivity() {
return mOwnerActivity;
}
@@ -316,13 +313,10 @@
l = nl;
}
- try {
- mWindowManager.addView(mDecor, l);
- mShowing = true;
-
- sendShowMessage();
- } finally {
- }
+ mWindowManager.addView(mDecor, l);
+ mShowing = true;
+
+ sendShowMessage();
}
/**
@@ -388,7 +382,7 @@
}
}
- // internal method to make sure mcreated is set properly without requiring
+ // internal method to make sure mCreated is set properly without requiring
// users to call through to super in onCreate
void dispatchOnCreate(Bundle savedInstanceState) {
if (!mCreated) {
@@ -400,7 +394,7 @@
/**
* Similar to {@link Activity#onCreate}, you should initialize your dialog
* in this method, including calling {@link #setContentView}.
- * @param savedInstanceState If this dialog is being reinitalized after a
+ * @param savedInstanceState If this dialog is being reinitialized after a
* the hosting activity was previously shut down, holds the result from
* the most recent call to {@link #onSaveInstanceState}, or null if this
* is the first time.
@@ -433,7 +427,7 @@
* state.
* @return A bundle with the state of the dialog.
*/
- public Bundle onSaveInstanceState() {
+ public @NonNull Bundle onSaveInstanceState() {
Bundle bundle = new Bundle();
bundle.putBoolean(DIALOG_SHOWING_TAG, mShowing);
if (mCreated) {
@@ -452,7 +446,7 @@
* @param savedInstanceState The state of the dialog previously saved by
* {@link #onSaveInstanceState()}.
*/
- public void onRestoreInstanceState(Bundle savedInstanceState) {
+ public void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
final Bundle dialogHierarchyState = savedInstanceState.getBundle(DIALOG_HIERARCHY_TAG);
if (dialogHierarchyState == null) {
// dialog has never been shown, or onCreated, nothing to restore.
@@ -473,7 +467,7 @@
* @return Window The current window, or null if the activity is not
* visual.
*/
- public Window getWindow() {
+ public @Nullable Window getWindow() {
return mWindow;
}
@@ -486,7 +480,7 @@
* @see #getWindow
* @see android.view.Window#getCurrentFocus
*/
- public View getCurrentFocus() {
+ public @Nullable View getCurrentFocus() {
return mWindow != null ? mWindow.getCurrentFocus() : null;
}
@@ -498,8 +492,7 @@
* @param id the identifier of the view to find
* @return The view with the given id or null.
*/
- @Nullable
- public View findViewById(@IdRes int id) {
+ public @Nullable View findViewById(@IdRes int id) {
return mWindow.findViewById(id);
}
@@ -520,19 +513,19 @@
*
* @param view The desired content to display.
*/
- public void setContentView(View view) {
+ public void setContentView(@NonNull View view) {
mWindow.setContentView(view);
}
/**
* Set the screen content to an explicit view. This view is placed
* directly into the screen's view hierarchy. It can itself be a complex
- * view hierarhcy.
+ * view hierarchy.
*
* @param view The desired content to display.
* @param params Layout parameters for the view.
*/
- public void setContentView(View view, ViewGroup.LayoutParams params) {
+ public void setContentView(@NonNull View view, @Nullable ViewGroup.LayoutParams params) {
mWindow.setContentView(view, params);
}
@@ -543,7 +536,7 @@
* @param view The desired content to display.
* @param params Layout parameters for the view.
*/
- public void addContentView(View view, ViewGroup.LayoutParams params) {
+ public void addContentView(@NonNull View view, @Nullable ViewGroup.LayoutParams params) {
mWindow.addContentView(view, params);
}
@@ -552,7 +545,7 @@
*
* @param title The new text to display in the title.
*/
- public void setTitle(CharSequence title) {
+ public void setTitle(@Nullable CharSequence title) {
mWindow.setTitle(title);
mWindow.getAttributes().setTitle(title);
}
@@ -578,7 +571,8 @@
* @see #onKeyUp
* @see android.view.KeyEvent
*/
- public boolean onKeyDown(int keyCode, KeyEvent event) {
+ @Override
+ public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
event.startTracking();
return true;
@@ -592,7 +586,8 @@
* KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle
* the event).
*/
- public boolean onKeyLongPress(int keyCode, KeyEvent event) {
+ @Override
+ public boolean onKeyLongPress(int keyCode, @NonNull KeyEvent event) {
return false;
}
@@ -605,7 +600,8 @@
* @see #onKeyDown
* @see KeyEvent
*/
- public boolean onKeyUp(int keyCode, KeyEvent event) {
+ @Override
+ public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && event.isTracking()
&& !event.isCanceled()) {
onBackPressed();
@@ -619,7 +615,8 @@
* KeyEvent.Callback.onKeyMultiple()}: always returns false (doesn't handle
* the event).
*/
- public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
+ @Override
+ public boolean onKeyMultiple(int keyCode, int repeatCount, @NonNull KeyEvent event) {
return false;
}
@@ -644,7 +641,7 @@
* @param event Description of the key event.
* @return True if the key shortcut was handled.
*/
- public boolean onKeyShortcut(int keyCode, KeyEvent event) {
+ public boolean onKeyShortcut(int keyCode, @NonNull KeyEvent event) {
return false;
}
@@ -658,7 +655,7 @@
* The default implementation will cancel the dialog when a touch
* happens outside of the window bounds.
*/
- public boolean onTouchEvent(MotionEvent event) {
+ public boolean onTouchEvent(@NonNull MotionEvent event) {
if (mCancelable && mShowing && mWindow.shouldCloseOnTouch(mContext, event)) {
cancel();
return true;
@@ -681,7 +678,7 @@
* @return Return true if you have consumed the event, false if you haven't.
* The default implementation always returns false.
*/
- public boolean onTrackballEvent(MotionEvent event) {
+ public boolean onTrackballEvent(@NonNull MotionEvent event) {
return false;
}
@@ -710,25 +707,30 @@
* @return Return true if you have consumed the event, false if you haven't.
* The default implementation always returns false.
*/
- public boolean onGenericMotionEvent(MotionEvent event) {
+ public boolean onGenericMotionEvent(@NonNull MotionEvent event) {
return false;
}
+ @Override
public void onWindowAttributesChanged(WindowManager.LayoutParams params) {
if (mDecor != null) {
mWindowManager.updateViewLayout(mDecor, params);
}
}
+ @Override
public void onContentChanged() {
}
+ @Override
public void onWindowFocusChanged(boolean hasFocus) {
}
+ @Override
public void onAttachedToWindow() {
}
+ @Override
public void onDetachedFromWindow() {
}
@@ -747,7 +749,8 @@
*
* @return boolean Return true if this event was consumed.
*/
- public boolean dispatchKeyEvent(KeyEvent event) {
+ @Override
+ public boolean dispatchKeyEvent(@NonNull KeyEvent event) {
if ((mOnKeyListener != null) && (mOnKeyListener.onKey(this, event.getKeyCode(), event))) {
return true;
}
@@ -767,7 +770,8 @@
* @param event The key shortcut event.
* @return True if this event was consumed.
*/
- public boolean dispatchKeyShortcutEvent(KeyEvent event) {
+ @Override
+ public boolean dispatchKeyShortcutEvent(@NonNull KeyEvent event) {
if (mWindow.superDispatchKeyShortcutEvent(event)) {
return true;
}
@@ -784,7 +788,8 @@
*
* @return boolean Return true if this event was consumed.
*/
- public boolean dispatchTouchEvent(MotionEvent ev) {
+ @Override
+ public boolean dispatchTouchEvent(@NonNull MotionEvent ev) {
if (mWindow.superDispatchTouchEvent(ev)) {
return true;
}
@@ -801,7 +806,8 @@
*
* @return boolean Return true if this event was consumed.
*/
- public boolean dispatchTrackballEvent(MotionEvent ev) {
+ @Override
+ public boolean dispatchTrackballEvent(@NonNull MotionEvent ev) {
if (mWindow.superDispatchTrackballEvent(ev)) {
return true;
}
@@ -818,14 +824,16 @@
*
* @return boolean Return true if this event was consumed.
*/
- public boolean dispatchGenericMotionEvent(MotionEvent ev) {
+ @Override
+ public boolean dispatchGenericMotionEvent(@NonNull MotionEvent ev) {
if (mWindow.superDispatchGenericMotionEvent(ev)) {
return true;
}
return onGenericMotionEvent(ev);
}
- public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+ @Override
+ public boolean dispatchPopulateAccessibilityEvent(@NonNull AccessibilityEvent event) {
event.setClassName(getClass().getName());
event.setPackageName(mContext.getPackageName());
@@ -840,6 +848,7 @@
/**
* @see Activity#onCreatePanelView(int)
*/
+ @Override
public View onCreatePanelView(int featureId) {
return null;
}
@@ -847,7 +856,8 @@
/**
* @see Activity#onCreatePanelMenu(int, Menu)
*/
- public boolean onCreatePanelMenu(int featureId, Menu menu) {
+ @Override
+ public boolean onCreatePanelMenu(int featureId, @NonNull Menu menu) {
if (featureId == Window.FEATURE_OPTIONS_PANEL) {
return onCreateOptionsMenu(menu);
}
@@ -858,10 +868,10 @@
/**
* @see Activity#onPreparePanel(int, View, Menu)
*/
+ @Override
public boolean onPreparePanel(int featureId, View view, Menu menu) {
if (featureId == Window.FEATURE_OPTIONS_PANEL && menu != null) {
- boolean goforit = onPrepareOptionsMenu(menu);
- return goforit && menu.hasVisibleItems();
+ return onPrepareOptionsMenu(menu) && menu.hasVisibleItems();
}
return true;
}
@@ -869,6 +879,7 @@
/**
* @see Activity#onMenuOpened(int, Menu)
*/
+ @Override
public boolean onMenuOpened(int featureId, Menu menu) {
if (featureId == Window.FEATURE_ACTION_BAR) {
mActionBar.dispatchMenuVisibilityChanged(true);
@@ -879,6 +890,7 @@
/**
* @see Activity#onMenuItemSelected(int, MenuItem)
*/
+ @Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
return false;
}
@@ -886,6 +898,7 @@
/**
* @see Activity#onPanelClosed(int, Menu)
*/
+ @Override
public void onPanelClosed(int featureId, Menu menu) {
if (featureId == Window.FEATURE_ACTION_BAR) {
mActionBar.dispatchMenuVisibilityChanged(false);
@@ -900,7 +913,7 @@
* @see Activity#onCreateOptionsMenu(Menu)
* @see #getOwnerActivity()
*/
- public boolean onCreateOptionsMenu(Menu menu) {
+ public boolean onCreateOptionsMenu(@NonNull Menu menu) {
return true;
}
@@ -912,21 +925,21 @@
* @see Activity#onPrepareOptionsMenu(Menu)
* @see #getOwnerActivity()
*/
- public boolean onPrepareOptionsMenu(Menu menu) {
+ public boolean onPrepareOptionsMenu(@NonNull Menu menu) {
return true;
}
/**
* @see Activity#onOptionsItemSelected(MenuItem)
*/
- public boolean onOptionsItemSelected(MenuItem item) {
+ public boolean onOptionsItemSelected(@NonNull MenuItem item) {
return false;
}
/**
* @see Activity#onOptionsMenuClosed(Menu)
*/
- public void onOptionsMenuClosed(Menu menu) {
+ public void onOptionsMenuClosed(@NonNull Menu menu) {
}
/**
@@ -959,47 +972,49 @@
/**
* @see Activity#onCreateContextMenu(ContextMenu, View, ContextMenuInfo)
*/
+ @Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
}
/**
* @see Activity#registerForContextMenu(View)
*/
- public void registerForContextMenu(View view) {
+ public void registerForContextMenu(@NonNull View view) {
view.setOnCreateContextMenuListener(this);
}
/**
* @see Activity#unregisterForContextMenu(View)
*/
- public void unregisterForContextMenu(View view) {
+ public void unregisterForContextMenu(@NonNull View view) {
view.setOnCreateContextMenuListener(null);
}
/**
* @see Activity#openContextMenu(View)
*/
- public void openContextMenu(View view) {
+ public void openContextMenu(@NonNull View view) {
view.showContextMenu();
}
/**
* @see Activity#onContextItemSelected(MenuItem)
*/
- public boolean onContextItemSelected(MenuItem item) {
+ public boolean onContextItemSelected(@NonNull MenuItem item) {
return false;
}
/**
* @see Activity#onContextMenuClosed(Menu)
*/
- public void onContextMenuClosed(Menu menu) {
+ public void onContextMenuClosed(@NonNull Menu menu) {
}
/**
* This hook is called when the user signals the desire to start a search.
*/
- public boolean onSearchRequested(SearchEvent searchEvent) {
+ @Override
+ public boolean onSearchRequested(@NonNull SearchEvent searchEvent) {
mSearchEvent = searchEvent;
return onSearchRequested();
}
@@ -1007,6 +1022,7 @@
/**
* This hook is called when the user signals the desire to start a search.
*/
+ @Override
public boolean onSearchRequested() {
final SearchManager searchManager = (SearchManager) mContext
.getSystemService(Context.SEARCH_SERVICE);
@@ -1029,13 +1045,10 @@
* @return SearchEvent The SearchEvent that triggered the {@link
* #onSearchRequested} callback.
*/
- public final SearchEvent getSearchEvent() {
+ public final @Nullable SearchEvent getSearchEvent() {
return mSearchEvent;
}
- /**
- * {@inheritDoc}
- */
@Override
public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) {
if (mActionBar != null && mActionModeTypeStarting == ActionMode.TYPE_PRIMARY) {
@@ -1044,9 +1057,6 @@
return null;
}
- /**
- * {@inheritDoc}
- */
@Override
public ActionMode onWindowStartingActionMode(ActionMode.Callback callback, int type) {
try {
@@ -1063,6 +1073,7 @@
* Note that if you override this method you should always call through
* to the superclass implementation by calling super.onActionModeStarted(mode).
*/
+ @Override
@CallSuper
public void onActionModeStarted(ActionMode mode) {
mActionMode = mode;
@@ -1074,6 +1085,7 @@
* Note that if you override this method you should always call through
* to the superclass implementation by calling super.onActionModeFinished(mode).
*/
+ @Override
@CallSuper
public void onActionModeFinished(ActionMode mode) {
if (mode == mActionMode) {
@@ -1139,7 +1151,7 @@
* Convenience for calling
* {@link android.view.Window#setFeatureDrawableUri}.
*/
- public final void setFeatureDrawableUri(int featureId, Uri uri) {
+ public final void setFeatureDrawableUri(int featureId, @Nullable Uri uri) {
getWindow().setFeatureDrawableUri(featureId, uri);
}
@@ -1147,7 +1159,7 @@
* Convenience for calling
* {@link android.view.Window#setFeatureDrawable(int, Drawable)}.
*/
- public final void setFeatureDrawable(int featureId, Drawable drawable) {
+ public final void setFeatureDrawable(int featureId, @Nullable Drawable drawable) {
getWindow().setFeatureDrawable(featureId, drawable);
}
@@ -1159,7 +1171,7 @@
getWindow().setFeatureDrawableAlpha(featureId, alpha);
}
- public LayoutInflater getLayoutInflater() {
+ public @NonNull LayoutInflater getLayoutInflater() {
return getWindow().getLayoutInflater();
}
@@ -1191,6 +1203,7 @@
* Cancel the dialog. This is essentially the same as calling {@link #dismiss()}, but it will
* also call your {@link DialogInterface.OnCancelListener} (if registered).
*/
+ @Override
public void cancel() {
if (!mCanceled && mCancelMessage != null) {
mCanceled = true;
@@ -1211,7 +1224,7 @@
*
* @param listener The {@link DialogInterface.OnCancelListener} to use.
*/
- public void setOnCancelListener(final OnCancelListener listener) {
+ public void setOnCancelListener(@Nullable OnCancelListener listener) {
if (mCancelAndDismissTaken != null) {
throw new IllegalStateException(
"OnCancelListener is already taken by "
@@ -1229,7 +1242,7 @@
* @param msg The msg to send when the dialog is canceled.
* @see #setOnCancelListener(android.content.DialogInterface.OnCancelListener)
*/
- public void setCancelMessage(final Message msg) {
+ public void setCancelMessage(@Nullable Message msg) {
mCancelMessage = msg;
}
@@ -1237,7 +1250,7 @@
* Set a listener to be invoked when the dialog is dismissed.
* @param listener The {@link DialogInterface.OnDismissListener} to use.
*/
- public void setOnDismissListener(final OnDismissListener listener) {
+ public void setOnDismissListener(@Nullable OnDismissListener listener) {
if (mCancelAndDismissTaken != null) {
throw new IllegalStateException(
"OnDismissListener is already taken by "
@@ -1254,7 +1267,7 @@
* Sets a listener to be invoked when the dialog is shown.
* @param listener The {@link DialogInterface.OnShowListener} to use.
*/
- public void setOnShowListener(OnShowListener listener) {
+ public void setOnShowListener(@Nullable OnShowListener listener) {
if (listener != null) {
mShowMessage = mListenersHandler.obtainMessage(SHOW, listener);
} else {
@@ -1266,13 +1279,13 @@
* Set a message to be sent when the dialog is dismissed.
* @param msg The msg to send when the dialog is dismissed.
*/
- public void setDismissMessage(final Message msg) {
+ public void setDismissMessage(@Nullable Message msg) {
mDismissMessage = msg;
}
/** @hide */
- public boolean takeCancelAndDismissListeners(String msg, final OnCancelListener cancel,
- final OnDismissListener dismiss) {
+ public boolean takeCancelAndDismissListeners(@Nullable String msg,
+ @Nullable OnCancelListener cancel, @Nullable OnDismissListener dismiss) {
if (mCancelAndDismissTaken != null) {
mCancelAndDismissTaken = null;
} else if (mCancelMessage != null || mDismissMessage != null) {
@@ -1306,15 +1319,15 @@
/**
* Sets the callback that will be called if a key is dispatched to the dialog.
*/
- public void setOnKeyListener(final OnKeyListener onKeyListener) {
+ public void setOnKeyListener(@Nullable OnKeyListener onKeyListener) {
mOnKeyListener = onKeyListener;
}
private static final class ListenersHandler extends Handler {
- private WeakReference<DialogInterface> mDialog;
+ private final WeakReference<DialogInterface> mDialog;
public ListenersHandler(Dialog dialog) {
- mDialog = new WeakReference<DialogInterface>(dialog);
+ mDialog = new WeakReference<>(dialog);
}
@Override
diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java
index 536c4a8..e681d47 100644
--- a/core/java/android/app/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -1193,13 +1193,52 @@
boolean isMediaScannerScannable, String mimeType, String path, long length,
boolean showNotification) {
return addCompletedDownload(title, description, isMediaScannerScannable, mimeType, path,
- length, showNotification, false);
+ length, showNotification, false, null, null);
+ }
+
+ /**
+ * Adds a file to the downloads database system, so it could appear in Downloads App
+ * (and thus become eligible for management by the Downloads App).
+ * <p>
+ * It is helpful to make the file scannable by MediaScanner by setting the param
+ * isMediaScannerScannable to true. It makes the file visible in media managing
+ * applications such as Gallery App, which could be a useful purpose of using this API.
+ *
+ * @param title the title that would appear for this file in Downloads App.
+ * @param description the description that would appear for this file in Downloads App.
+ * @param isMediaScannerScannable true if the file is to be scanned by MediaScanner. Files
+ * scanned by MediaScanner appear in the applications used to view media (for example,
+ * Gallery app).
+ * @param mimeType mimetype of the file.
+ * @param path absolute pathname to the file. The file should be world-readable, so that it can
+ * be managed by the Downloads App and any other app that is used to read it (for example,
+ * Gallery app to display the file, if the file contents represent a video/image).
+ * @param length length of the downloaded file
+ * @param showNotification true if a notification is to be sent, false otherwise
+ * @param uri the original HTTP URI of the download
+ * @param referer the HTTP Referer for the download
+ * @return an ID for the download entry added to the downloads app, unique across the system
+ * This ID is used to make future calls related to this download.
+ */
+ public long addCompletedDownload(String title, String description,
+ boolean isMediaScannerScannable, String mimeType, String path, long length,
+ boolean showNotification, Uri uri, Uri referer) {
+ return addCompletedDownload(title, description, isMediaScannerScannable, mimeType, path,
+ length, showNotification, false, uri, referer);
}
/** {@hide} */
public long addCompletedDownload(String title, String description,
boolean isMediaScannerScannable, String mimeType, String path, long length,
boolean showNotification, boolean allowWrite) {
+ return addCompletedDownload(title, description, isMediaScannerScannable, mimeType, path,
+ length, showNotification, allowWrite, null, null);
+ }
+
+ /** {@hide} */
+ public long addCompletedDownload(String title, String description,
+ boolean isMediaScannerScannable, String mimeType, String path, long length,
+ boolean showNotification, boolean allowWrite, Uri uri, Uri referer) {
// make sure the input args are non-null/non-zero
validateArgumentIsNonEmpty("title", title);
validateArgumentIsNonEmpty("description", description);
@@ -1210,10 +1249,18 @@
}
// if there is already an entry with the given path name in downloads.db, return its id
- Request request = new Request(NON_DOWNLOADMANAGER_DOWNLOAD)
- .setTitle(title)
+ Request request;
+ if (uri != null) {
+ request = new Request(uri);
+ } else {
+ request = new Request(NON_DOWNLOADMANAGER_DOWNLOAD);
+ }
+ request.setTitle(title)
.setDescription(description)
.setMimeType(mimeType);
+ if (referer != null) {
+ request.addRequestHeader("Referer", referer.toString());
+ }
ContentValues values = request.toContentValues(null);
values.put(Downloads.Impl.COLUMN_DESTINATION,
Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD);
@@ -1333,7 +1380,7 @@
if (destinationType == Downloads.Impl.DESTINATION_FILE_URI ||
destinationType == Downloads.Impl.DESTINATION_EXTERNAL ||
destinationType == Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD) {
- String localPath = getString(getColumnIndex(COLUMN_LOCAL_FILENAME));
+ String localPath = super.getString(getColumnIndex(COLUMN_LOCAL_FILENAME));
if (localPath == null) {
return null;
}
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index ddd0ae9..a599584 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -244,6 +244,10 @@
}
}
+ public boolean isWaitingForRemoteExit() {
+ return mIsReturning && mResultReceiver != null;
+ }
+
/**
* This is called onResume. If an Activity is resuming and the transitions
* haven't started yet, force the views to appear. This is likely to be
@@ -288,6 +292,10 @@
cancelPendingTransitions();
}
mAreViewsReady = true;
+ if (mResultReceiver != null) {
+ mResultReceiver.send(MSG_CANCEL, null);
+ mResultReceiver = null;
+ }
}
private void cancel() {
@@ -537,12 +545,10 @@
setTransitioningViewsVisiblity(View.INVISIBLE, false);
}
TransitionManager.beginDelayedTransition(decorView, transition);
- if (startSharedElementTransition && !mSharedElementNames.isEmpty()) {
- mSharedElements.get(0).invalidate();
- }
if (startEnterTransition) {
- setTransitioningViewsVisiblity(View.VISIBLE, true);
+ setTransitioningViewsVisiblity(View.VISIBLE, false);
}
+ decorView.invalidate();
} else {
transitionStarted();
}
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index d54ffa0..0404288 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -100,6 +100,10 @@
mExitSharedElementBundle = resultData;
sharedElementExitBack();
break;
+ case MSG_CANCEL:
+ mIsCanceled = true;
+ finish();
+ break;
}
}
@@ -268,7 +272,8 @@
if (transition != null && decorView != null && mTransitioningViews != null) {
setTransitioningViewsVisiblity(View.VISIBLE, false);
TransitionManager.beginDelayedTransition(decorView, transition);
- setTransitioningViewsVisiblity(View.INVISIBLE, true);
+ setTransitioningViewsVisiblity(View.INVISIBLE, false);
+ decorView.invalidate();
} else {
transitionStarted();
}
@@ -367,7 +372,7 @@
scheduleGhostVisibilityChange(View.VISIBLE);
setGhostVisibility(View.VISIBLE);
if (viewsTransition != null) {
- setTransitioningViewsVisiblity(View.INVISIBLE, true);
+ setTransitioningViewsVisiblity(View.INVISIBLE, false);
}
decorView.invalidate();
} else {
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index f7a4557..2a04c39 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -1033,11 +1033,12 @@
* false if it is not.
*/
public void setUserVisibleHint(boolean isVisibleToUser) {
- if (!mUserVisibleHint && isVisibleToUser && mState < STARTED && mFragmentManager != null) {
+ if (!mUserVisibleHint && isVisibleToUser && mState < STARTED
+ && mFragmentManager != null && isAdded()) {
mFragmentManager.performPendingDeferredStart(this);
}
mUserVisibleHint = isVisibleToUser;
- mDeferStart = !isVisibleToUser;
+ mDeferStart = mState < STARTED && !isVisibleToUser;
}
/**
@@ -1582,21 +1583,21 @@
/**
* Called when the Fragment's activity changes from fullscreen mode to multi-window mode and
- * visa-versa. This is generally tied to {@link Activity#onMultiWindowChanged} of the containing
- * Activity.
+ * visa-versa. This is generally tied to {@link Activity#onMultiWindowModeChanged} of the
+ * containing Activity.
*
- * @param inMultiWindow True if the activity is in multi-window mode.
+ * @param isInMultiWindowMode True if the activity is in multi-window mode.
*/
- public void onMultiWindowChanged(boolean inMultiWindow) {
+ public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {
}
/**
* Called by the system when the activity changes to and from picture-in-picture mode. This is
- * generally tied to {@link Activity#onPictureInPictureChanged} of the containing Activity.
+ * generally tied to {@link Activity#onPictureInPictureModeChanged} of the containing Activity.
*
- * @param inPictureInPicture True if the activity is in picture-in-picture mode.
+ * @param isInPictureInPictureMode True if the activity is in picture-in-picture mode.
*/
- public void onPictureInPictureChanged(boolean inPictureInPicture) {
+ public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
}
public void onConfigurationChanged(Configuration newConfig) {
@@ -2334,17 +2335,17 @@
}
}
- void performMultiWindowChanged(boolean inMultiWindow) {
- onMultiWindowChanged(inMultiWindow);
+ void performMultiWindowModeChanged(boolean isInMultiWindowMode) {
+ onMultiWindowModeChanged(isInMultiWindowMode);
if (mChildFragmentManager != null) {
- mChildFragmentManager.dispatchMultiWindowChanged(inMultiWindow);
+ mChildFragmentManager.dispatchMultiWindowModeChanged(isInMultiWindowMode);
}
}
- void performPictureInPictureChanged(boolean inPictureInPicture) {
- onPictureInPictureChanged(inPictureInPicture);
+ void performPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
+ onPictureInPictureModeChanged(isInPictureInPictureMode);
if (mChildFragmentManager != null) {
- mChildFragmentManager.dispatchPictureInPictureChanged(inPictureInPicture);
+ mChildFragmentManager.dispatchPictureInPictureModeChanged(isInPictureInPictureMode);
}
}
diff --git a/core/java/android/app/FragmentController.java b/core/java/android/app/FragmentController.java
index 57b0ff1..b3d2df5 100644
--- a/core/java/android/app/FragmentController.java
+++ b/core/java/android/app/FragmentController.java
@@ -247,10 +247,10 @@
* the activity changed.
* <p>Call when the multi-window mode of the activity changed.
*
- * @see Fragment#onMultiWindowChanged
+ * @see Fragment#onMultiWindowModeChanged
*/
- public void dispatchMultiWindowChanged(boolean inMultiWindow) {
- mHost.mFragmentManager.dispatchMultiWindowChanged(inMultiWindow);
+ public void dispatchMultiWindowModeChanged(boolean isInMultiWindowMode) {
+ mHost.mFragmentManager.dispatchMultiWindowModeChanged(isInMultiWindowMode);
}
/**
@@ -258,10 +258,10 @@
* mode of the activity changed.
* <p>Call when the picture-in-picture mode of the activity changed.
*
- * @see Fragment#onPictureInPictureChanged
+ * @see Fragment#onPictureInPictureModeChanged
*/
- public void dispatchPictureInPictureChanged(boolean inPictureInPicture) {
- mHost.mFragmentManager.dispatchPictureInPictureChanged(inPictureInPicture);
+ public void dispatchPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
+ mHost.mFragmentManager.dispatchPictureInPictureModeChanged(isInPictureInPictureMode);
}
/**
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 2852baf..8369f17 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -2069,26 +2069,26 @@
mParent = null;
}
- public void dispatchMultiWindowChanged(boolean inMultiWindow) {
+ public void dispatchMultiWindowModeChanged(boolean isInMultiWindowMode) {
if (mAdded == null) {
return;
}
for (int i = mAdded.size() - 1; i >= 0; --i) {
final Fragment f = mAdded.get(i);
if (f != null) {
- f.performMultiWindowChanged(inMultiWindow);
+ f.performMultiWindowModeChanged(isInMultiWindowMode);
}
}
}
- public void dispatchPictureInPictureChanged(boolean inPictureInPicture) {
+ public void dispatchPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
if (mAdded == null) {
return;
}
for (int i = mAdded.size() - 1; i >= 0; --i) {
final Fragment f = mAdded.get(i);
if (f != null) {
- f.performPictureInPictureChanged(inPictureInPicture);
+ f.performPictureInPictureModeChanged(isInPictureInPictureMode);
}
}
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 639c207..8ee6fd0 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -47,6 +47,7 @@
import android.os.Debug;
import android.os.IBinder;
import android.os.IInterface;
+import android.os.IProgressListener;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
@@ -461,7 +462,8 @@
// Multi-user APIs
public boolean switchUser(int userid) throws RemoteException;
public boolean startUserInBackground(int userid) throws RemoteException;
- public boolean unlockUser(int userid, byte[] token, byte[] secret) throws RemoteException;
+ public boolean unlockUser(int userid, byte[] token, byte[] secret, IProgressListener listener)
+ throws RemoteException;
public int stopUser(int userid, boolean force, IStopUserCallback callback) throws RemoteException;
public UserInfo getCurrentUser() throws RemoteException;
public boolean isUserRunning(int userid, int flags) throws RemoteException;
@@ -621,11 +623,11 @@
public int getAppStartMode(int uid, String packageName) throws RemoteException;
- public boolean inMultiWindow(IBinder token) throws RemoteException;
+ public boolean isInMultiWindowMode(IBinder token) throws RemoteException;
- public boolean inPictureInPicture(IBinder token) throws RemoteException;
+ public boolean isInPictureInPictureMode(IBinder token) throws RemoteException;
- public void enterPictureInPicture(IBinder token) throws RemoteException;
+ public void enterPictureInPictureMode(IBinder token) throws RemoteException;
public int setVrMode(IBinder token, boolean enabled, ComponentName packageName)
throws RemoteException;
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index 628bde0..a3b2638 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -158,8 +158,8 @@
void notifyCleartextNetwork(byte[] firstPacket) throws RemoteException;
void startBinderTracking() throws RemoteException;
void stopBinderTrackingAndDump(FileDescriptor fd) throws RemoteException;
- void scheduleMultiWindowChanged(IBinder token, boolean multiWindowMode) throws RemoteException;
- void schedulePictureInPictureChanged(IBinder token, boolean multiWindowMode) throws RemoteException;
+ void scheduleMultiWindowModeChanged(IBinder token, boolean isInMultiWindowMode) throws RemoteException;
+ void schedulePictureInPictureModeChanged(IBinder token, boolean isInPictureInPictureMode) throws RemoteException;
void scheduleLocalVoiceInteractionStarted(IBinder token, IVoiceInteractor voiceInteractor) throws RemoteException;
String descriptor = "android.app.IApplicationThread";
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index 6432558..fa67529 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -35,4 +35,9 @@
* Called whenever the pinned stack is done animating a resize.
*/
void onPinnedStackAnimationEnded();
+
+ /**
+ * Called when we launched an activity that we forced to be resizable.
+ */
+ void onActivityForcedResizable(String packageName, int taskId);
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 8423de8..2e4a8c6 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -174,6 +174,9 @@
* <li>Notification of an ongoing countdown timer should be stamped with the timer's end time.
* </ul>
*
+ * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not shown
+ * anymore by default and must be opted into by using
+ * {@link android.app.Notification.Builder#setShowWhen(boolean)}
*/
public long when;
@@ -206,6 +209,8 @@
* {@link Notification.Builder} has displayed the number in the expanded notification view.
*
* If the number is 0 or negative, it is never shown.
+ *
+ * @deprecated this number is not shown anymore
*/
public int number;
@@ -1205,6 +1210,7 @@
// Flags bitwise-ored to mFlags
private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
+ private static final int FLAG_HINT_LAUNCHES_ACTIVITY = 1 << 1;
// Default value for flags integer
private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
@@ -1367,6 +1373,30 @@
public CharSequence getCancelLabel() {
return mCancelLabel;
}
+
+ /**
+ * Set a hint that this Action will launch an {@link Activity} directly, telling the
+ * platform that it can generate the appropriate transitions.
+ * @param hintLaunchesActivity {@code true} if the content intent will launch
+ * an activity and transitions should be generated, false otherwise.
+ * @return this object for method chaining
+ */
+ public WearableExtender setHintContentIntentLaunchesActivity(
+ boolean hintLaunchesActivity) {
+ setFlag(FLAG_HINT_LAUNCHES_ACTIVITY, hintLaunchesActivity);
+ return this;
+ }
+
+ /**
+ * Get a hint that this Action will launch an {@link Activity} directly, telling the
+ * platform that it can generate the appropriate transitions
+ * @return {@code true} if the content intent will launch an activity and transitions
+ * should be generated, false otherwise. The default value is {@code false} if this was
+ * never set.
+ */
+ public boolean getHintContentIntentLaunchesActivity() {
+ return (mFlags & FLAG_HINT_LAUNCHES_ACTIVITY) != 0;
+ }
}
}
@@ -2133,7 +2163,9 @@
if (toAdopt == null) {
mN = new Notification();
- mN.extras.putBoolean(EXTRA_SHOW_WHEN, true);
+ if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
+ mN.extras.putBoolean(EXTRA_SHOW_WHEN, true);
+ }
mN.priority = PRIORITY_DEFAULT;
mN.visibility = VISIBILITY_PRIVATE;
} else {
@@ -2183,8 +2215,10 @@
/**
* Add a timestamp pertaining to the notification (usually the time the event occurred).
- * It will be shown in the notification content view by default; use
- * {@link #setShowWhen(boolean) setShowWhen} to control this.
+ *
+ * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not
+ * shown anymore by default and must be opted into by using
+ * {@link android.app.Notification.Builder#setShowWhen(boolean)}
*
* @see Notification#when
*/
@@ -2196,6 +2230,8 @@
/**
* Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown
* in the content view.
+ * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this defaults to
+ * {@code false}. For earlier apps, the default is {@code true}.
*/
public Builder setShowWhen(boolean show) {
mN.extras.putBoolean(EXTRA_SHOW_WHEN, show);
@@ -2304,9 +2340,22 @@
}
/**
- * Set the third line of text in the platform notification template.
- * Don't use if you're also using {@link #setProgress(int, int, boolean)}; they occupy the
- * same location in the standard template.
+ * This provides some additional information that is displayed in the notification. No
+ * guarantees are given where exactly it is displayed.
+ *
+ * <p>This information should only be provided if it provides an essential
+ * benefit to the understanding of the notification. The more text you provide the
+ * less readable it becomes. For example, an email client should only provide the account
+ * name here if more than one email account has been added.</p>
+ *
+ * <p>As of {@link android.os.Build.VERSION_CODES#N} this information is displayed in the
+ * notification header area.
+ *
+ * On Android versions before {@link android.os.Build.VERSION_CODES#N}
+ * this will be shown in the third line of text in the platform notification template.
+ * You should not be using {@link #setProgress(int, int, boolean)} at the
+ * same time on those versions; they occupy the same place.
+ * </p>
*/
public Builder setSubText(CharSequence text) {
mN.extras.putCharSequence(EXTRA_SUB_TEXT, safeCharSequence(text));
@@ -2345,6 +2394,8 @@
* Set the large number at the right-hand side of the notification. This is
* equivalent to setContentInfo, although it might show the number in a different
* font size for readability.
+ *
+ * @deprecated this number is not shown anywhere anymore
*/
public Builder setNumber(int number) {
mN.number = number;
@@ -2356,6 +2407,10 @@
*
* The platform template will draw this on the last line of the notification, at the far
* right (to the right of a smallIcon if it has been placed there).
+ *
+ * @deprecated use {@link #setSubText(CharSequence)} instead to set a text in the header.
+ * For legacy apps targeting a version below {@link android.os.Build.VERSION_CODES#N} this
+ * field will still show up, but the subtext will take precedence.
*/
public Builder setContentInfo(CharSequence info) {
mN.extras.putCharSequence(EXTRA_INFO_TEXT, safeCharSequence(info));
@@ -3009,10 +3064,8 @@
contentView.setBoolean(R.id.notification_header, "setExpanded", false);
contentView.setTextViewText(R.id.app_name_text, null);
contentView.setViewVisibility(R.id.chronometer, View.GONE);
- contentView.setViewVisibility(R.id.header_sub_text, View.GONE);
- contentView.setViewVisibility(R.id.header_content_info, View.GONE);
- contentView.setViewVisibility(R.id.sub_text_divider, View.GONE);
- contentView.setViewVisibility(R.id.content_info_divider, View.GONE);
+ contentView.setViewVisibility(R.id.header_text, View.GONE);
+ contentView.setViewVisibility(R.id.header_text_divider, View.GONE);
contentView.setViewVisibility(R.id.time_divider, View.GONE);
contentView.setImageViewIcon(R.id.profile_badge, null);
contentView.setViewVisibility(R.id.profile_badge, View.GONE);
@@ -3112,39 +3165,12 @@
private void bindNotificationHeader(RemoteViews contentView) {
bindSmallIcon(contentView);
bindHeaderAppName(contentView);
- bindHeaderSubText(contentView);
- bindContentInfo(contentView);
+ bindHeaderText(contentView);
bindHeaderChronometerAndTime(contentView);
bindExpandButton(contentView);
bindProfileBadge(contentView);
}
- private void bindContentInfo(RemoteViews contentView) {
- boolean visible = false;
- if (mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
- contentView.setTextViewText(R.id.header_content_info,
- processLegacyText(mN.extras.getCharSequence(EXTRA_INFO_TEXT)));
- contentView.setViewVisibility(R.id.header_content_info, View.VISIBLE);
- visible = true;
- } else if (mN.number > 0) {
- final int tooBig = mContext.getResources().getInteger(
- R.integer.status_bar_notification_info_maxnum);
- if (mN.number > tooBig) {
- contentView.setTextViewText(R.id.header_content_info, processLegacyText(
- mContext.getResources().getString(
- R.string.status_bar_notification_info_overflow)));
- } else {
- contentView.setTextViewText(R.id.header_content_info,
- processLegacyText(String.valueOf(mN.number)));
- }
- contentView.setViewVisibility(R.id.header_content_info, View.VISIBLE);
- visible = true;
- }
- if (visible) {
- contentView.setViewVisibility(R.id.content_info_divider, View.VISIBLE);
- }
- }
-
private void bindExpandButton(RemoteViews contentView) {
contentView.setDrawableParameters(R.id.expand_button, false, -1, resolveContrastColor(),
PorterDuff.Mode.SRC_ATOP, -1);
@@ -3169,17 +3195,22 @@
}
}
- private void bindHeaderSubText(RemoteViews contentView) {
- CharSequence subText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
- if (subText == null && mStyle != null && mStyle.mSummaryTextSet
+ private void bindHeaderText(RemoteViews contentView) {
+ CharSequence headerText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
+ if (headerText == null && mStyle != null && mStyle.mSummaryTextSet
&& mStyle.hasSummaryInHeader()) {
- subText = mStyle.mSummaryText;
+ headerText = mStyle.mSummaryText;
}
- if (subText != null) {
+ if (headerText == null
+ && mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
+ && mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
+ headerText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
+ }
+ if (headerText != null) {
// TODO: Remove the span entirely to only have the string with propper formating.
- contentView.setTextViewText(R.id.header_sub_text, processLegacyText(subText));
- contentView.setViewVisibility(R.id.header_sub_text, View.VISIBLE);
- contentView.setViewVisibility(R.id.sub_text_divider, View.VISIBLE);
+ contentView.setTextViewText(R.id.header_text, processLegacyText(headerText));
+ contentView.setViewVisibility(R.id.header_text, View.VISIBLE);
+ contentView.setViewVisibility(R.id.header_text_divider, View.VISIBLE);
}
}
@@ -4863,6 +4894,7 @@
private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
+ private static final int FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY = 1 << 6;
// Default value for flags integer
private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
@@ -5429,6 +5461,29 @@
return mHintScreenTimeout;
}
+ /**
+ * Set a hint that this notification's content intent will launch an {@link Activity}
+ * directly, telling the platform that it can generate the appropriate transitions.
+ * @param hintContentIntentLaunchesActivity {@code true} if the content intent will launch
+ * an activity and transitions should be generated, false otherwise.
+ * @return this object for method chaining
+ */
+ public WearableExtender setHintContentIntentLaunchesActivity(
+ boolean hintContentIntentLaunchesActivity) {
+ setFlag(FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY, hintContentIntentLaunchesActivity);
+ return this;
+ }
+
+ /**
+ * Get a hint that this notification's content intent will launch an {@link Activity}
+ * directly, telling the platform that it can generate the appropriate transitions
+ * @return {@code true} if the content intent will launch an activity and transitions should
+ * be generated, false otherwise. The default value is {@code false} if this was never set.
+ */
+ public boolean getHintContentIntentLaunchesActivity() {
+ return (mFlags & FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY) != 0;
+ }
+
private void setFlag(int mask, boolean value) {
if (value) {
mFlags |= mask;
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 0d4729d..47bff644 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -153,25 +153,31 @@
/**
* {@link #getCurrentInterruptionFilter() Interruption filter} constant -
- * Normal interruption filter.
+ * Normal interruption filter - no notifications are suppressed.
*/
public static final int INTERRUPTION_FILTER_ALL = 1;
/**
* {@link #getCurrentInterruptionFilter() Interruption filter} constant -
- * Priority interruption filter.
+ * Priority interruption filter - all notifications are suppressed except those that match
+ * the priority criteria. Some audio streams are muted. See
+ * {@link Policy#priorityCallSenders}, {@link Policy#priorityCategories},
+ * {@link Policy#priorityMessageSenders} to define or query this criteria. Users can
+ * additionally specify packages that can bypass this interruption filter.
*/
public static final int INTERRUPTION_FILTER_PRIORITY = 2;
/**
* {@link #getCurrentInterruptionFilter() Interruption filter} constant -
- * No interruptions filter.
+ * No interruptions filter - all notifications are suppressed and all audio streams (except
+ * those used for phone calls) and vibrations are muted.
*/
public static final int INTERRUPTION_FILTER_NONE = 3;
/**
* {@link #getCurrentInterruptionFilter() Interruption filter} constant -
- * Alarms only interruption filter.
+ * Alarms only interruption filter - all notifications except those of category
+ * {@link Notification#CATEGORY_ALARM} are suppressed. Some audio streams are muted.
*/
public static final int INTERRUPTION_FILTER_ALARMS = 4;
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 54d813d..4c4f128 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -66,7 +66,7 @@
}
};
- private String[] mSystemLocales = {};
+ private String[] mSystemLocales = null;
private final HashSet<String> mNonSystemLocales = new HashSet<>();
private boolean mHasNonSystemLocales = false;
@@ -94,9 +94,18 @@
private final ArrayList<WeakReference<Resources>> mResourceReferences = new ArrayList<>();
/**
- * Each Activity may have only one Resources object.
+ * Resources and base configuration override associated with an Activity.
*/
- private final WeakHashMap<IBinder, WeakReference<Resources>> mActivityResourceReferences =
+ private static class ActivityResources {
+ public final Configuration overrideConfig = new Configuration();
+ public final ArrayList<WeakReference<Resources>> activityResources = new ArrayList<>();
+ }
+
+ /**
+ * Each Activity may has a base override configuration that is applied to each Resources object,
+ * which in turn may have their own override configuration specified.
+ */
+ private final WeakHashMap<IBinder, ActivityResources> mActivityResourceReferences =
new WeakHashMap<>();
/**
@@ -115,18 +124,20 @@
}
public Configuration getConfiguration() {
- return mResConfiguration;
+ synchronized (this) {
+ return mResConfiguration;
+ }
}
- DisplayMetrics getDisplayMetricsLocked() {
- return getDisplayMetricsLocked(Display.DEFAULT_DISPLAY);
+ DisplayMetrics getDisplayMetrics() {
+ return getDisplayMetrics(Display.DEFAULT_DISPLAY);
}
/**
* Protected so that tests can override and returns something a fixed value.
*/
@VisibleForTesting
- protected DisplayMetrics getDisplayMetricsLocked(int displayId) {
+ protected DisplayMetrics getDisplayMetrics(int displayId) {
DisplayMetrics dm = new DisplayMetrics();
final Display display =
getAdjustedDisplay(displayId, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
@@ -272,10 +283,9 @@
return config;
}
-
private ResourcesImpl createResourcesImpl(@NonNull ResourcesKey key) {
AssetManager assets = createAssetManager(key);
- DisplayMetrics dm = getDisplayMetricsLocked(key.mDisplayId);
+ DisplayMetrics dm = getDisplayMetrics(key.mDisplayId);
Configuration config = generateConfig(key, dm);
ResourcesImpl impl = new ResourcesImpl(assets, dm, config, key.mCompatInfo);
if (DEBUG) {
@@ -290,7 +300,7 @@
* @param key The key to match.
* @return a ResourcesImpl if the key matches a cache entry, null otherwise.
*/
- private ResourcesImpl findResourcesImplForKey(@NonNull ResourcesKey key) {
+ private ResourcesImpl findResourcesImplForKeyLocked(@NonNull ResourcesKey key) {
WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.get(key);
ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null;
if (impl != null && impl.getAssets().isUpToDate()) {
@@ -303,7 +313,7 @@
* Find the ResourcesKey that this ResourcesImpl object is associated with.
* @return the ResourcesKey or null if none was found.
*/
- private ResourcesKey findKeyForResourceImpl(@NonNull ResourcesImpl resourceImpl) {
+ private ResourcesKey findKeyForResourceImplLocked(@NonNull ResourcesImpl resourceImpl) {
final int refCount = mResourceImpls.size();
for (int i = 0; i < refCount; i++) {
WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
@@ -315,36 +325,46 @@
return null;
}
+ private ActivityResources getOrCreateActivityResourcesStructLocked(
+ @NonNull IBinder activityToken) {
+ ActivityResources activityResources = mActivityResourceReferences.get(activityToken);
+ if (activityResources == null) {
+ activityResources = new ActivityResources();
+ mActivityResourceReferences.put(activityToken, activityResources);
+ }
+ return activityResources;
+ }
+
/**
* Gets an existing Resources object tied to this Activity, or creates one if it doesn't exist
* or the class loader is different.
*/
private Resources getOrCreateResourcesForActivityLocked(@NonNull IBinder activityToken,
@NonNull ClassLoader classLoader, @NonNull ResourcesImpl impl) {
- // This is a request tied to an Activity, meaning we will need to update all
- // Activity related Resources to match this configuration.
- WeakReference<Resources> weakResourceRef = mActivityResourceReferences.get(activityToken);
- Resources resources = weakResourceRef != null ? weakResourceRef.get() : null;
- if (resources == null || !Objects.equals(resources.getClassLoader(), classLoader)) {
- resources = new Resources(classLoader);
- mActivityResourceReferences.put(activityToken, new WeakReference<>(resources));
- if (DEBUG) {
- Slog.d(TAG, "- creating new ref=" + resources);
- }
- } else {
- if (DEBUG) {
- Slog.d(TAG, "- using existing ref=" + resources);
+ final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
+ activityToken);
+
+ final int refCount = activityResources.activityResources.size();
+ for (int i = 0; i < refCount; i++) {
+ WeakReference<Resources> weakResourceRef = activityResources.activityResources.get(i);
+ Resources resources = weakResourceRef.get();
+
+ if (resources != null
+ && Objects.equals(resources.getClassLoader(), classLoader)
+ && resources.getImpl() == impl) {
+ if (DEBUG) {
+ Slog.d(TAG, "- using existing ref=" + resources);
+ }
+ return resources;
}
}
- if (resources.getImpl() != impl) {
- if (DEBUG) {
- Slog.d(TAG, "- setting ref=" + resources + " with impl=" + impl);
- }
-
- // Setting an impl is expensive because we update all ThemeImpl references.
- // too.
- resources.setImpl(impl);
+ Resources resources = new Resources(classLoader);
+ resources.setImpl(impl);
+ activityResources.activityResources.add(new WeakReference<>(resources));
+ if (DEBUG) {
+ Slog.d(TAG, "- creating new ref=" + resources);
+ Slog.d(TAG, "- setting ref=" + resources + " with impl=" + impl);
}
return resources;
}
@@ -359,7 +379,7 @@
final int refCount = mResourceReferences.size();
for (int i = 0; i < refCount; i++) {
WeakReference<Resources> weakResourceRef = mResourceReferences.get(i);
- Resources resources = weakResourceRef != null ? weakResourceRef.get() : null;
+ Resources resources = weakResourceRef.get();
if (resources != null &&
Objects.equals(resources.getClassLoader(), classLoader) &&
resources.getImpl() == impl) {
@@ -382,6 +402,182 @@
}
/**
+ * Creates base resources for an Activity. Calls to
+ * {@link #getResources(IBinder, String, String[], String[], String[], int, Configuration,
+ * CompatibilityInfo, ClassLoader)} with the same activityToken will have their override
+ * configurations merged with the one specified here.
+ *
+ * @param activityToken Represents an Activity.
+ * @param resDir The base resource path. Can be null (only framework resources will be loaded).
+ * @param splitResDirs An array of split resource paths. Can be null.
+ * @param overlayDirs An array of overlay paths. Can be null.
+ * @param libDirs An array of resource library paths. Can be null.
+ * @param displayId The ID of the display for which to create the resources.
+ * @param overrideConfig The configuration to apply on top of the base configuration. Can be
+ * null. This provides the base override for this Activity.
+ * @param compatInfo The compatibility settings to use. Cannot be null. A default to use is
+ * {@link CompatibilityInfo#DEFAULT_COMPATIBILITY_INFO}.
+ * @param classLoader The class loader to use when inflating Resources. If null, the
+ * {@link ClassLoader#getSystemClassLoader()} is used.
+ * @return a Resources object from which to access resources.
+ */
+ public Resources createBaseActivityResources(@NonNull IBinder activityToken,
+ @Nullable String resDir,
+ @Nullable String[] splitResDirs,
+ @Nullable String[] overlayDirs,
+ @Nullable String[] libDirs,
+ int displayId,
+ @Nullable Configuration overrideConfig,
+ @NonNull CompatibilityInfo compatInfo,
+ @Nullable ClassLoader classLoader) {
+ final ResourcesKey key = new ResourcesKey(
+ resDir,
+ splitResDirs,
+ overlayDirs,
+ libDirs,
+ displayId,
+ overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
+ compatInfo);
+ classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
+
+ if (DEBUG) {
+ Slog.d(TAG, "createBaseActivityResources activity=" + activityToken
+ + " with key=" + key);
+ }
+
+ synchronized (this) {
+ final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
+ activityToken);
+
+ if (overrideConfig != null) {
+ activityResources.overrideConfig.setTo(overrideConfig);
+ } else {
+ activityResources.overrideConfig.setToDefaults();
+ }
+ }
+
+ // Update any existing Activity Resources references.
+ updateResourcesForActivity(activityToken, overrideConfig);
+
+ // Now request an actual Resources object.
+ return getOrCreateResources(activityToken, key, classLoader);
+ }
+
+ /**
+ * Gets an existing Resources object set with a ResourcesImpl object matching the given key,
+ * or creates one if it doesn't exist.
+ *
+ * @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.
+ * @return A Resources object that gets updated when
+ * {@link #applyConfigurationToResourcesLocked(Configuration, CompatibilityInfo)}
+ * is called.
+ */
+ private Resources getOrCreateResources(@Nullable IBinder activityToken,
+ @NonNull ResourcesKey key, @NonNull ClassLoader classLoader) {
+ final boolean findSystemLocales;
+ final boolean hasNonSystemLocales;
+ synchronized (this) {
+ findSystemLocales = (mSystemLocales == null || mSystemLocales.length == 0);
+ hasNonSystemLocales = mHasNonSystemLocales;
+
+ if (DEBUG) {
+ Throwable here = new Throwable();
+ here.fillInStackTrace();
+ Slog.w(TAG, "!! Get resources for activity=" + activityToken + " key=" + key, here);
+ }
+
+ if (activityToken != null) {
+ final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
+ activityToken);
+
+ // Clean up any dead references so they don't pile up.
+ ArrayUtils.unstableRemoveIf(activityResources.activityResources,
+ sEmptyReferencePredicate);
+
+ // Rebase the key's override config on top of the Activity's base override.
+ if (key.hasOverrideConfiguration()
+ && !activityResources.overrideConfig.equals(Configuration.EMPTY)) {
+ final Configuration temp = new Configuration(activityResources.overrideConfig);
+ temp.updateFrom(key.mOverrideConfiguration);
+ key.mOverrideConfiguration.setTo(temp);
+ }
+
+ ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(key);
+ if (resourcesImpl != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "- using existing impl=" + resourcesImpl);
+ }
+ return getOrCreateResourcesForActivityLocked(activityToken, classLoader,
+ resourcesImpl);
+ }
+
+ // We will create the ResourcesImpl object outside of holding this lock.
+
+ } else {
+ // Clean up any dead references so they don't pile up.
+ ArrayUtils.unstableRemoveIf(mResourceReferences, sEmptyReferencePredicate);
+
+ // Not tied to an Activity, find a shared Resources that has the right ResourcesImpl
+ ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(key);
+ if (resourcesImpl != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "- using existing impl=" + resourcesImpl);
+ }
+ return getOrCreateResourcesLocked(classLoader, resourcesImpl);
+ }
+
+ // We will create the ResourcesImpl object outside of holding this lock.
+ }
+ }
+
+ // If we're here, we didn't find a suitable ResourcesImpl to use, so create one now.
+ ResourcesImpl resourcesImpl = createResourcesImpl(key);
+
+ final String[] systemLocales = findSystemLocales
+ ? AssetManager.getSystem().getLocales() : null;
+ final String[] nonSystemLocales = resourcesImpl.getAssets().getNonSystemLocales();
+ // Avoid checking for non-pseudo-locales if we already know there were some from a previous
+ // Resources. The default value (for when hasNonSystemLocales is true) doesn't matter,
+ // since mHasNonSystemLocales will also be true, and thus isPseudoLocalesOnly would not be
+ // able to affect mHasNonSystemLocales.
+ final boolean isPseudoLocalesOnly = hasNonSystemLocales ||
+ LocaleList.isPseudoLocalesOnly(nonSystemLocales);
+
+ synchronized (this) {
+ if (mSystemLocales == null || mSystemLocales.length == 0) {
+ mSystemLocales = systemLocales;
+ }
+ mNonSystemLocales.addAll(Arrays.asList(nonSystemLocales));
+ mHasNonSystemLocales = mHasNonSystemLocales || !isPseudoLocalesOnly;
+
+ ResourcesImpl existingResourcesImpl = findResourcesImplForKeyLocked(key);
+ if (existingResourcesImpl != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "- got beat! existing impl=" + existingResourcesImpl
+ + " new impl=" + resourcesImpl);
+ }
+ resourcesImpl.getAssets().close();
+ resourcesImpl = existingResourcesImpl;
+ } else {
+ // Add this ResourcesImpl to the cache.
+ mResourceImpls.put(key, new WeakReference<>(resourcesImpl));
+ }
+
+ final Resources resources;
+ if (activityToken != null) {
+ resources = getOrCreateResourcesForActivityLocked(activityToken, classLoader,
+ resourcesImpl);
+ } else {
+ resources = getOrCreateResourcesLocked(classLoader, resourcesImpl);
+ }
+ return resources;
+ }
+ }
+
+ /**
* Gets or creates a new Resources object associated with the IBinder token. References returned
* by this method live as long as the Activity, meaning they can be cached and used by the
* Activity even after a configuration change. If any other parameter is changed
@@ -425,92 +621,8 @@
displayId,
overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
compatInfo);
-
classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
-
- final boolean findSystemLocales;
- final boolean hasNonSystemLocales;
- synchronized (this) {
- findSystemLocales = (mSystemLocales.length == 0);
- hasNonSystemLocales = mHasNonSystemLocales;
-
- if (DEBUG) {
- Throwable here = new Throwable();
- here.fillInStackTrace();
- Slog.w(TAG, "!! Get resources for activity=" + activityToken + " key=" + key, here);
- }
-
- if (activityToken != null) {
- ResourcesImpl resourcesImpl = findResourcesImplForKey(key);
- if (resourcesImpl != null) {
- if (DEBUG) {
- Slog.d(TAG, "- using existing impl=" + resourcesImpl);
- }
- return getOrCreateResourcesForActivityLocked(activityToken, classLoader,
- resourcesImpl);
- }
-
- // We will create the ResourcesImpl object outside of holding this lock.
-
- } else {
- // Clean up any dead references so they don't pile up.
- ArrayUtils.unstableRemoveIf(mResourceReferences, sEmptyReferencePredicate);
-
- // Not tied to an Activity, find a shared Resources that has the right ResourcesImpl
- ResourcesImpl resourcesImpl = findResourcesImplForKey(key);
- if (resourcesImpl != null) {
- if (DEBUG) {
- Slog.d(TAG, "- using existing impl=" + resourcesImpl);
- }
- return getOrCreateResourcesLocked(classLoader, resourcesImpl);
- }
-
- // We will create the ResourcesImpl object outside of holding this lock.
- }
- }
-
- // If we're here, we didn't find a suitable ResourcesImpl to use, so create one now.
- ResourcesImpl resourcesImpl = createResourcesImpl(key);
-
- final String[] systemLocales = findSystemLocales
- ? AssetManager.getSystem().getLocales() : null;
- final String[] nonSystemLocales = resourcesImpl.getAssets().getNonSystemLocales();
- // Avoid checking for non-pseudo-locales if we already know there were some from a previous
- // Resources. The default value (for when hasNonSystemLocales is true) doesn't matter,
- // since mHasNonSystemLocales will also be true, and thus isPseudoLocalesOnly would not be
- // able to affect mHasNonSystemLocales.
- final boolean isPseudoLocalesOnly = hasNonSystemLocales ||
- LocaleList.isPseudoLocalesOnly(nonSystemLocales);
-
- synchronized (this) {
- if (mSystemLocales.length == 0) {
- mSystemLocales = systemLocales;
- }
- mNonSystemLocales.addAll(Arrays.asList(nonSystemLocales));
- mHasNonSystemLocales = mHasNonSystemLocales || !isPseudoLocalesOnly;
-
- ResourcesImpl existingResourcesImpl = findResourcesImplForKey(key);
- if (existingResourcesImpl != null) {
- if (DEBUG) {
- Slog.d(TAG, "- got beat! existing impl=" + existingResourcesImpl
- + " new impl=" + resourcesImpl);
- }
- resourcesImpl.getAssets().close();
- resourcesImpl = existingResourcesImpl;
- } else {
- // Add this ResourcesImpl to the cache.
- mResourceImpls.put(key, new WeakReference<>(resourcesImpl));
- }
-
- final Resources resources;
- if (activityToken != null) {
- resources = getOrCreateResourcesForActivityLocked(activityToken, classLoader,
- resourcesImpl);
- } else {
- resources = getOrCreateResourcesLocked(classLoader, resourcesImpl);
- }
- return resources;
- }
+ return getOrCreateResources(activityToken, key, classLoader);
}
/**
@@ -524,34 +636,100 @@
*/
public void updateResourcesForActivity(@NonNull IBinder activityToken,
@Nullable Configuration overrideConfig) {
- final ClassLoader classLoader;
- final ResourcesKey oldKey;
synchronized (this) {
- // Extract the ResourcesKey that was last used to create the Resources for this
- // activity.
- WeakReference<Resources> weakResRef = mActivityResourceReferences.get(activityToken);
- final Resources resources = weakResRef != null ? weakResRef.get() : null;
- if (resources == null) {
- Slog.e(TAG, "can't update resources for uncached activity " + activityToken);
+ final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
+ activityToken);
+
+ if (Objects.equals(activityResources.overrideConfig, overrideConfig)) {
+ // They are the same, no work to do.
return;
}
- classLoader = resources.getClassLoader();
- oldKey = findKeyForResourceImpl(resources.getImpl());
- if (oldKey == null) {
- Slog.e(TAG, "can't find ResourcesKey for resources impl=" + resources.getImpl());
- return;
+ // Grab a copy of the old configuration so we can create the delta's of each
+ // Resources object associated with this Activity.
+ final Configuration oldConfig = new Configuration(activityResources.overrideConfig);
+
+ // Update the Activity's base override.
+ if (overrideConfig != null) {
+ activityResources.overrideConfig.setTo(overrideConfig);
+ } else {
+ activityResources.overrideConfig.setToDefaults();
+ }
+
+ if (DEBUG) {
+ Throwable here = new Throwable();
+ here.fillInStackTrace();
+ Slog.d(TAG, "updating resources override for activity=" + activityToken
+ + " from oldConfig=" + Configuration.resourceQualifierString(oldConfig)
+ + " to newConfig="
+ + Configuration.resourceQualifierString(activityResources.overrideConfig),
+ here);
+ }
+
+ final boolean activityHasOverrideConfig =
+ !activityResources.overrideConfig.equals(Configuration.EMPTY);
+
+ // Rebase each Resources associated with this Activity.
+ final int refCount = activityResources.activityResources.size();
+ for (int i = 0; i < refCount; i++) {
+ WeakReference<Resources> weakResRef = activityResources.activityResources.get(i);
+ Resources resources = weakResRef.get();
+ if (resources == null) {
+ continue;
+ }
+
+ // Extract the ResourcesKey that was last used to create the Resources for this
+ // activity.
+ final ResourcesKey oldKey = findKeyForResourceImplLocked(resources.getImpl());
+ if (oldKey == null) {
+ Slog.e(TAG, "can't find ResourcesKey for resources impl="
+ + resources.getImpl());
+ continue;
+ }
+
+ // Build the new override configuration for this ResourcesKey.
+ final Configuration rebasedOverrideConfig = new Configuration();
+ if (overrideConfig != null) {
+ rebasedOverrideConfig.setTo(overrideConfig);
+ }
+
+ if (activityHasOverrideConfig && oldKey.hasOverrideConfiguration()) {
+ // Generate a delta between the old base Activity override configuration and
+ // the actual final override configuration that was used to figure out the real
+ // delta this Resources object wanted.
+ Configuration overrideOverrideConfig = Configuration.generateDelta(
+ oldConfig, oldKey.mOverrideConfiguration);
+ rebasedOverrideConfig.updateFrom(overrideOverrideConfig);
+ }
+
+ // Create the new ResourcesKey with the rebased override config.
+ final ResourcesKey newKey = new ResourcesKey(oldKey.mResDir, oldKey.mSplitResDirs,
+ oldKey.mOverlayDirs, oldKey.mLibDirs, oldKey.mDisplayId,
+ rebasedOverrideConfig, oldKey.mCompatInfo);
+
+ if (DEBUG) {
+ Slog.d(TAG, "rebasing ref=" + resources + " from oldKey=" + oldKey
+ + " to newKey=" + newKey);
+ }
+
+ ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(newKey);
+ if (resourcesImpl == null) {
+ resourcesImpl = createResourcesImpl(newKey);
+ mResourceImpls.put(newKey, new WeakReference<>(resourcesImpl));
+ }
+
+ if (resourcesImpl != resources.getImpl()) {
+ // Set the ResourcesImpl, updating it for all users of this Resources object.
+ resources.setImpl(resourcesImpl);
+ }
}
}
-
- // Update the Resources object with the new override config and all of the existing
- // settings.
- getResources(activityToken, oldKey.mResDir, oldKey.mSplitResDirs, oldKey.mOverlayDirs,
- oldKey.mLibDirs, oldKey.mDisplayId, overrideConfig, oldKey.mCompatInfo,
- classLoader);
}
/* package */ void setDefaultLocalesLocked(@NonNull LocaleList locales) {
+ if (mSystemLocales == null) {
+ throw new RuntimeException("ResourcesManager is not ready to negotiate locales.");
+ }
final int bestLocale;
if (mHasNonSystemLocales) {
bestLocale = locales.getFirstMatchIndexWithEnglishSupported(mNonSystemLocales);
@@ -575,7 +753,7 @@
int changes = mResConfiguration.updateFrom(config);
// Things might have changed in display manager, so clear the cached displays.
mDisplays.clear();
- DisplayMetrics defaultDisplayMetrics = getDisplayMetricsLocked();
+ DisplayMetrics defaultDisplayMetrics = getDisplayMetrics();
if (compat != null && (mResCompatibilityInfo == null ||
!mResCompatibilityInfo.equals(compat))) {
@@ -629,7 +807,7 @@
}
tmpConfig.setTo(localeAdjustedConfig);
if (!isDefaultDisplay) {
- dm = getDisplayMetricsLocked(displayId);
+ dm = getDisplayMetrics(displayId);
applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
}
if (hasOverrideConfiguration) {
@@ -649,4 +827,4 @@
return changes != 0;
}
-}
\ No newline at end of file
+}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 2987fbc..bdc4404 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -882,7 +882,12 @@
public final T getService(ContextImpl ctx) {
synchronized (StaticApplicationContextServiceFetcher.this) {
if (mCachedInstance == null) {
- mCachedInstance = createService(ctx.getApplicationContext());
+ Context appContext = ctx.getApplicationContext();
+ // If the application context is null, we're either in the system process or
+ // it's the application context very early in app initialization. In both these
+ // cases, the passed-in ContextImpl will not be freed, so it's safe to pass it
+ // to the service. http://b/27532714 .
+ mCachedInstance = createService(appContext != null ? appContext : ctx);
}
return mCachedInstance;
}
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index 276f774..2c1ee8e 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -36,6 +36,7 @@
import android.view.WindowContentFrameStats;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.IAccessibilityManager;
+
import libcore.io.IoUtils;
import java.io.FileOutputStream;
@@ -77,6 +78,7 @@
private int mOwningUid;
+ @Override
public void connect(IAccessibilityServiceClient client, int flags) {
if (client == null) {
throw new IllegalArgumentException("Client cannot be null!");
@@ -326,11 +328,12 @@
int flags) {
IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface(
ServiceManager.getService(Context.ACCESSIBILITY_SERVICE));
- AccessibilityServiceInfo info = new AccessibilityServiceInfo();
+ final AccessibilityServiceInfo info = new AccessibilityServiceInfo();
info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
info.flags |= AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
- | AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS;
+ | AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS
+ | AccessibilityServiceInfo.FLAG_FORCE_DIRECT_BOOT_AWARE;
info.setCapabilities(AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT
| AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION
| AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index b52af27..72b9318 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -161,8 +161,8 @@
/** @hide */
@IntDef(flag = true, value = {
- FLAG_SET_SYSTEM,
- FLAG_SET_LOCK
+ FLAG_SYSTEM,
+ FLAG_LOCK
})
@Retention(RetentionPolicy.SOURCE)
public @interface SetWallpaperFlags {}
@@ -170,12 +170,12 @@
/**
* Flag: use the supplied imagery as the general system wallpaper.
*/
- public static final int FLAG_SET_SYSTEM = 1 << 0;
+ public static final int FLAG_SYSTEM = 1 << 0;
/**
* Flag: use the supplied imagery as the lock-screen wallpaper.
*/
- public static final int FLAG_SET_LOCK = 1 << 1;
+ public static final int FLAG_LOCK = 1 << 1;
private final Context mContext;
@@ -336,7 +336,7 @@
try {
Bundle params = new Bundle();
- ParcelFileDescriptor fd = mService.getWallpaper(this, FLAG_SET_SYSTEM,
+ ParcelFileDescriptor fd = mService.getWallpaper(this, FLAG_SYSTEM,
params, userId);
if (fd != null) {
try {
@@ -663,11 +663,18 @@
/**
* Get an open, readable file descriptor to the given wallpaper image file.
- * The callee is resopnsible for closing the fd when done ingesting the file.
+ * The caller is responsible for closing the file descriptor when done ingesting the file.
*
* <p>If no lock-specific wallpaper has been configured for the given user, then
- * this method will return {@code null} when requesting {@link #FLAG_SET_LOCK} rather than
+ * this method will return {@code null} when requesting {@link #FLAG_LOCK} rather than
* returning the system wallpaper's image file.
+ *
+ * @param which The wallpaper whose image file is to be retrieved. Must be a single
+ * defined kind of wallpaper, either {@link #FLAG_SYSTEM} or
+ * {@link #FLAG_LOCK}.
+ *
+ * @see #FLAG_LOCK
+ * @see #FLAG_SYSTEM
*/
public ParcelFileDescriptor getWallpaperFile(int which) {
return getWallpaperFile(which, mContext.getUserId());
@@ -677,10 +684,19 @@
* Version of {@link #getWallpaperFile(int)} that can access the wallpaper data
* for a given user. The caller must hold the INTERACT_ACROSS_USERS_FULL
* permission to access another user's wallpaper data.
+ *
+ * @param which The wallpaper whose image file is to be retrieved. Must be a single
+ * defined kind of wallpaper, either {@link #FLAG_SYSTEM} or
+ * {@link #FLAG_LOCK}.
+ * @param userId The user or profile whose imagery is to be retrieved
+ *
+ * @see #FLAG_LOCK
+ * @see #FLAG_SYSTEM
+ *
* @hide
*/
public ParcelFileDescriptor getWallpaperFile(int which, int userId) {
- if (which != FLAG_SET_SYSTEM && which != FLAG_SET_LOCK) {
+ if (which != FLAG_SYSTEM && which != FLAG_LOCK) {
throw new IllegalArgumentException("Must request exactly one kind of wallpaper");
}
@@ -730,8 +746,8 @@
* such wallpaper configured, returns a negative number.
*
* @param which The wallpaper whose ID is to be returned. Must be a single
- * defined kind of wallpaper, either {@link #FLAG_SET_SYSTEM} or
- * {@link #FLAG_SET_LOCK}.
+ * defined kind of wallpaper, either {@link #FLAG_SYSTEM} or
+ * {@link #FLAG_LOCK}.
* @return The positive numeric ID of the current wallpaper of the given kind,
* or a negative value if no such wallpaper is configured.
*/
@@ -823,24 +839,24 @@
* <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#SET_WALLPAPER}.
*
- * @param resid The bitmap to save.
+ * @param resid The resource ID of the bitmap to be used as the wallpaper image
*
* @throws IOException If an error occurs reverting to the built-in
* wallpaper.
*/
public void setResource(@RawRes int resid) throws IOException {
- setResource(resid, FLAG_SET_SYSTEM);
+ setResource(resid, FLAG_SYSTEM);
}
/**
- * Version of {@link #setResource(int)} that takes an optional Bundle for returning
- * metadata about the operation to the caller.
+ * Version of {@link #setResource(int)} that allows the caller to specify which
+ * of the supported wallpaper categories to set.
*
- * @param resid
- * @param which Flags indicating which wallpaper(s) to configure with the new imagery.
+ * @param resid The resource ID of the bitmap to be used as the wallpaper image
+ * @param which Flags indicating which wallpaper(s) to configure with the new imagery
*
- * @see #FLAG_SET_LOCK
- * @see #FLAG_SET_SYSTEM
+ * @see #FLAG_LOCK
+ * @see #FLAG_SYSTEM
*
* @return An integer ID assigned to the newly active wallpaper; or zero on failure.
*
@@ -934,11 +950,10 @@
*/
public int setBitmap(Bitmap fullImage, Rect visibleCropHint, boolean allowBackup)
throws IOException {
- return setBitmap(fullImage, visibleCropHint, allowBackup, FLAG_SET_SYSTEM);
+ return setBitmap(fullImage, visibleCropHint, allowBackup, FLAG_SYSTEM);
}
/**
- /**
* Version of {@link #setBitmap(Bitmap, Rect, boolean)} that allows the caller
* to specify which of the supported wallpaper categories to set.
*
@@ -951,8 +966,8 @@
* image for restore to a future device; {@code false} otherwise.
* @param which Flags indicating which wallpaper(s) to configure with the new imagery.
*
- * @see #FLAG_SET_LOCK
- * @see #FLAG_SET_SYSTEM
+ * @see #FLAG_LOCK
+ * @see #FLAG_SYSTEM
*
* @return An integer ID assigned to the newly active wallpaper; or zero on failure.
*
@@ -1054,7 +1069,7 @@
*/
public int setStream(InputStream bitmapData, Rect visibleCropHint, boolean allowBackup)
throws IOException {
- return setStream(bitmapData, visibleCropHint, allowBackup, FLAG_SET_SYSTEM);
+ return setStream(bitmapData, visibleCropHint, allowBackup, FLAG_SYSTEM);
}
/**
@@ -1070,8 +1085,8 @@
* image for restore to a future device; {@code false} otherwise.
* @param which Flags indicating which wallpaper(s) to configure with the new imagery.
*
- * @see #FLAG_SET_LOCK
- * @see #FLAG_SET_SYSTEM
+ * @see #FLAG_LOCK
+ * @see #FLAG_SYSTEM
*
* @throws IOException
*/
@@ -1287,7 +1302,7 @@
*/
@SystemApi
public void clearWallpaper() {
- clearWallpaper(FLAG_SET_SYSTEM, mContext.getUserId());
+ clearWallpaper(FLAG_SYSTEM, mContext.getUserId());
}
/**
@@ -1467,20 +1482,20 @@
/**
* Remove one or more currently set wallpapers, reverting to the system default
- * display for each one. If {@link #FLAG_SET_SYSTEM} is set in the {@code which}
+ * display for each one. If {@link #FLAG_SYSTEM} is set in the {@code which}
* parameter, the intent {@link Intent#ACTION_WALLPAPER_CHANGED} will be broadcast
* upon success.
*
- * @param which A bitwise combination of {@link #FLAG_SET_SYSTEM} or
- * {@link #FLAG_SET_LOCK}
+ * @param which A bitwise combination of {@link #FLAG_SYSTEM} or
+ * {@link #FLAG_LOCK}
* @throws IOException If an error occurs reverting to the built-in wallpaper.
*/
public void clear(int which) throws IOException {
- if ((which & FLAG_SET_SYSTEM) != 0) {
+ if ((which & FLAG_SYSTEM) != 0) {
clear();
}
- if ((which & FLAG_SET_LOCK) != 0) {
- clearWallpaper(FLAG_SET_LOCK, mContext.getUserId());
+ if ((which & FLAG_LOCK) != 0) {
+ clearWallpaper(FLAG_LOCK, mContext.getUserId());
}
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index e7427bf..f15b8fe 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -71,6 +71,7 @@
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -2184,9 +2185,6 @@
* Force a new device unlock password (the password needed to access the entire device, not for
* individual accounts) on the user. This takes effect immediately.
* <p>
- * Calling this from a managed profile that shares the password with the owner profile will
- * throw a security exception.
- * <p>
* <em>Note: This API has been limited as of {@link android.os.Build.VERSION_CODES#N} for
* device admins that are not device owner and not profile owner.
* The password can now only be changed if there is currently no password set. Device owner
@@ -2200,10 +2198,10 @@
* case the currently active quality will be increased to match.
* <p>
* Calling with a null or empty password will clear any existing PIN, pattern or password if the
- * current password constraints allow it. <em>Note: This will not
- * work in {@link android.os.Build.VERSION_CODES#N} and later for device admins that are not
- * device owner and not profile owner. Once set, the password cannot be changed to null or
- * empty, except by device owner or profile owner.</em>
+ * current password constraints allow it. <em>Note: This will not work in
+ * {@link android.os.Build.VERSION_CODES#N} and later for managed profiles, or for device admins
+ * that are not device owner or profile owner. Once set, the password cannot be changed to null
+ * or empty except by these admins.</em>
* <p>
* The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_RESET_PASSWORD} to be able to call this method; if it has
@@ -2654,6 +2652,43 @@
}
/**
+ * Mark a CA certificate as approved by the device user. This means that they have been notified
+ * of the installation, were made aware of the risks, viewed the certificate and still wanted to
+ * keep the certificate on the device.
+ *
+ * Calling with {@param approval} as {@code true} will cancel any ongoing warnings related to
+ * this certificate.
+ *
+ * @hide
+ */
+ public boolean approveCaCert(String alias, int userHandle, boolean approval) {
+ if (mService != null) {
+ try {
+ return mService.approveCaCert(alias, userHandle, approval);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check whether a CA certificate has been approved by the device user.
+ *
+ * @hide
+ */
+ public boolean isCaCertApproved(String alias, int userHandle) {
+ if (mService != null) {
+ try {
+ return mService.isCaCertApproved(alias, userHandle);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
+
+ /**
* Installs the given certificate as a user CA.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
@@ -2782,7 +2817,7 @@
* compromised, certificates it had already installed will be protected.
*
* <p>If the installer must have access to the credentials, call
- * {@link #installKeyPair(ComponentName, PrivateKey, Certificate, String, boolean)} instead.
+ * {@link #installKeyPair(ComponentName, PrivateKey, Certificate[], String, boolean)} instead.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
* {@code null} if calling from a delegated certificate installer.
@@ -2796,13 +2831,14 @@
*/
public boolean installKeyPair(@Nullable ComponentName admin, @NonNull PrivateKey privKey,
@NonNull Certificate cert, @NonNull String alias) {
- return installKeyPair(admin, privKey, cert, alias, false);
+ return installKeyPair(admin, privKey, new Certificate[] {cert}, alias, false);
}
/**
* Called by a device or profile owner, or delegated certificate installer, to install a
- * certificate and corresponding private key. All apps within the profile will be able to access
- * the certificate and use the private key, given direct user approval.
+ * certificate chain and corresponding private key for the leaf certificate. All apps within the
+ * profile will be able to access the certificate chain and use the private key, given direct
+ * user approval.
*
* <p>The caller of this API may grant itself access to the certificate and private key
* immediately, without user approval. It is a best practice not to request this unless strictly
@@ -2811,7 +2847,9 @@
* @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
* {@code null} if calling from a delegated certificate installer.
* @param privKey The private key to install.
- * @param cert The certificate to install.
+ * @param certs The certificate chain to install. The chain should start with the leaf
+ * certificate and include the chain of trust in order. This will be returned by
+ * {@link android.security.KeyChain#getCertificateChain}.
* @param alias The private key alias under which to install the certificate. If a certificate
* with that alias already exists, it will be overwritten.
* @param requestAccess {@code true} to request that the calling app be granted access to the
@@ -2820,14 +2858,20 @@
* @return {@code true} if the keys were installed, {@code false} otherwise.
* @throws SecurityException if {@code admin} is not {@code null} and not a device or profile
* owner.
+ * @see android.security.KeyChain#getCertificateChain
*/
public boolean installKeyPair(@Nullable ComponentName admin, @NonNull PrivateKey privKey,
- @NonNull Certificate cert, @NonNull String alias, boolean requestAccess) {
+ @NonNull Certificate[] certs, @NonNull String alias, boolean requestAccess) {
try {
- final byte[] pemCert = Credentials.convertToPem(cert);
+ final byte[] pemCert = Credentials.convertToPem(certs[0]);
+ byte[] pemChain = null;
+ if (certs.length > 1) {
+ pemChain = Credentials.convertToPem(Arrays.copyOfRange(certs, 1, certs.length));
+ }
final byte[] pkcs8Key = KeyFactory.getInstance(privKey.getAlgorithm())
.getKeySpec(privKey, PKCS8EncodedKeySpec.class).getEncoded();
- return mService.installKeyPair(admin, pkcs8Key, pemCert, alias, requestAccess);
+ return mService.installKeyPair(admin, pkcs8Key, pemCert, pemChain, alias,
+ requestAccess);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
@@ -2933,17 +2977,21 @@
* @return {@code true} if the package is set as always-on VPN controller; {@code false}
* otherwise.
* @throws SecurityException if {@code admin} is not a device or a profile owner.
+ * @throws NameNotFoundException if {@code vpnPackage} is not installed.
+ * @throws UnsupportedOperationException if {@code vpnPackage} exists but does not support being
+ * set as always-on, or if always-on VPN is not available.
*/
- public boolean setAlwaysOnVpnPackage(@NonNull ComponentName admin,
- @Nullable String vpnPackage) {
+ public void setAlwaysOnVpnPackage(@NonNull ComponentName admin, @Nullable String vpnPackage)
+ throws NameNotFoundException, UnsupportedOperationException {
if (mService != null) {
try {
- return mService.setAlwaysOnVpnPackage(admin, vpnPackage);
+ if (!mService.setAlwaysOnVpnPackage(admin, vpnPackage)) {
+ throw new NameNotFoundException(vpnPackage);
+ }
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
- return false;
}
/**
@@ -3736,24 +3784,22 @@
*
* @param admin The name of the admin component to check.
* @param info Device owner information which will be displayed instead of the user owner info.
- * @return Whether the device owner information has been set.
* @throws SecurityException if {@code admin} is not a device owner.
*/
- public boolean setDeviceOwnerLockScreenInfo(@NonNull ComponentName admin, String info) {
+ public void setDeviceOwnerLockScreenInfo(@NonNull ComponentName admin, CharSequence info) {
if (mService != null) {
try {
- return mService.setDeviceOwnerLockScreenInfo(admin, info);
+ mService.setDeviceOwnerLockScreenInfo(admin, info);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
}
- return false;
}
/**
* @return The device owner information. If it is not set returns {@code null}.
*/
- public String getDeviceOwnerLockScreenInfo() {
+ public CharSequence getDeviceOwnerLockScreenInfo() {
if (mService != null) {
try {
return mService.getDeviceOwnerLockScreenInfo();
@@ -3803,13 +3849,17 @@
* @return {@code true} if the package is suspended or {@code false} if the package is not
* suspended, could not be found or an error occurred.
* @throws SecurityException if {@code admin} is not a device or profile owner.
+ * @throws NameNotFoundException if the package could not be found.
*/
- public boolean getPackageSuspended(@NonNull ComponentName admin, String packageName) {
+ public boolean isPackageSuspended(@NonNull ComponentName admin, String packageName)
+ throws NameNotFoundException {
if (mService != null) {
try {
- return mService.getPackageSuspended(admin, packageName);
+ return mService.isPackageSuspended(admin, packageName);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
+ } catch (IllegalArgumentException ex) {
+ throw new NameNotFoundException(packageName);
}
}
return false;
@@ -4110,6 +4160,10 @@
* The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_DISABLE_KEYGUARD_FEATURES} to be able to call this method;
* if not, a security exception will be thrown.
+ * <p>
+ * This method can be called on the {@link DevicePolicyManager} instance returned by
+ * {@link #getParentProfileInstance(ComponentName)} in order to set the configuration for
+ * the parent profile.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param target Component name of the agent to be enabled.
@@ -4130,7 +4184,7 @@
@NonNull ComponentName target, PersistableBundle configuration) {
if (mService != null) {
try {
- mService.setTrustAgentConfiguration(admin, target, configuration);
+ mService.setTrustAgentConfiguration(admin, target, configuration, mParentInstance);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -4141,6 +4195,10 @@
* Gets configuration for the given trust agent based on aggregating all calls to
* {@link #setTrustAgentConfiguration(ComponentName, ComponentName, PersistableBundle)} for
* all device admins.
+ * <p>
+ * This method can be called on the {@link DevicePolicyManager} instance returned by
+ * {@link #getParentProfileInstance(ComponentName)} in order to retrieve the configuration set
+ * on the parent profile.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with. If null,
* this function returns a list of configurations for all admins that declare
@@ -4161,7 +4219,8 @@
@NonNull ComponentName agent, int userHandle) {
if (mService != null) {
try {
- return mService.getTrustAgentConfiguration(admin, agent, userHandle);
+ return mService.getTrustAgentConfiguration(admin, agent, userHandle,
+ mParentInstance);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -4881,15 +4940,30 @@
* @throws SecurityException if {@code admin} is not a device or profile owner.
*/
public Bundle getUserRestrictions(@NonNull ComponentName admin) {
- return getUserRestrictions(admin, myUserId());
- }
-
- /** @hide per-user version */
- public Bundle getUserRestrictions(@NonNull ComponentName admin, int userHandle) {
Bundle ret = null;
if (mService != null) {
try {
- ret = mService.getUserRestrictions(admin, userHandle);
+ ret = mService.getUserRestrictions(admin);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return ret == null ? new Bundle() : ret;
+ }
+
+ /**
+ * Called by the system to get the user restrictions for a user.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param userHandle user id the admin is running as.
+ *
+ * @hide
+ */
+ public Bundle getUserRestrictionsForUser(@NonNull ComponentName admin, int userHandle) {
+ Bundle ret = null;
+ if (mService != null) {
+ try {
+ ret = mService.getUserRestrictionsForUser(admin, userHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -5590,10 +5664,9 @@
}
/**
- * @hide
* Return if this user is a managed profile of another user. An admin can become the profile
* owner of a managed profile with {@link #ACTION_PROVISION_MANAGED_PROFILE} and of a managed
- * user with {@link #ACTION_PROVISION_MANAGED_USER}.
+ * user with {@link #createAndManageUser}
* @param admin Which profile owner this request is associated with.
* @return if this user is a managed profile of another user.
*/
@@ -5789,8 +5862,43 @@
/**
* Called by the profile owner of a managed profile to obtain a {@link DevicePolicyManager}
* whose calls act on the parent profile.
- * <p>
- * Note only some methods will work on the parent Manager.
+ *
+ * <p>The following methods are supported for the parent instance, all other methods will
+ * throw a SecurityException when called on the parent instance:
+ * <ul>
+ * <li>{@link #getPasswordQuality}</li>
+ * <li>{@link #setPasswordQuality}</li>
+ * <li>{@link #getPasswordMinimumLength}</li>
+ * <li>{@link #setPasswordMinimumLength}</li>
+ * <li>{@link #getPasswordMinimumUpperCase}</li>
+ * <li>{@link #setPasswordMinimumUpperCase}</li>
+ * <li>{@link #getPasswordMinimumLowerCase}</li>
+ * <li>{@link #setPasswordMinimumLowerCase}</li>
+ * <li>{@link #getPasswordMinimumLetters}</li>
+ * <li>{@link #setPasswordMinimumLetters}</li>
+ * <li>{@link #getPasswordMinimumNumeric}</li>
+ * <li>{@link #setPasswordMinimumNumeric}</li>
+ * <li>{@link #getPasswordMinimumSymbols}</li>
+ * <li>{@link #setPasswordMinimumSymbols}</li>
+ * <li>{@link #getPasswordMinimumNonLetter}</li>
+ * <li>{@link #setPasswordMinimumNonLetter}</li>
+ * <li>{@link #getPasswordHistoryLength}</li>
+ * <li>{@link #setPasswordHistoryLength}</li>
+ * <li>{@link #getPasswordExpirationTimeout}</li>
+ * <li>{@link #setPasswordExpirationTimeout}</li>
+ * <li>{@link #getPasswordExpiration}</li>
+ * <li>{@link #isActivePasswordSufficient}</li>
+ * <li>{@link #getCurrentFailedPasswordAttempts}</li>
+ * <li>{@link #getMaximumFailedPasswordsForWipe}</li>
+ * <li>{@link #setMaximumFailedPasswordsForWipe}</li>
+ * <li>{@link #getMaximumTimeToLock}</li>
+ * <li>{@link #setMaximumTimeToLock}</li>
+ * <li>{@link #lockNow}</li>
+ * <li>{@link #getKeyguardDisabledFeatures}</li>
+ * <li>{@link #setKeyguardDisabledFeatures}</li>
+ * <li>{@link #getTrustAgentConfiguration}</li>
+ * <li>{@link #setTrustAgentConfiguration}</li>
+ * </ul>
*
* @return a new instance of {@link DevicePolicyManager} that acts on the parent profile.
* @throws SecurityException if {@code admin} is not a profile owner.
@@ -5897,7 +6005,7 @@
* <p>
* <strong> The device logs are retrieved from a RAM region which is not guaranteed to be
* corruption-free during power cycles, due to hardware variations and limitations. As a result,
- * this API is provided as best-effort and the returned logs may contain corrupted
+ * this API is provided as best-effort and the returned logs may be empty or contain corrupted
* data. </strong>
* <p>
* There must be only one user on the device, managed by the device owner. Otherwise a
@@ -5919,7 +6027,7 @@
/**
* Called by a profile owner of a managed profile to set the color used for customization. This
* color is used as background color of the confirm credentials screen for that user. The
- * default color is {@link android.graphics.Color#GRAY}.
+ * default color is teal (#00796B).
* <p>
* The confirm credentials screen can be created using
* {@link android.app.KeyguardManager#createConfirmDeviceCredentialIntent}.
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index aed220d..1fb2283 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -135,18 +135,20 @@
void clearProfileOwner(in ComponentName who);
boolean hasUserSetupCompleted();
- boolean setDeviceOwnerLockScreenInfo(in ComponentName who, String deviceOwnerInfo);
- String getDeviceOwnerLockScreenInfo();
+ void setDeviceOwnerLockScreenInfo(in ComponentName who, CharSequence deviceOwnerInfo);
+ CharSequence getDeviceOwnerLockScreenInfo();
String[] setPackagesSuspended(in ComponentName admin, in String[] packageNames, boolean suspended);
- boolean getPackageSuspended(in ComponentName admin, String packageName);
+ boolean isPackageSuspended(in ComponentName admin, String packageName);
boolean installCaCert(in ComponentName admin, in byte[] certBuffer);
void uninstallCaCerts(in ComponentName admin, in String[] aliases);
void enforceCanManageCaCerts(in ComponentName admin);
+ boolean approveCaCert(in String alias, int userHandle, boolean approval);
+ boolean isCaCertApproved(in String alias, int userHandle);
boolean installKeyPair(in ComponentName who, in byte[] privKeyBuffer, in byte[] certBuffer,
- String alias, boolean requestAccess);
+ in byte[] certChainBuffer, String alias, boolean requestAccess);
boolean removeKeyPair(in ComponentName who, String alias);
void choosePrivateKeyAlias(int uid, in Uri uri, in String alias, IBinder aliasCallback);
@@ -169,7 +171,8 @@
ComponentName getRestrictionsProvider(int userHandle);
void setUserRestriction(in ComponentName who, in String key, boolean enable);
- Bundle getUserRestrictions(in ComponentName who, int userId);
+ Bundle getUserRestrictions(in ComponentName who);
+ Bundle getUserRestrictionsForUser(in ComponentName who, int userId);
void addCrossProfileIntentFilter(in ComponentName admin, in IntentFilter filter, int flags);
void clearCrossProfileIntentFilters(in ComponentName admin);
@@ -225,9 +228,9 @@
boolean getBluetoothContactSharingDisabledForUser(int userId);
void setTrustAgentConfiguration(in ComponentName admin, in ComponentName agent,
- in PersistableBundle args);
+ in PersistableBundle args, boolean parent);
List<PersistableBundle> getTrustAgentConfiguration(in ComponentName admin,
- in ComponentName agent, int userId);
+ in ComponentName agent, int userId, boolean parent);
boolean addCrossProfileWidgetProvider(in ComponentName admin, String packageName);
boolean removeCrossProfileWidgetProvider(in ComponentName admin, String packageName);
diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java
index 001a81d..2858991 100644
--- a/core/java/android/app/admin/SecurityLog.java
+++ b/core/java/android/app/admin/SecurityLog.java
@@ -67,7 +67,7 @@
* information about the process encapsulated in an {@link Object} array, accessible via
* {@link SecurityEvent#getData()}:
* process name (String), exact start time (long), app Uid (integer), app Pid (integer),
- * seinfo tag (String), SHA-256 hash of the APK in hexadecimal (String)
+ * seinfo tag (String), SHA-256 hash of the base APK in hexadecimal (String)
*/
public static final int TAG_APP_PROCESS_START = SecurityLogTags.SECURITY_APP_PROCESS_START;
/**
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 801c951..8e515e2 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -427,23 +427,31 @@
}
/**
- * Tells the application agent that the backup data size exceeded current transport quota.
- * Later calls to {@link #onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor)}
- * and {@link #onFullBackup(FullBackupDataOutput)} could use this information
- * to reduce backup size under the limit.
- * However, the quota can change, so do not assume that the value passed in here is absolute,
- * similarly all subsequent backups should not be restricted to this size.
- * This callback will be invoked before data has been put onto the wire in a preflight check,
- * so it is relatively inexpensive to hit your quota.
- * Apps that hit quota repeatedly without dealing with it can be subject to having their backup
- * schedule reduced.
- * The {@code quotaBytes} is a loose guideline b/c of metadata added by the backupmanager
- * so apps should be more aggressive in trimming their backup set.
+ * Notification that the application's current backup operation causes it to exceed
+ * the maximum size permitted by the transport. The ongoing backup operation is
+ * halted and rolled back: any data that had been stored by a previous backup operation
+ * is still intact. Typically the quota-exceeded state will be detected before any data
+ * is actually transmitted over the network.
*
- * @param backupDataBytes Expected or already processed amount of data.
- * Could be less than total backup size if backup process was interrupted
- * before finish of processing all backup data.
- * @param quotaBytes Current amount of backup data that is allowed for the app.
+ * <p>The {@code quotaBytes} value is the total data size currently permitted for this
+ * application. If desired, the application can use this as a hint for determining
+ * how much data to store. For example, a messaging application might choose to
+ * store only the newest messages, dropping enough older content to stay under
+ * the quota.
+ *
+ * <p class="note">Note that the maximum quota for the application can change over
+ * time. In particular, in the future the quota may grow. Applications that adapt
+ * to the quota when deciding what data to store should be aware of this and implement
+ * their data storage mechanisms in a way that can take advantage of additional
+ * quota.
+ *
+ * @param backupDataBytes The amount of data measured while initializing the backup
+ * operation, if the total exceeds the app's alloted quota. If initial measurement
+ * suggested that the data would fit but then too much data was actually submitted
+ * as part of the operation, then this value is the amount of data that had been
+ * streamed into the transport at the time the quota was reached.
+ * @param quotaBytes The maximum data size that the transport currently permits
+ * this application to store as a backup.
*/
public void onQuotaExceeded(long backupDataBytes, long quotaBytes) {
}
diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java
index 1af4953..2d9f4a7 100644
--- a/core/java/android/appwidget/AppWidgetHost.java
+++ b/core/java/android/appwidget/AppWidgetHost.java
@@ -17,8 +17,7 @@
package android.appwidget;
import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.List;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -35,7 +34,9 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SystemClock;
import android.util.DisplayMetrics;
+import android.util.SparseArray;
import android.util.TypedValue;
import android.widget.RemoteViews;
import android.widget.RemoteViews.OnClickHandler;
@@ -62,7 +63,7 @@
private final Handler mHandler;
private final int mHostId;
private final Callbacks mCallbacks;
- private final HashMap<Integer,AppWidgetHostView> mViews = new HashMap<>();
+ private final SparseArray<AppWidgetHostView> mViews = new SparseArray<>();
private OnClickHandler mOnClickHandler;
static class Callbacks extends IAppWidgetHost.Stub {
@@ -164,7 +165,6 @@
bindService();
}
-
private static void bindService() {
synchronized (sServiceLock) {
if (sService == null) {
@@ -179,17 +179,25 @@
* becomes visible, i.e. from onStart() in your Activity.
*/
public void startListening() {
- int[] updatedIds;
- ArrayList<RemoteViews> updatedViews = new ArrayList<RemoteViews>();
+ final int[] idsToUpdate;
+ synchronized (mViews) {
+ int N = mViews.size();
+ idsToUpdate = new int[N];
+ for (int i = 0; i < N; i++) {
+ idsToUpdate[i] = mViews.keyAt(i);
+ }
+ }
+ List<RemoteViews> updatedViews;
+ int[] updatedIds = new int[idsToUpdate.length];
try {
- updatedIds = sService.startListening(mCallbacks, mContextOpPackageName, mHostId,
- updatedViews);
+ updatedViews = sService.startListening(
+ mCallbacks, mContextOpPackageName, mHostId, idsToUpdate, updatedIds).getList();
}
catch (RemoteException e) {
throw new RuntimeException("system server dead?", e);
}
- final int N = updatedIds.length;
+ int N = updatedViews.size();
for (int i = 0; i < N; i++) {
updateAppWidgetView(updatedIds[i], updatedViews.get(i));
}
@@ -206,10 +214,6 @@
catch (RemoteException e) {
throw new RuntimeException("system server dead?", e);
}
-
- // This is here because keyguard needs it since it'll be switching users after this call.
- // If it turns out other apps need to call this often, we should re-think how this works.
- clearViews();
}
/**
@@ -418,7 +422,9 @@
* Clear the list of Views that have been created by this AppWidgetHost.
*/
protected void clearViews() {
- mViews.clear();
+ synchronized (mViews) {
+ mViews.clear();
+ }
}
}
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index d762a17..2a7eff8 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -36,6 +36,7 @@
import android.os.ParcelUuid;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SystemProperties;
import android.util.Log;
import android.util.Pair;
@@ -1015,6 +1016,8 @@
try {
if (mService != null) {
return mService.factoryReset();
+ } else {
+ SystemProperties.set("persist.bluetooth.factoryreset", "true");
}
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index 68442ea..b8a40dc 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -422,7 +422,7 @@
try {
mAuthRetry = true;
mService.writeDescriptor(mClientIf, address, handle,
- descriptor.getCharacteristic().getWriteType(),
+ BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT,
AUTHENTICATION_MITM, descriptor.getValue());
return;
} catch (RemoteException e) {
@@ -545,7 +545,6 @@
/*package*/ BluetoothGattCharacteristic getCharacteristicById(BluetoothDevice device, int instanceId) {
for(BluetoothGattService svc : mServices) {
for(BluetoothGattCharacteristic charac : svc.getCharacteristics()) {
- Log.w(TAG, "getCharacteristicById() comparing " + charac.getInstanceId() + " and " + instanceId);
if (charac.getInstanceId() == instanceId)
return charac;
}
@@ -944,7 +943,8 @@
try {
mService.writeDescriptor(mClientIf, device.getAddress(), descriptor.getInstanceId(),
- characteristic.getWriteType(), AUTHENTICATION_NONE, descriptor.getValue());
+ BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT, AUTHENTICATION_NONE,
+ descriptor.getValue());
} catch (RemoteException e) {
Log.e(TAG,"",e);
mDeviceBusy = false;
diff --git a/core/java/android/bluetooth/BluetoothGattCharacteristic.java b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
index 7d698b3..01f82e6 100644
--- a/core/java/android/bluetooth/BluetoothGattCharacteristic.java
+++ b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
@@ -284,6 +284,8 @@
out.writeInt(mInstance);
out.writeInt(mProperties);
out.writeInt(mPermissions);
+ out.writeInt(mKeySize);
+ out.writeInt(mWriteType);
out.writeTypedList(mDescriptors);
}
@@ -303,6 +305,8 @@
mInstance = in.readInt();
mProperties = in.readInt();
mPermissions = in.readInt();
+ mKeySize = in.readInt();
+ mWriteType = in.readInt();
mDescriptors = new ArrayList<BluetoothGattDescriptor>();
diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java
index e355a1c..35437a1 100644
--- a/core/java/android/bluetooth/BluetoothManager.java
+++ b/core/java/android/bluetooth/BluetoothManager.java
@@ -144,7 +144,7 @@
}
/**
- *
+ *
* Get a list of devices that match any of the given connection
* states.
*
diff --git a/core/java/android/bluetooth/BluetoothPbapClient.java b/core/java/android/bluetooth/BluetoothPbapClient.java
index 736e55d..eab4c6f 100644
--- a/core/java/android/bluetooth/BluetoothPbapClient.java
+++ b/core/java/android/bluetooth/BluetoothPbapClient.java
@@ -40,7 +40,6 @@
"android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED";
private IBluetoothPbapClient mService;
- private BluetoothDevice mDevice;
private final Context mContext;
private ServiceListener mServiceListener;
private BluetoothAdapter mAdapter;
@@ -173,7 +172,6 @@
}
if (mService != null && isEnabled() && isValidDevice(device)) {
try {
- mDevice = device;
return mService.connect(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
@@ -193,13 +191,13 @@
* @return false on error,
* true otherwise
*/
- public boolean disconnect() {
+ public boolean disconnect(BluetoothDevice device) {
if (DBG) {
- log("disconnect(" + mDevice + ")");
+ log("disconnect(" + device + ")" + new Exception() );
}
- if (mService != null && isEnabled() && isValidDevice(mDevice)) {
+ if (mService != null && isEnabled() && isValidDevice(device)) {
try {
- mService.disconnect(mDevice);
+ mService.disconnect(device);
return true;
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
@@ -328,4 +326,66 @@
}
return false;
}
+
+ /**
+ * Set priority of the profile
+ *
+ * <p> The device should already be paired.
+ * Priority can be one of {@link #PRIORITY_ON} or
+ * {@link #PRIORITY_OFF},
+ *
+ * @param device Paired bluetooth device
+ * @param priority
+ * @return true if priority is set, false on error
+ */
+ public boolean setPriority(BluetoothDevice device, int priority) {
+ if (DBG) {
+ log("setPriority(" + device + ", " + priority + ")");
+ }
+ if (mService != null && isEnabled() &&
+ isValidDevice(device)) {
+ if (priority != BluetoothProfile.PRIORITY_OFF &&
+ priority != BluetoothProfile.PRIORITY_ON) {
+ return false;
+ }
+ try {
+ return mService.setPriority(device, priority);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ return false;
+ }
+ }
+ if (mService == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ return false;
+ }
+
+ /**
+ * Get the priority of the profile.
+ *
+ * <p> The priority can be any of:
+ * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
+ * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
+ *
+ * @param device Bluetooth device
+ * @return priority of the device
+ */
+ public int getPriority(BluetoothDevice device) {
+ if (VDBG) {
+ log("getPriority(" + device + ")");
+ }
+ if (mService != null && isEnabled() && isValidDevice(device)) {
+ try {
+ return mService.getPriority(device);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ return PRIORITY_OFF;
+ }
+ }
+ if (mService == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ return PRIORITY_OFF;
+ }
}
diff --git a/core/java/android/bluetooth/IBluetoothPbapClient.aidl b/core/java/android/bluetooth/IBluetoothPbapClient.aidl
index b26ea29..6d4c5a6 100644
--- a/core/java/android/bluetooth/IBluetoothPbapClient.aidl
+++ b/core/java/android/bluetooth/IBluetoothPbapClient.aidl
@@ -29,4 +29,6 @@
List<BluetoothDevice> getConnectedDevices();
List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
int getConnectionState(in BluetoothDevice device);
+ boolean setPriority(in BluetoothDevice device, int priority);
+ int getPriority(in BluetoothDevice device);
}
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index cd67b3e..4db4567 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -17,6 +17,7 @@
package android.content;
import android.accounts.Account;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -60,6 +61,8 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
@@ -289,6 +292,31 @@
/** @hide */
public static final int SYNC_OBSERVER_TYPE_ALL = 0x7fffffff;
+ /** @hide */
+ @IntDef(flag = true,
+ value = {
+ NOTIFY_SYNC_TO_NETWORK,
+ NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface NotifyFlags {}
+
+ /**
+ * Flag for {@link #notifyChange(Uri, ContentObserver, int)}: attempt to sync the change
+ * to the network.
+ */
+ public static final int NOTIFY_SYNC_TO_NETWORK = 1<<0;
+
+ /**
+ * Flag for {@link #notifyChange(Uri, ContentObserver, int)}: if set, this notification
+ * will be skipped if it is being delivered to the root URI of a ContentObserver that is
+ * using "notify for descendants." The purpose of this is to allow the provide to send
+ * a general notification of "something under X" changed that observers of that specific
+ * URI can receive, while also sending a specific URI under X. It would use this flag
+ * when sending the former, so that observers of "X and descendants" only see the latter.
+ */
+ public static final int NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS = 1<<1;
+
// Always log queries which take 500ms+; shorter queries are
// sampled accordingly.
private static final boolean ENABLE_CONTENT_SAMPLE = false;
@@ -1676,7 +1704,7 @@
* The observer that originated the change will only receive the notification if it
* has requested to receive self-change notifications by implementing
* {@link ContentObserver#deliverSelfNotifications()} to return true.
- * @param syncToNetwork If true, attempt to sync the change to the network.
+ * @param syncToNetwork If true, same as {@link #NOTIFY_SYNC_TO_NETWORK}.
* @see #requestSync(android.accounts.Account, String, android.os.Bundle)
*/
public void notifyChange(@NonNull Uri uri, @Nullable ContentObserver observer,
@@ -1690,6 +1718,32 @@
}
/**
+ * Notify registered observers that a row was updated.
+ * To register, call {@link #registerContentObserver(android.net.Uri, boolean, android.database.ContentObserver) registerContentObserver()}.
+ * By default, CursorAdapter objects will get this notification.
+ * If syncToNetwork is true, this will attempt to schedule a local sync using the sync
+ * adapter that's registered for the authority of the provided uri. No account will be
+ * passed to the sync adapter, so all matching accounts will be synchronized.
+ *
+ * @param uri The uri of the content that was changed.
+ * @param observer The observer that originated the change, may be <code>null</null>.
+ * The observer that originated the change will only receive the notification if it
+ * has requested to receive self-change notifications by implementing
+ * {@link ContentObserver#deliverSelfNotifications()} to return true.
+ * @param flags Additional flags: {@link #NOTIFY_SYNC_TO_NETWORK}.
+ * @see #requestSync(android.accounts.Account, String, android.os.Bundle)
+ */
+ public void notifyChange(@NonNull Uri uri, @Nullable ContentObserver observer,
+ @NotifyFlags int flags) {
+ Preconditions.checkNotNull(uri, "uri");
+ notifyChange(
+ ContentProvider.getUriWithoutUserId(uri),
+ observer,
+ flags,
+ ContentProvider.getUserIdFromUri(uri, UserHandle.myUserId()));
+ }
+
+ /**
* Notify registered observers within the designated user(s) that a row was updated.
*
* @hide
@@ -1699,7 +1753,24 @@
try {
getContentService().notifyChange(
uri, observer == null ? null : observer.getContentObserver(),
- observer != null && observer.deliverSelfNotifications(), syncToNetwork,
+ observer != null && observer.deliverSelfNotifications(),
+ syncToNetwork ? NOTIFY_SYNC_TO_NETWORK : 0,
+ userHandle);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Notify registered observers within the designated user(s) that a row was updated.
+ *
+ * @hide
+ */
+ public void notifyChange(Uri uri, ContentObserver observer, @NotifyFlags int flags,
+ @UserIdInt int userHandle) {
+ try {
+ getContentService().notifyChange(
+ uri, observer == null ? null : observer.getContentObserver(),
+ observer != null && observer.deliverSelfNotifications(), flags,
userHandle);
} catch (RemoteException e) {
}
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index b2df207..087ac47 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -164,6 +164,7 @@
return mBase.getSharedPreferences(name, mode);
}
+ /** @removed */
@Override
public SharedPreferences getSharedPreferences(File file, int mode) {
return mBase.getSharedPreferences(file, mode);
@@ -201,6 +202,7 @@
return mBase.getFileStreamPath(name);
}
+ /** @removed */
@Override
public File getSharedPreferencesPath(String name) {
return mBase.getSharedPreferencesPath(name);
diff --git a/core/java/android/content/IContentService.aidl b/core/java/android/content/IContentService.aidl
index d47e780..3446e03 100644
--- a/core/java/android/content/IContentService.aidl
+++ b/core/java/android/content/IContentService.aidl
@@ -52,7 +52,7 @@
* USER_CURRENT are properly interpreted.
*/
void notifyChange(in Uri uri, IContentObserver observer,
- boolean observerWantsSelfNotifications, boolean syncToNetwork,
+ boolean observerWantsSelfNotifications, int flags,
int userHandle);
void requestSync(in Account account, String authority, in Bundle extras);
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 57ab7a2..30f2c94 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1581,14 +1581,6 @@
= "android.intent.extra.UNINSTALL_ALL_USERS";
/**
- * Specified when the uninstall confirmation dialog is not required to be shown.
- * Use with {@link #ACTION_UNINSTALL_PACKAGE}
- * @hide
- */
- public static final String EXTRA_SKIP_UNINSTALL_CONFIRMATION =
- "android.intent.extra.SKIP_UNINSTALL_CONFIRMATION";
-
- /**
* A string associated with a {@link #ACTION_UPGRADE_SETUP} activity
* describing the last run version of the platform that was setup.
* @hide
@@ -2856,12 +2848,15 @@
"com.google.android.c2dm.intent.RECEIVE";
/**
- * Broadcast Action: hook for permforming cleanup after a system update.
+ * Broadcast Action: This is broadcast once when the user is booting after a
+ * system update. It can be used to perform cleanup or upgrades after a
+ * system update.
+ * <p>
+ * This broadcast is sent after the {@link #ACTION_LOCKED_BOOT_COMPLETED}
+ * broadcast but before the {@link #ACTION_BOOT_COMPLETED} broadcast. It's
+ * only sent when the {@link Build#FINGERPRINT} has changed, and it's only
+ * sent to receivers in the system image.
*
- * The broadcast is sent when the system is booting, before the
- * BOOT_COMPLETED broadcast. It is only sent to receivers in the system
- * image. A receiver for this should do its work and then disable itself
- * so that it does not get run again at the next boot.
* @hide
*/
public static final String ACTION_PRE_BOOT_COMPLETED =
@@ -3061,15 +3056,29 @@
public static final String ACTION_MANAGED_PROFILE_UNLOCKED =
"android.intent.action.MANAGED_PROFILE_UNLOCKED";
+ /** @hide */
+ public static final String ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED =
+ "android.intent.action.MANAGED_PROFILE_AVAILABILITY_CHANGED";
+
/**
- * Broadcast sent to the primary user when an associated managed profile's availability has
- * changed. This includes when the user toggles the profile's quiet mode. Carries an extra
+ * Broadcast sent to the primary user when an associated managed profile has become available.
+ * Currently this includes when the user disables quiet mode for the profile. Carries an extra
* {@link #EXTRA_USER} that specifies the UserHandle of the profile. When quiet mode is changed,
* this broadcast will carry a boolean extra {@link #EXTRA_QUIET_MODE} indicating the new state
* of quiet mode. This is only sent to registered receivers, not manifest receivers.
*/
- public static final String ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED =
- "android.intent.action.MANAGED_PROFILE_AVAILABILITY_CHANGED";
+ public static final String ACTION_MANAGED_PROFILE_AVAILABLE =
+ "android.intent.action.MANAGED_PROFILE_AVAILABLE";
+
+ /**
+ * Broadcast sent to the primary user when an associated managed profile has become unavailable.
+ * Currently this includes when the user enables quiet mode for the profile. Carries an extra
+ * {@link #EXTRA_USER} that specifies the UserHandle of the profile. When quiet mode is changed,
+ * this broadcast will carry a boolean extra {@link #EXTRA_QUIET_MODE} indicating the new state
+ * of quiet mode. This is only sent to registered receivers, not manifest receivers.
+ */
+ public static final String ACTION_MANAGED_PROFILE_UNAVAILABLE =
+ "android.intent.action.MANAGED_PROFILE_UNAVAILABLE";
/**
* Sent when the user taps on the clock widget in the system's "quick settings" area.
@@ -4170,6 +4179,9 @@
/**
* Optional boolean extra indicating whether quiet mode has been switched on or off.
+ * When a profile goes into quiet mode, all apps in the profile are killed and the
+ * profile user is stopped. Widgets originating from the profile are masked, and app
+ * launcher icons are grayed out.
*/
public static final String EXTRA_QUIET_MODE = "android.intent.extra.QUIET_MODE";
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index 3a17e23..22ab43b 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -449,11 +449,12 @@
}
/**
- * Modify priority of this filter. The default priority is 0. Positive
- * values will be before the default, lower values will be after it.
- * Applications must use a value that is larger than
- * {@link #SYSTEM_LOW_PRIORITY} and smaller than
- * {@link #SYSTEM_HIGH_PRIORITY} .
+ * Modify priority of this filter. This only affects receiver filters.
+ * The priority of activity filters are set in XML and cannot be changed
+ * programatically. The default priority is 0. Positive values will be
+ * before the default, lower values will be after it. Applications should
+ * use a value that is larger than {@link #SYSTEM_LOW_PRIORITY} and
+ * smaller than {@link #SYSTEM_HIGH_PRIORITY} .
*
* @param priority The new priority value.
*
@@ -883,6 +884,15 @@
return true;
}
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof AuthorityEntry) {
+ final AuthorityEntry other = (AuthorityEntry)obj;
+ return match(other);
+ }
+ return false;
+ }
+
/**
* Determine whether this AuthorityEntry matches the given data Uri.
* <em>Note that this comparison is case-sensitive, unlike formal
@@ -917,7 +927,7 @@
}
return MATCH_CATEGORY_HOST;
}
- };
+ }
/**
* Add a new Intent data "scheme specific part" to match against. The filter must
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 5da3c86..0f4cbbb 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -767,7 +767,11 @@
*/
public int lockTaskLaunchMode;
- public Layout layout;
+ /**
+ * Information about desired position and size of activity on the display when
+ * it is first started.
+ */
+ public WindowLayout windowLayout;
public ActivityInfo() {
}
@@ -788,7 +792,7 @@
parentActivityName = orig.parentActivityName;
maxRecents = orig.maxRecents;
lockTaskLaunchMode = orig.lockTaskLaunchMode;
- layout = orig.layout;
+ windowLayout = orig.windowLayout;
resizeMode = orig.resizeMode;
}
@@ -886,10 +890,10 @@
pw.println(prefix + "lockTaskLaunchMode="
+ lockTaskLaunchModeToString(lockTaskLaunchMode));
}
- if (layout != null) {
- pw.println(prefix + "defaultLayout=" + layout.width + "|"
- + layout.widthFraction + ", " + layout.height + "|"
- + layout.heightFraction + ", " + layout.gravity);
+ if (windowLayout != null) {
+ pw.println(prefix + "windowLayout=" + windowLayout.width + "|"
+ + windowLayout.widthFraction + ", " + windowLayout.height + "|"
+ + windowLayout.heightFraction + ", " + windowLayout.gravity);
}
pw.println(prefix + "resizeMode=" + resizeModeToString(resizeMode));
super.dumpBack(pw, prefix, flags);
@@ -922,14 +926,15 @@
dest.writeInt(persistableMode);
dest.writeInt(maxRecents);
dest.writeInt(lockTaskLaunchMode);
- if (layout != null) {
+ if (windowLayout != null) {
dest.writeInt(1);
- dest.writeInt(layout.width);
- dest.writeFloat(layout.widthFraction);
- dest.writeInt(layout.height);
- dest.writeFloat(layout.heightFraction);
- dest.writeInt(layout.gravity);
- dest.writeInt(layout.minimalSize);
+ dest.writeInt(windowLayout.width);
+ dest.writeFloat(windowLayout.widthFraction);
+ dest.writeInt(windowLayout.height);
+ dest.writeFloat(windowLayout.heightFraction);
+ dest.writeInt(windowLayout.gravity);
+ dest.writeInt(windowLayout.minimalWidth);
+ dest.writeInt(windowLayout.minimalHeight);
} else {
dest.writeInt(0);
}
@@ -964,36 +969,107 @@
maxRecents = source.readInt();
lockTaskLaunchMode = source.readInt();
if (source.readInt() == 1) {
- layout = new Layout(source);
+ windowLayout = new WindowLayout(source);
}
resizeMode = source.readInt();
}
- public static final class Layout {
- public Layout(int width, float widthFraction, int height, float heightFraction, int gravity,
- int minimalSize) {
+ /**
+ * Contains information about position and size of the activity on the display.
+ *
+ * Used in freeform mode to set desired position when activity is first launched.
+ * It describes how big the activity wants to be in both width and height,
+ * the minimal allowed size, and the gravity to be applied.
+ *
+ * @attr ref android.R.styleable#AndroidManifestLayout_defaultWidth
+ * @attr ref android.R.styleable#AndroidManifestLayout_defaultHeight
+ * @attr ref android.R.styleable#AndroidManifestLayout_gravity
+ * @attr ref android.R.styleable#AndroidManifestLayout_minimalWidth
+ * @attr ref android.R.styleable#AndroidManifestLayout_minimalHeight
+ */
+ public static final class WindowLayout {
+ public WindowLayout(int width, float widthFraction, int height, float heightFraction, int gravity,
+ int minimalWidth, int minimalHeight) {
this.width = width;
this.widthFraction = widthFraction;
this.height = height;
this.heightFraction = heightFraction;
this.gravity = gravity;
- this.minimalSize = minimalSize;
+ this.minimalWidth = minimalWidth;
+ this.minimalHeight = minimalHeight;
}
- Layout(Parcel source) {
+ WindowLayout(Parcel source) {
width = source.readInt();
widthFraction = source.readFloat();
height = source.readInt();
heightFraction = source.readFloat();
gravity = source.readInt();
- minimalSize = source.readInt();
+ minimalWidth = source.readInt();
+ minimalHeight = source.readInt();
}
+ /**
+ * Width of activity in pixels.
+ *
+ * @attr ref android.R.styleable#AndroidManifestLayout_defaultWidth
+ */
public final int width;
+
+ /**
+ * Width of activity as a fraction of available display width.
+ * If both {@link #width} and this value are set this one will be preferred.
+ *
+ * @attr ref android.R.styleable#AndroidManifestLayout_defaultWidth
+ */
public final float widthFraction;
+
+ /**
+ * Height of activity in pixels.
+ *
+ * @attr ref android.R.styleable#AndroidManifestLayout_defaultHeight
+ */
public final int height;
+
+ /**
+ * Height of activity as a fraction of available display height.
+ * If both {@link #height} and this value are set this one will be preferred.
+ *
+ * @attr ref android.R.styleable#AndroidManifestLayout_defaultHeight
+ */
public final float heightFraction;
+
+ /**
+ * Gravity of activity.
+ * Currently {@link android.view.Gravity#TOP}, {@link android.view.Gravity#BOTTOM},
+ * {@link android.view.Gravity#LEFT} and {@link android.view.Gravity#RIGHT} are supported.
+ *
+ * @attr ref android.R.styleable#AndroidManifestLayout_gravity
+ */
public final int gravity;
- public final int minimalSize;
+
+ /**
+ * Minimal width of activity in pixels to be able to display its content.
+ *
+ * <p><strong>NOTE:</strong> A task's root activity value is applied to all additional
+ * activities launched in the task. That is if the root activity of a task set minimal
+ * width, then the system will set the same minimal width on all other activities in the
+ * task. It will also ignore any other minimal width attributes of non-root activities.
+ *
+ * @attr ref android.R.styleable#AndroidManifestLayout_minimalWidth
+ */
+ public final int minimalWidth;
+
+ /**
+ * Minimal height of activity in pixels to be able to display its content.
+ *
+ * <p><strong>NOTE:</strong> A task's root activity value is applied to all additional
+ * activities launched in the task. That is if the root activity of a task set minimal
+ * height, then the system will set the same minimal height on all other activities in the
+ * task. It will also ignore any other minimal height attributes of non-root activities.
+ *
+ * @attr ref android.R.styleable#AndroidManifestLayout_minimalHeight
+ */
+ public final int minimalHeight;
}
}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index e3fb161..6fce36b 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -316,6 +316,12 @@
int getApplicationEnabledSetting(in String packageName, int userId);
/**
+ * Logs process start information (including APK hash) to the security log.
+ */
+ void logAppProcessStartIfNeeded(String processName, int uid, String seinfo, String apkFile,
+ int pid);
+
+ /**
* As per {@link android.content.pm.PackageManager#flushPackageRestrictionsAsUser}.
*/
void flushPackageRestrictionsAsUser(in int userId);
@@ -431,10 +437,9 @@
void performFstrimIfNeeded();
/**
- * Ask the package manager to extract packages if needed, to save
- * the VM unzipping the APK in memory during launch.
+ * Ask the package manager to update packages if needed.
*/
- void extractPackagesIfNeeded();
+ void updatePackagesIfNeeded();
/**
* Notify the package manager that a package is going to be used.
diff --git a/core/java/android/content/pm/IShortcutService.aidl b/core/java/android/content/pm/IShortcutService.aidl
index 8f9dcfc..31d377b 100644
--- a/core/java/android/content/pm/IShortcutService.aidl
+++ b/core/java/android/content/pm/IShortcutService.aidl
@@ -47,4 +47,8 @@
int getIconMaxDimensions(String packageName, int userId);
void resetThrottling(); // system only API for developer opsions
+
+ byte[] getBackupPayload(int user);
+
+ void applyRestore(in byte[] payload, int user);
}
\ No newline at end of file
diff --git a/core/java/android/content/pm/LauncherActivityInfo.java b/core/java/android/content/pm/LauncherActivityInfo.java
index a5617b4..40e1a9f 100644
--- a/core/java/android/content/pm/LauncherActivityInfo.java
+++ b/core/java/android/content/pm/LauncherActivityInfo.java
@@ -103,53 +103,22 @@
* @return The drawable associated with the activity.
*/
public Drawable getIcon(int density) {
- final int iconRes = mResolveInfo.getIconResource();
- Drawable icon = getDrawableForDensity(iconRes, density);
- // Get the default density icon
- if (icon == null) {
- icon = mResolveInfo.loadIcon(mPm);
- }
- return icon;
- }
-
- /**
- * Returns the icon for this activity, without any badging for the profile.
- * This function can get the icon no matter the icon needs to be badged or not.
- * @param density The preferred density of the icon, zero for default density. Use
- * density DPI values from {@link DisplayMetrics}.
- * @see #getBadgedIcon(int)
- * @see DisplayMetrics
- * @return The drawable associated with the activity.
- */
- private Drawable getOriginalIcon(int density) {
final int iconRes = mResolveInfo.getIconResourceInternal();
- Drawable icon = getDrawableForDensity(iconRes, density);
- // Get the default density icon
- if (icon == null) {
- icon = mResolveInfo.loadIcon(mPm);
- }
- return icon;
- }
-
- /**
- * Returns the drawable for this activity, without any badging for the profile.
- * @param iconRes id of the drawable.
- * @param density The preferred density of the icon, zero for default density. Use
- * density DPI values from {@link DisplayMetrics}.
- * @see DisplayMetrics
- * @return The drawable associated with the resource id.
- */
- private Drawable getDrawableForDensity(int iconRes, int density) {
+ Drawable icon = null;
// Get the preferred density icon from the app's resources
if (density != 0 && iconRes != 0) {
try {
final Resources resources
= mPm.getResourcesForApplication(mActivityInfo.applicationInfo);
- return resources.getDrawableForDensity(iconRes, density);
+ icon = resources.getDrawableForDensity(iconRes, density);
} catch (NameNotFoundException | Resources.NotFoundException exc) {
}
}
- return null;
+ // Get the default density icon
+ if (icon == null) {
+ icon = mResolveInfo.loadIcon(mPm);
+ }
+ return icon;
}
/**
@@ -201,7 +170,7 @@
* @return A badged icon for the activity.
*/
public Drawable getBadgedIcon(int density) {
- Drawable originalIcon = getOriginalIcon(density);
+ Drawable originalIcon = getIcon(density);
if (originalIcon instanceof BitmapDrawable) {
return mPm.getUserBadgedIcon(originalIcon, mUser);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index e89cbd7..39bc783 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -130,6 +130,7 @@
MATCH_DISABLED_COMPONENTS,
MATCH_DISABLED_UNTIL_USED_COMPONENTS,
MATCH_SYSTEM_ONLY,
+ MATCH_FACTORY_ONLY,
MATCH_DEBUG_TRIAGED_MISSING,
})
@Retention(RetentionPolicy.SOURCE)
@@ -415,6 +416,13 @@
public static final int MATCH_SYSTEM_ONLY = 0x00100000;
/**
+ * Internal {@link PackageInfo} flag: include only components on the system image.
+ * This will not return information on any unbundled update to system components.
+ * @hide
+ */
+ public static final int MATCH_FACTORY_ONLY = 0x00200000;
+
+ /**
* Internal flag used to indicate that a system component has done their
* homework and verified that they correctly handle packages and components
* that come and go over time. In particular:
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index db7ca2a..fe8db9f 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1096,13 +1096,19 @@
pkg.mSignatures = null;
pkg.mSigningKeys = null;
- collectCertificates(pkg, new File(pkg.baseCodePath), pkg.applicationInfo.flags, parseFlags);
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
+ try {
+ collectCertificates(
+ pkg, new File(pkg.baseCodePath), pkg.applicationInfo.flags, parseFlags);
- if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
- for (int i = 0; i < pkg.splitCodePaths.length; i++) {
- collectCertificates(pkg, new File(pkg.splitCodePaths[i]), pkg.splitFlags[i],
- parseFlags);
+ if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
+ for (int i = 0; i < pkg.splitCodePaths.length; i++) {
+ collectCertificates(
+ pkg, new File(pkg.splitCodePaths[i]), pkg.splitFlags[i], parseFlags);
+ }
}
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
@@ -1118,6 +1124,7 @@
Certificate[][] allSignersCerts = null;
Signature[] signatures = null;
try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "verifyV2");
allSignersCerts = ApkSignatureSchemeV2Verifier.verify(apkPath);
signatures = convertToSignatures(allSignersCerts);
// APK verified using APK Signature Scheme v2.
@@ -1130,6 +1137,8 @@
"Failed to collect certificates from " + apkPath
+ " using APK Signature Scheme v2",
e);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
if (verified) {
@@ -1158,10 +1167,15 @@
StrictJarFile jarFile = null;
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "strictJarFileCtor");
+ // Ignore signature stripping protections when verifying APKs from system partition.
+ // For those APKs we only care about extracting signer certificates, and don't care
+ // about verifying integrity.
+ boolean signatureSchemeRollbackProtectionsEnforced =
+ (parseFlags & PARSE_IS_SYSTEM) == 0;
jarFile = new StrictJarFile(
apkPath,
- !verified // whether to verify JAR signature
- );
+ !verified, // whether to verify JAR signature
+ signatureSchemeRollbackProtectionsEnforced);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
// Always verify manifest, regardless of source
@@ -1181,7 +1195,7 @@
}
// APK's integrity needs to be verified using JAR signature scheme.
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "buildVerifyList");
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "verifyV1");
final List<ZipEntry> toVerify = new ArrayList<>();
toVerify.add(manifestEntry);
@@ -1203,7 +1217,6 @@
toVerify.add(entry);
}
}
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
if (!codeFound && requireCode) {
throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
@@ -1213,7 +1226,6 @@
// Verify that entries are signed consistently with the first entry
// we encountered. Note that for splits, certificates may have
// already been populated during an earlier parse of a base APK.
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "verifyEntries");
for (ZipEntry entry : toVerify) {
final Certificate[][] entryCerts = loadCertificates(jarFile, entry);
if (ArrayUtils.isEmpty(entryCerts)) {
@@ -1295,7 +1307,12 @@
if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
// TODO: factor signature related items out of Package object
final Package tempPkg = new Package(null);
- collectCertificates(tempPkg, apkFile, 0 /*apkFlags*/, 0 /*flags*/);
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
+ try {
+ collectCertificates(tempPkg, apkFile, 0 /*apkFlags*/, 0 /*flags*/);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
signatures = tempPkg.mSignatures;
} else {
signatures = null;
@@ -3643,12 +3660,15 @@
int gravity = sw.getInt(
com.android.internal.R.styleable.AndroidManifestLayout_gravity,
Gravity.CENTER);
- int minimalSize = sw.getDimensionPixelSize(
- com.android.internal.R.styleable.AndroidManifestLayout_minimalSize,
+ int minimalWidth = sw.getDimensionPixelSize(
+ com.android.internal.R.styleable.AndroidManifestLayout_minimalWidth,
+ -1);
+ int minimalHeight = sw.getDimensionPixelSize(
+ com.android.internal.R.styleable.AndroidManifestLayout_minimalHeight,
-1);
sw.recycle();
- a.info.layout = new ActivityInfo.Layout(width, widthFraction,
- height, heightFraction, gravity, minimalSize);
+ a.info.windowLayout = new ActivityInfo.WindowLayout(width, widthFraction,
+ height, heightFraction, gravity, minimalWidth, minimalHeight);
}
private Activity parseActivityAlias(Package owner, Resources res,
@@ -3730,7 +3750,7 @@
info.uiOptions = target.info.uiOptions;
info.parentActivityName = target.info.parentActivityName;
info.maxRecents = target.info.maxRecents;
- info.layout = target.info.layout;
+ info.windowLayout = target.info.windowLayout;
info.resizeMode = target.info.resizeMode;
info.encryptionAware = info.directBootAware = target.info.directBootAware;
@@ -5523,7 +5543,7 @@
return pi;
}
- public final static class Instrumentation extends Component {
+ public final static class Instrumentation extends Component<IntentInfo> {
public final InstrumentationInfo info;
public Instrumentation(final ParsePackageItemArgs args, final InstrumentationInfo _info) {
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 984a960..65e0b92 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -113,6 +113,13 @@
public static final int PROTECTION_FLAG_PREINSTALLED = 0x400;
/**
+ * Additional flag for {@link #protectionLevel}, corresponding
+ * to the <code>setup</code> value of
+ * {@link android.R.attr#protectionLevel}.
+ */
+ public static final int PROTECTION_FLAG_SETUP = 0x800;
+
+ /**
* Mask for {@link #protectionLevel}: the basic protection type.
*/
public static final int PROTECTION_MASK_BASE = 0xf;
@@ -226,6 +233,9 @@
if ((level&PermissionInfo.PROTECTION_FLAG_PREINSTALLED) != 0) {
protLevel += "|preinstalled";
}
+ if ((level&PermissionInfo.PROTECTION_FLAG_SETUP) != 0) {
+ protLevel += "|setup";
+ }
return protLevel;
}
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index ae75e3f..1812575 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -34,20 +34,22 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+// TODO Enhance javadoc
/**
- * TODO Enhance javadoc
*
- * Represents a shortcut form an application.
+ * Represents a shortcut from an application.
*
- * Notes...
- * - If an {@link Icon} is of a resource, then we'll just persist the package name and resource ID.
+ * <p>Notes about icons:
+ * <ul>
+ * <li>If an {@link Icon} is a resource, the system keeps the package name and the resource ID.
+ * Otherwise, the bitmap is fetched when it's registered to ShortcutManager,
+ * then shrunk if necessary, and persisted.
+ * <li>The system disallows byte[] icons, because they can easily go over the binder size limit.
+ * </ul>
*
- * Otherwise, the bitmap will be fetched when it's registered to ShortcutManager, then *shrunk*
- * if necessary, and persisted.
- *
- * We will disallow byte[] icons, because they can easily go over binder size limit.
+ * @see {@link ShortcutManager}.
*/
-public class ShortcutInfo implements Parcelable {
+public final class ShortcutInfo implements Parcelable {
/* @hide */
public static final int FLAG_DYNAMIC = 1 << 0;
@@ -118,6 +120,9 @@
@NonNull
private String mTitle;
+ @Nullable
+ private String mText;
+
/**
* Intent *with extras removed*.
*/
@@ -157,6 +162,7 @@
mActivityComponent = b.mActivityComponent;
mIcon = b.mIcon;
mTitle = b.mTitle;
+ mText = b.mText;
mIntent = b.mIntent;
if (mIntent != null) {
final Bundle intentExtras = mIntent.getExtras();
@@ -176,6 +182,7 @@
* @hide
*/
public void enforceMandatoryFields() {
+ Preconditions.checkStringNotEmpty(mId, "Shortcut ID must be provided");
Preconditions.checkStringNotEmpty(mTitle, "Shortcut title must be provided");
Preconditions.checkNotNull(mIntent, "Shortcut Intent must be provided");
}
@@ -195,16 +202,17 @@
if ((cloneFlags & CLONE_REMOVE_ICON) == 0) {
mIcon = source.mIcon;
mBitmapPath = source.mBitmapPath;
+ mIconResourceId = source.mIconResourceId;
}
mTitle = source.mTitle;
+ mText = source.mText;
if ((cloneFlags & CLONE_REMOVE_INTENT) == 0) {
mIntent = source.mIntent;
mIntentPersistableExtras = source.mIntentPersistableExtras;
}
mWeight = source.mWeight;
mExtras = source.mExtras;
- mIconResourceId = source.mIconResourceId;
} else {
// Set this bit.
mFlags |= FLAG_KEY_FIELDS_ONLY;
@@ -244,6 +252,9 @@
if (source.mTitle != null) {
mTitle = source.mTitle;
}
+ if (source.mText != null) {
+ mText = source.mText;
+ }
if (source.mIntent != null) {
mIntent = source.mIntent;
mIntentPersistableExtras = source.mIntentPersistableExtras;
@@ -305,6 +316,8 @@
private String mTitle;
+ private String mText;
+
private Intent mIntent;
private int mWeight;
@@ -360,6 +373,9 @@
/**
* Sets the title of a shortcut. This is a mandatory field.
+ *
+ * <p>This field is intended for a concise description of a shortcut displayed under
+ * an icon. The recommend max length is 10 characters.
*/
@NonNull
public Builder setTitle(@NonNull String title) {
@@ -368,6 +384,18 @@
}
/**
+ * Sets the text of a shortcut. This is an optional field.
+ *
+ * <p>This field is intended to be more descriptive than the shortcut title.
+ * The recommend max length is 25 characters.
+ */
+ @NonNull
+ public Builder setText(@NonNull String text) {
+ mText = Preconditions.checkStringNotEmpty(text, "text");
+ return this;
+ }
+
+ /**
* Sets the intent of a shortcut. This is a mandatory field. The extras must only contain
* persistable information. (See {@link PersistableBundle}).
*/
@@ -457,6 +485,14 @@
}
/**
+ * Return the shortcut text.
+ */
+ @Nullable
+ public String getText() {
+ return mText;
+ }
+
+ /**
* Return the intent.
*
* <p>All shortcuts must have an intent, but this method will return null when
@@ -630,6 +666,7 @@
mActivityComponent = source.readParcelable(cl);
mIcon = source.readParcelable(cl);
mTitle = source.readString();
+ mText = source.readString();
mIntent = source.readParcelable(cl);
mIntentPersistableExtras = source.readParcelable(cl);
mWeight = source.readInt();
@@ -647,6 +684,7 @@
dest.writeParcelable(mActivityComponent, flags);
dest.writeParcelable(mIcon, flags);
dest.writeString(mTitle);
+ dest.writeString(mText);
dest.writeParcelable(mIntent, flags);
dest.writeParcelable(mIntentPersistableExtras, flags);
dest.writeInt(mWeight);
@@ -708,6 +746,9 @@
sb.append(", title=");
sb.append(secure ? "***" : mTitle);
+ sb.append(", text=");
+ sb.append(secure ? "***" : mText);
+
sb.append(", icon=");
sb.append(mIcon);
@@ -744,7 +785,8 @@
/** @hide */
public ShortcutInfo(String id, String packageName, ComponentName activityComponent,
- Icon icon, String title, Intent intent, PersistableBundle intentPersistableExtras,
+ Icon icon, String title, String text, Intent intent,
+ PersistableBundle intentPersistableExtras,
int weight, PersistableBundle extras, long lastChangedTimestamp,
int flags, int iconResId, String bitmapPath) {
mId = id;
@@ -752,6 +794,7 @@
mActivityComponent = activityComponent;
mIcon = icon;
mTitle = title;
+ mText = text;
mIntent = intent;
mIntentPersistableExtras = intentPersistableExtras;
mWeight = weight;
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index b247f65..e4a98b5 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -19,15 +19,13 @@
import android.content.Context;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import java.util.List;
+// TODO Enhance javadoc
/**
- * TODO Enhance javadoc
- *
* {@link ShortcutManager} manages shortcuts created by applications.
*
* <h3>Dynamic shortcuts and pinned shortcuts</h3>
@@ -66,15 +64,18 @@
* {@link #getRemainingCallCount()} times until the rate-limiting counter is reset,
* which happens at a certain time every day.
*
- * <p>An applications can use {@link #getRateLimitResetTime()} to get the next reset time.
+ * <p>An application can use {@link #getRateLimitResetTime()} to get the next reset time.
+ *
+ * <p>For testing purposes, use "Developer Options" (found in the Settings menu) to reset the
+ * internal rate-limiting counter. Automated tests can use the following ADB shell command to
+ * achieve the same effect:</p>
+ * <pre>adb shell cmd shortcut reset-throttling</pre>
*
* <h3>Backup and Restore</h3>
*
* Shortcuts will be backed up and restored across devices. This means all information, including
* IDs, must be meaningful on a different device.
*
- * TODO: Define a Broadcast to let apps update shortcuts on a restored device.
- *
* <h3>APIs for launcher</h3>
*
* Launcher applications should use {@link LauncherApps} to get shortcuts that are published from
diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java
index 4255582..d57f2e6e 100644
--- a/core/java/android/content/pm/ShortcutServiceInternal.java
+++ b/core/java/android/content/pm/ShortcutServiceInternal.java
@@ -40,28 +40,37 @@
}
public abstract List<ShortcutInfo>
- getShortcuts(@NonNull String callingPackage, long changedSince,
+ getShortcuts(int launcherUserId,
+ @NonNull String callingPackage, long changedSince,
@Nullable String packageName, @Nullable ComponentName componentName,
@ShortcutQuery.QueryFlags int flags,
int userId);
public abstract List<ShortcutInfo>
- getShortcutInfo(@NonNull String callingPackage,
+ getShortcutInfo(int launcherUserId, @NonNull String callingPackage,
@NonNull String packageName, @Nullable List<String> ids, int userId);
- public abstract void pinShortcuts(@NonNull String callingPackage, @NonNull String packageName,
+
+ public abstract boolean
+ isPinnedByCaller(int launcherUserId, @NonNull String callingPackage,
+ @NonNull String packageName, @NonNull String id, int userId);
+
+ public abstract void pinShortcuts(int launcherUserId,
+ @NonNull String callingPackage, @NonNull String packageName,
@NonNull List<String> shortcutIds, int userId);
- public abstract Intent createShortcutIntent(@NonNull String callingPackage,
+ public abstract Intent createShortcutIntent(int launcherUserId, @NonNull String callingPackage,
@NonNull String packageName, @NonNull String shortcutId, int userId);
public abstract void addListener(@NonNull ShortcutChangeListener listener);
- public abstract int getShortcutIconResId(@NonNull String callingPackage,
+ public abstract int getShortcutIconResId(int launcherUserId, @NonNull String callingPackage,
@NonNull ShortcutInfo shortcut, int userId);
- public abstract ParcelFileDescriptor getShortcutIconFd(@NonNull String callingPackage,
+ public abstract ParcelFileDescriptor getShortcutIconFd(int launcherUserId,
+ @NonNull String callingPackage,
@NonNull ShortcutInfo shortcut, int userId);
- public abstract boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId);
+ public abstract boolean hasShortcutHostPermission(int launcherUserId,
+ @NonNull String callingPackage);
}
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index e8a3438..dd3a36c 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -96,6 +96,7 @@
public int flags;
public long creationTime;
public long lastLoggedInTime;
+ public String lastLoggedInFingerprint;
public int profileGroupId;
public int restrictedProfileParentId;
@@ -214,6 +215,7 @@
serialNumber = orig.serialNumber;
creationTime = orig.creationTime;
lastLoggedInTime = orig.lastLoggedInTime;
+ lastLoggedInFingerprint = orig.lastLoggedInFingerprint;
partial = orig.partial;
profileGroupId = orig.profileGroupId;
restrictedProfileParentId = orig.restrictedProfileParentId;
@@ -241,6 +243,7 @@
dest.writeInt(serialNumber);
dest.writeLong(creationTime);
dest.writeLong(lastLoggedInTime);
+ dest.writeString(lastLoggedInFingerprint);
dest.writeInt(partial ? 1 : 0);
dest.writeInt(profileGroupId);
dest.writeInt(guestToRemove ? 1 : 0);
@@ -265,6 +268,7 @@
serialNumber = source.readInt();
creationTime = source.readLong();
lastLoggedInTime = source.readLong();
+ lastLoggedInFingerprint = source.readString();
partial = source.readInt() != 0;
profileGroupId = source.readInt();
guestToRemove = source.readInt() != 0;
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 387fda7..93fe73b 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -714,12 +714,11 @@
* the resource ID passed here is an alias to another Drawable resource.
* This means that if the density configuration of the alias resource
* is different than the actual resource, the density of the returned
- * Drawable would be incorrect, resulting in bad scaling. To work
- * around this, you can instead retrieve the Drawable through
- * {@link TypedArray#getDrawable TypedArray.getDrawable}. Use
- * {@link android.content.Context#obtainStyledAttributes(int[])
- * Context.obtainStyledAttributes} with
- * an array containing the resource ID of interest to create the TypedArray.</p>
+ * Drawable would be incorrect, resulting in bad scaling. To work
+ * around this, you can instead manually resolve the aliased reference
+ * by using {@link #getValue(int, TypedValue, boolean)} and passing
+ * {@code true} for {@code resolveRefs}. The resulting
+ * {@link TypedValue#resourceId} value may be passed to this method.</p>
*
* <p class="note"><strong>Note:</strong> To obtain a themed drawable, use
* {@link android.content.Context#getDrawable(int) Context.getDrawable(int)}
diff --git a/core/java/android/ddm/DdmHandleHello.java b/core/java/android/ddm/DdmHandleHello.java
index 2dce425..b2288fc 100644
--- a/core/java/android/ddm/DdmHandleHello.java
+++ b/core/java/android/ddm/DdmHandleHello.java
@@ -136,12 +136,14 @@
}
String vmFlags = "CheckJNI="
+ (vmRuntime.isCheckJniEnabled() ? "true" : "false");
+ boolean isNativeDebuggable = vmRuntime.isNativeDebuggable();
ByteBuffer out = ByteBuffer.allocate(28
+ vmIdent.length() * 2
+ appName.length() * 2
+ instructionSetDescription.length() * 2
- + vmFlags.length() * 2);
+ + vmFlags.length() * 2
+ + 1);
out.order(ChunkHandler.CHUNK_ORDER);
out.putInt(DdmServer.CLIENT_PROTOCOL_VERSION);
out.putInt(android.os.Process.myPid());
@@ -154,6 +156,7 @@
putString(out, instructionSetDescription);
out.putInt(vmFlags.length());
putString(out, vmFlags);
+ out.put((byte)(isNativeDebuggable ? 1 : 0));
Chunk reply = new Chunk(CHUNK_HELO, out);
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 3dbe437..acf0677 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -76,7 +76,7 @@
* <li>If necessary, modify the returned {@link Camera.Parameters} object and call
* {@link #setParameters(Camera.Parameters)}.
*
- * <li>If desired, call {@link #setDisplayOrientation(int)}.
+ * <li>Call {@link #setDisplayOrientation(int)} to ensure correct orientation of preview.
*
* <li><b>Important</b>: Pass a fully initialized {@link SurfaceHolder} to
* {@link #setPreviewDisplay(SurfaceHolder)}. Without a surface, the camera
@@ -1511,9 +1511,15 @@
* <p>Starting from API level 14, this method can be called when preview is
* active.
*
+ * <p><b>Note: </b>Before API level 24, the default value for orientation is 0. Starting in
+ * API level 24, the default orientation will be such that applications in forced-landscape mode
+ * will have correct preview orientation, which may be either a default of 0 or
+ * 180. Applications that operate in portrait mode or allow for changing orientation must still
+ * call this method after each orientation change to ensure correct preview display in all
+ * cases.</p>
+ *
* @param degrees the angle that the picture will be rotated clockwise.
- * Valid values are 0, 90, 180, and 270. The starting
- * position is 0 (landscape).
+ * Valid values are 0, 90, 180, and 270.
* @see #setPreviewDisplay(SurfaceHolder)
*/
public native final void setDisplayOrientation(int degrees);
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 63ee8d2..cc82eb6 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -186,7 +186,7 @@
* @see #TYPE_LINEAR_ACCELERATION
*/
public static final String STRING_TYPE_LINEAR_ACCELERATION =
- "android.sensor.linear_acceleration";
+ "android.sensor.linear_acceleration";
/**
* A constant describing a rotation vector sensor type.
@@ -229,7 +229,7 @@
* @see #TYPE_AMBIENT_TEMPERATURE
*/
public static final String STRING_TYPE_AMBIENT_TEMPERATURE =
- "android.sensor.ambient_temperature";
+ "android.sensor.ambient_temperature";
/**
* A constant describing an uncalibrated magnetic field sensor type.
@@ -254,7 +254,7 @@
* @see #TYPE_MAGNETIC_FIELD_UNCALIBRATED
*/
public static final String STRING_TYPE_MAGNETIC_FIELD_UNCALIBRATED =
- "android.sensor.magnetic_field_uncalibrated";
+ "android.sensor.magnetic_field_uncalibrated";
/**
* A constant describing an uncalibrated rotation vector sensor type.
@@ -280,7 +280,7 @@
* @see #TYPE_GAME_ROTATION_VECTOR
*/
public static final String STRING_TYPE_GAME_ROTATION_VECTOR =
- "android.sensor.game_rotation_vector";
+ "android.sensor.game_rotation_vector";
/**
* A constant describing an uncalibrated gyroscope sensor type.
@@ -302,7 +302,7 @@
* @see #TYPE_GYROSCOPE_UNCALIBRATED
*/
public static final String STRING_TYPE_GYROSCOPE_UNCALIBRATED =
- "android.sensor.gyroscope_uncalibrated";
+ "android.sensor.gyroscope_uncalibrated";
/**
* A constant describing a significant motion trigger sensor.
@@ -324,7 +324,7 @@
* @see #TYPE_SIGNIFICANT_MOTION
*/
public static final String STRING_TYPE_SIGNIFICANT_MOTION =
- "android.sensor.significant_motion";
+ "android.sensor.significant_motion";
/**
* A constant describing a step detector sensor.
@@ -391,7 +391,7 @@
* @see #TYPE_GEOMAGNETIC_ROTATION_VECTOR
*/
public static final String STRING_TYPE_GEOMAGNETIC_ROTATION_VECTOR =
- "android.sensor.geomagnetic_rotation_vector";
+ "android.sensor.geomagnetic_rotation_vector";
/**
* A constant describing a heart rate monitor.
@@ -431,7 +431,7 @@
* A constant string describing a wake up tilt detector sensor type.
*
* @hide
- * @see #TYPE_WAKE_UP_TILT_DETECTOR
+ * @see #TYPE_TILT_DETECTOR
*/
public static final String SENSOR_STRING_TYPE_TILT_DETECTOR =
"android.sensor.tilt_detector";
@@ -495,7 +495,7 @@
*/
public static final String STRING_TYPE_GLANCE_GESTURE = "android.sensor.glance_gesture";
- /**
+ /**
* A constant describing a pick up sensor.
*
* A sensor of this type triggers when the device is picked up regardless of wherever it was
@@ -514,7 +514,7 @@
*/
public static final String STRING_TYPE_PICK_UP_GESTURE = "android.sensor.pick_up_gesture";
- /**
+ /**
* A constant describing a wrist tilt gesture sensor.
*
* A sensor of this type triggers when the device face is tilted towards the user.
@@ -553,7 +553,7 @@
*/
public static final String STRING_TYPE_DEVICE_ORIENTATION = "android.sensor.device_orientation";
- /**
+ /**
* A constant describing a pose sensor with 6 degrees of freedom.
*
* Similar to {@link #TYPE_ROTATION_VECTOR}, with additional delta
@@ -578,7 +578,7 @@
*/
public static final String STRING_TYPE_POSE_6DOF = "android.sensor.pose_6dof";
- /**
+ /**
* A constant describing a stationary detect sensor.
*
* See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
@@ -593,7 +593,7 @@
*/
public static final String STRING_TYPE_STATIONARY_DETECT = "android.sensor.stationary_detect";
- /**
+ /**
* A constant describing a motion detect sensor.
*
* See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
@@ -608,7 +608,7 @@
*/
public static final String STRING_TYPE_MOTION_DETECT = "android.sensor.motion_detect";
- /**
+ /**
* A constant describing a motion detect sensor.
*
* See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
@@ -706,6 +706,14 @@
private static final int DATA_INJECTION_MASK = 0x10;
private static final int DATA_INJECTION_SHIFT = 4;
+ // MASK for dynamic sensor (sensor that added during runtime), bit 6.
+ private static final int DYNAMIC_SENSOR_MASK = 0x20;
+ private static final int DYNAMIC_SENSOR_SHIFT = 5;
+
+ // MASK for indication bit of sensor additional information support (bit 7).
+ private static final int ADDITIONAL_INFO_MASK = 0x40;
+ private static final int ADDITIONAL_INFO_SHIFT = 6;
+
// TODO(): The following arrays are fragile and error-prone. This needs to be refactored.
// Note: This needs to be updated, whenever a new sensor is added.
@@ -887,13 +895,37 @@
}
/**
- * @return The type of this sensor as a string.
+ * @return The UUID of the sensor. If the sensor does not support UUID, the returned value will
+ * be an all zero UUID; if the sensor's combination of type and name is guaranteed to be unique
+ * in system, the return value will be an all "F" UUID.
+ *
+ * @hide
*/
+ @SystemApi
public UUID getUuid() {
return mUuid;
}
/**
+ * @return The unique id of sensor. Return value of 0 means this sensor does not support UUID;
+ * return value of -1 means this sensor can be uniquely identified in system by combination of
+ * its type and name.
+ */
+ public int getId() {
+ if (mUuid == ALL_0_UUID) {
+ return 0;
+ } else if (mUuid == ALL_F_UUID) {
+ return -1;
+ } else {
+ int id = Math.abs(mUuid.hashCode()) + 1;
+ if (id <= 0) { // catch corner case when hash is Integer.MIN_VALUE and Integer.MAX_VALUE
+ id = 1;
+ }
+ return id;
+ }
+ }
+
+ /**
* @hide
* @return The permission required to access this sensor. If empty, no permission is required.
*/
@@ -961,6 +993,26 @@
}
/**
+ * Returns true if the sensor is a dynamic sensor.
+ *
+ * @return <code>true</code> if the sensor is a dynamic sensor (sensor added at runtime).
+ * @see SensorManager.DynamicSensorCallback
+ */
+ public boolean isDynamicSensor() {
+ return (mFlags & DYNAMIC_SENSOR_MASK) != 0;
+ }
+
+ /**
+ * Returns true if the sensor supports sensor additional information API
+ *
+ * @return <code>true</code> if the sensor supports sensor additional information API
+ * @see SensorAdditionalInfo
+ */
+ public boolean isAdditionalInfoSupported() {
+ return (mFlags & ADDITIONAL_INFO_MASK) != 0;
+ }
+
+ /**
* Returns true if the sensor supports data injection when the
* HAL is set to data injection mode.
*
@@ -986,6 +1038,10 @@
+ ", power=" + mPower + ", minDelay=" + mMinDelay + "}";
}
+ //special UUID hash constant
+ private final static UUID ALL_0_UUID = new UUID(0,0);
+ private final static UUID ALL_F_UUID = new UUID(~0L, ~0L);
+
/**
* Sets the Type associated with the sensor.
* NOTE: to be used only by native bindings in SensorManager.
diff --git a/core/java/android/hardware/SensorAdditionalInfo.java b/core/java/android/hardware/SensorAdditionalInfo.java
index 8e5b8a3..572a287 100644
--- a/core/java/android/hardware/SensorAdditionalInfo.java
+++ b/core/java/android/hardware/SensorAdditionalInfo.java
@@ -27,7 +27,7 @@
* android.hardware.SensorEventCallback#onSensorAdditionalInfo onSensorAdditionalInfo}.
*
* @see SensorManager
- * @see SensorEventListener3
+ * @see SensorEventCallback
* @see Sensor
*
*/
@@ -106,7 +106,7 @@
* such as accelerometer, gyro, etc.
*
* Payload:
- * floatValues[0..11]: First 3 rows of a homogenous matrix in row major order that captures
+ * floatValues[0..11]: First 3 rows of a homogeneous matrix in row major order that captures
* any linear transformation, including rotation, scaling, shear, shift.
*/
public static final int TYPE_VEC3_CALIBRATION = 0x10002;
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index 35c96f7..0d96b8e 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -18,7 +18,7 @@
/**
* This class represents a {@link android.hardware.Sensor Sensor} event and
- * holds informations such as the sensor's type, the time-stamp, accuracy and of
+ * holds information such as the sensor's type, the time-stamp, accuracy and of
* course the sensor's {@link SensorEvent#values data}.
*
* <p>
@@ -163,7 +163,7 @@
* </ul>
* <p>
* Typically the output of the gyroscope is integrated over time to
- * calculate a rotation describing the change of angles over the timestep,
+ * calculate a rotation describing the change of angles over the time step,
* for example:
* </p>
*
@@ -173,7 +173,7 @@
* private float timestamp;
*
* public void onSensorChanged(SensorEvent event) {
- * // This timestep's delta rotation to be multiplied by the current rotation
+ * // This time step's delta rotation to be multiplied by the current rotation
* // after computing it from the gyro sample data.
* if (timestamp != 0) {
* final float dT = (event.timestamp - timestamp) * NS2S;
@@ -192,8 +192,8 @@
* axisZ /= omegaMagnitude;
* }
*
- * // Integrate around this axis with the angular speed by the timestep
- * // in order to get a delta rotation from this sample over the timestep
+ * // Integrate around this axis with the angular speed by the time step
+ * // in order to get a delta rotation from this sample over the time step
* // We will convert this axis-angle representation of the delta rotation
* // into a quaternion before turning it into the rotation matrix.
* float thetaOverTwo = omegaMagnitude * dT / 2.0f;
@@ -433,9 +433,9 @@
* Each field is a component of the estimated hard iron calibration.
* The values are in micro-Tesla (uT).
* </p>
- * <p> Hard iron - These distortions arise due to the magnetized iron, steel or permanenet
+ * <p> Hard iron - These distortions arise due to the magnetized iron, steel or permanent
* magnets on the device.
- * Soft iron - These distortions arise due to the interaction with the earth's magentic
+ * Soft iron - These distortions arise due to the interaction with the earth's magnetic
* field.
* </p>
* <h4> {@link android.hardware.Sensor#TYPE_GAME_ROTATION_VECTOR}:</h4>
@@ -508,14 +508,14 @@
*
*
* <ul>
- * <li> values[0]: x*sin(θ/2) </li>
- * <li> values[1]: y*sin(θ/2) </li>
- * <li> values[2]: z*sin(θ/2) </li>
- * <li> values[3]: cos(θ/2) </li>
+ * <li> values[0]: x*sin(θ/2) </li>
+ * <li> values[1]: y*sin(θ/2) </li>
+ * <li> values[2]: z*sin(θ/2) </li>
+ * <li> values[3]: cos(θ/2) </li>
*
*
* <li> values[4]: Translation along x axis from an arbitrary origin. </li>
- * li> values[5]: Translation along y axis from an arbitrary origin. </li>
+ * <li> values[5]: Translation along y axis from an arbitrary origin. </li>
* <li> values[6]: Translation along z axis from an arbitrary origin. </li>
*
* <li> values[7]: Delta quaternion rotation x*sin(θ/2) </li>
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 5684aa5..a20307a 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -884,7 +884,7 @@
* Used for receiving notifications from the SensorManager when dynamic sensors are connected or
* disconnected.
*/
- public static abstract class DynamicSensorConnectionCallback {
+ public static abstract class DynamicSensorCallback {
/**
* Called when there is a dynamic sensor being connected to the system.
*
@@ -902,62 +902,73 @@
/**
- * Add a {@link android.hardware.SensorManager.DynamicSensorConnectionCallback
- * DynamicSensorConnectionCallback} to receive dynamic sensor connection callbacks. Repeat
+ * Add a {@link android.hardware.SensorManager.DynamicSensorCallback
+ * DynamicSensorCallback} to receive dynamic sensor connection callbacks. Repeat
* registration with the already registered callback object will have no additional effect.
*
* @param callback An object that implements the
- * {@link android.hardware.SensorManager.DynamicSensorConnectionCallback
- * DynamicSensorConnectionCallback}
+ * {@link android.hardware.SensorManager.DynamicSensorCallback
+ * DynamicSensorCallback}
* interface for receiving callbacks.
- * @see #addDynamicSensorCallback(DynamicSensorConnectionCallback, Handler)
+ * @see #addDynamicSensorCallback(DynamicSensorCallback, Handler)
*
* @throws IllegalArgumentException when callback is null.
*/
- public void registerDynamicSensorCallback(DynamicSensorConnectionCallback callback) {
+ public void registerDynamicSensorCallback(DynamicSensorCallback callback) {
registerDynamicSensorCallback(callback, null);
}
/**
- * Add a {@link android.hardware.SensorManager.DynamicSensorConnectionCallback
- * DynamicSensorConnectionCallback} to receive dynamic sensor connection callbacks. Repeat
+ * Add a {@link android.hardware.SensorManager.DynamicSensorCallback
+ * DynamicSensorCallback} to receive dynamic sensor connection callbacks. Repeat
* registration with the already registered callback object will have no additional effect.
*
* @param callback An object that implements the
- * {@link android.hardware.SensorManager.DynamicSensorConnectionCallback
- * DynamicSensorConnectionCallback} interface for receiving callbacks.
+ * {@link android.hardware.SensorManager.DynamicSensorCallback
+ * DynamicSensorCallback} interface for receiving callbacks.
* @param handler The {@link android.os.Handler Handler} the {@link
- * android.hardware.SensorManager.DynamicSensorConnectionCallback
+ * android.hardware.SensorManager.DynamicSensorCallback
* sensor connection events} will be delivered to.
*
* @throws IllegalArgumentException when callback is null.
*/
public void registerDynamicSensorCallback(
- DynamicSensorConnectionCallback callback, Handler handler) {
+ DynamicSensorCallback callback, Handler handler) {
registerDynamicSensorCallbackImpl(callback, handler);
}
/**
- * Remove a {@link android.hardware.SensorManager.DynamicSensorConnectionCallback
- * DynamicSensorConnectionCallback} to stop sending dynamic sensor connection events to that
+ * Remove a {@link android.hardware.SensorManager.DynamicSensorCallback
+ * DynamicSensorCallback} to stop sending dynamic sensor connection events to that
* callback.
*
* @param callback An object that implements the
- * {@link android.hardware.SensorManager.DynamicSensorConnectionCallback
- * DynamicSensorConnectionCallback}
+ * {@link android.hardware.SensorManager.DynamicSensorCallback
+ * DynamicSensorCallback}
* interface for receiving callbacks.
*/
- public void unregisterDynamicSensorCallback(DynamicSensorConnectionCallback callback) {
+ public void unregisterDynamicSensorCallback(DynamicSensorCallback callback) {
unregisterDynamicSensorCallbackImpl(callback);
}
+ /**
+ * Tell if dynamic sensor discovery feature is supported by system.
+ *
+ * @return <code>true</code> if dynamic sensor discovery is supported, <code>false</code>
+ * otherwise.
+ */
+ public boolean isDynamicSensorDiscoverySupported() {
+ List<Sensor> sensors = getSensorList(Sensor.TYPE_DYNAMIC_SENSOR_META);
+ return sensors.size() > 0;
+ }
+
/** @hide */
protected abstract void registerDynamicSensorCallbackImpl(
- DynamicSensorConnectionCallback callback, Handler handler);
+ DynamicSensorCallback callback, Handler handler);
/** @hide */
protected abstract void unregisterDynamicSensorCallbackImpl(
- DynamicSensorConnectionCallback callback);
+ DynamicSensorCallback callback);
/**
* <p>
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index b75e653..259ca03 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -1,4 +1,4 @@
- /*
+/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -31,12 +31,15 @@
import android.util.SparseIntArray;
import dalvik.system.CloseGuard;
+import com.android.internal.annotations.GuardedBy;
+
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+
/**
* Sensor manager implementation that communicates with the built-in
* system sensors.
@@ -55,7 +58,9 @@
private static native boolean nativeIsDataInjectionEnabled(long nativeInstance);
private static final Object sLock = new Object();
- private static boolean sSensorModuleInitialized = false;
+ @GuardedBy("sLock")
+ private static boolean sNativeClassInited = false;
+ @GuardedBy("sLock")
private static InjectEventQueue sInjectEventQueue = null;
private final ArrayList<Sensor> mFullSensorsList = new ArrayList<>();
@@ -71,7 +76,7 @@
new HashMap<TriggerEventListener, TriggerEventQueue>();
// Dynamic Sensor callbacks
- private HashMap<DynamicSensorConnectionCallback, Handler>
+ private HashMap<DynamicSensorCallback, Handler>
mDynamicSensorCallbacks = new HashMap<>();
private BroadcastReceiver mDynamicSensorBroadcastReceiver;
@@ -84,8 +89,8 @@
/** {@hide} */
public SystemSensorManager(Context context, Looper mainLooper) {
synchronized(sLock) {
- if (!sSensorModuleInitialized) {
- sSensorModuleInitialized = true;
+ if (!sNativeClassInited) {
+ sNativeClassInited = true;
nativeClassInit();
}
}
@@ -115,7 +120,7 @@
@Override
protected List<Sensor> getFullDynamicSensorList() {
// only set up broadcast receiver if the application tries to find dynamic sensors or
- // explicitly register a DynamicSensorConnectionCallback
+ // explicitly register a DynamicSensorCallback
setupDynamicSensorBroadcastReceiver();
updateDynamicSensorList();
return mFullDynamicSensorsList;
@@ -349,9 +354,9 @@
Handler mainHandler = new Handler(mContext.getMainLooper());
- for (Map.Entry<DynamicSensorConnectionCallback, Handler> entry :
+ for (Map.Entry<DynamicSensorCallback, Handler> entry :
mDynamicSensorCallbacks.entrySet()) {
- final DynamicSensorConnectionCallback callback = entry.getKey();
+ final DynamicSensorCallback callback = entry.getKey();
Handler handler =
entry.getValue() == null ? mainHandler : entry.getValue();
@@ -408,7 +413,7 @@
/** @hide */
protected void registerDynamicSensorCallbackImpl(
- DynamicSensorConnectionCallback callback, Handler handler) {
+ DynamicSensorCallback callback, Handler handler) {
if (DEBUG_DYNAMIC_SENSOR) {
Log.i(TAG, "DYNS Register dynamic sensor callback");
}
@@ -427,7 +432,7 @@
/** @hide */
protected void unregisterDynamicSensorCallbackImpl(
- DynamicSensorConnectionCallback callback) {
+ DynamicSensorCallback callback) {
if (DEBUG_DYNAMIC_SENSOR) {
Log.i(TAG, "Removing dynamic sensor listerner");
}
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 4add962..d06e08b 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -2025,11 +2025,20 @@
* produced in response to a capture request submitted
* while in HDR mode.</p>
* <p>Since substantial post-processing is generally needed to
- * produce an HDR image, only YUV and JPEG outputs are
- * supported for LIMITED/FULL device HDR captures, and only
- * JPEG outputs are supported for LEGACY HDR
- * captures. Using a RAW output for HDR capture is not
+ * produce an HDR image, only YUV, PRIVATE, and JPEG
+ * outputs are supported for LIMITED/FULL device HDR
+ * captures, and only JPEG outputs are supported for LEGACY
+ * HDR captures. Using a RAW output for HDR capture is not
* supported.</p>
+ * <p>Some devices may also support always-on HDR, which
+ * applies HDR processing at full frame rate. For these
+ * devices, intents other than STILL_CAPTURE will also
+ * produce an HDR output with no frame rate impact compared
+ * to normal operation, though the quality may be lower
+ * than for STILL_CAPTURE intents.</p>
+ * <p>If SCENE_MODE_HDR is used with unsupported output types
+ * or capture intents, the images captured will be as if
+ * the SCENE_MODE was not enabled at all.</p>
*
* @see CaptureRequest#CONTROL_CAPTURE_INTENT
* @see CaptureRequest#CONTROL_SCENE_MODE
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 5748726..4f41e1c 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -3373,7 +3373,7 @@
/**
* <p>Maximum raw value output by sensor for this frame.</p>
- * <p>Since the android.sensor.blackLevel may change for different
+ * <p>Since the {@link CameraCharacteristics#SENSOR_BLACK_LEVEL_PATTERN android.sensor.blackLevelPattern} may change for different
* capture settings (e.g., {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}), the white
* level will change accordingly. This key is similar to
* {@link CameraCharacteristics#SENSOR_INFO_WHITE_LEVEL android.sensor.info.whiteLevel}, but specifies the camera device
@@ -3385,6 +3385,7 @@
* >= 0</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
+ * @see CameraCharacteristics#SENSOR_BLACK_LEVEL_PATTERN
* @see CameraCharacteristics#SENSOR_INFO_WHITE_LEVEL
* @see CameraCharacteristics#SENSOR_OPTICAL_BLACK_REGIONS
* @see CaptureRequest#SENSOR_SENSITIVITY
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index d84a6fc..d2e820e 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -974,6 +974,7 @@
if (mRemoteDevice != null) {
mRemoteDevice.disconnect();
+ mRemoteDevice.unlinkToDeath(this, /*flags*/0);
}
// Only want to fire the onClosed callback once;
diff --git a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
index ddc3fd1..ef5f6d7 100644
--- a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
+++ b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
@@ -31,6 +31,7 @@
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.params.OutputConfiguration;
import android.hardware.camera2.utils.SubmitInfo;
+import android.os.IBinder;
import android.os.RemoteException;
import android.view.Surface;
@@ -53,6 +54,12 @@
mRemoteDevice = remoteDevice;
}
+ public void unlinkToDeath(IBinder.DeathRecipient recipient, int flags) {
+ if (mRemoteDevice.asBinder() != null) {
+ mRemoteDevice.asBinder().unlinkToDeath(recipient, flags);
+ }
+ }
+
public void disconnect() {
try {
mRemoteDevice.disconnect();
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceState.java b/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
index 2c2ad1c2..b0b94e3 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
@@ -70,7 +70,7 @@
* CameraDeviceStateListener callbacks to be called after state transitions.
*/
public interface CameraDeviceStateListener {
- void onError(int errorCode, RequestHolder holder);
+ void onError(int errorCode, Object errorArg, RequestHolder holder);
void onConfiguring();
void onIdle();
void onBusy();
@@ -162,11 +162,12 @@
* @param captureError Report a recoverable error for a single buffer or result using a valid
* error code for {@code ICameraDeviceCallbacks}, or
* {@link #NO_CAPTURE_ERROR}.
+ * @param captureErrorArg An argument for some error captureError codes.
* @return {@code false} if an error has occurred.
*/
public synchronized boolean setCaptureResult(final RequestHolder request,
- final CameraMetadataNative result,
- final int captureError) {
+ final CameraMetadataNative result,
+ final int captureError, final Object captureErrorArg) {
if (mCurrentState != STATE_CAPTURING) {
Log.e(TAG, "Cannot receive result while in state: " + mCurrentState);
mCurrentError = CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE;
@@ -179,7 +180,7 @@
mCurrentHandler.post(new Runnable() {
@Override
public void run() {
- mCurrentListener.onError(captureError, request);
+ mCurrentListener.onError(captureError, captureErrorArg, request);
}
});
} else {
@@ -194,6 +195,11 @@
return mCurrentError == NO_CAPTURE_ERROR;
}
+ public synchronized boolean setCaptureResult(final RequestHolder request,
+ final CameraMetadataNative result) {
+ return setCaptureResult(request, result, NO_CAPTURE_ERROR, /*errorArg*/null);
+ }
+
/**
* Set the listener for state transition callbacks.
*
@@ -239,7 +245,7 @@
mCurrentHandler.post(new Runnable() {
@Override
public void run() {
- mCurrentListener.onError(mCurrentError, mCurrentRequest);
+ mCurrentListener.onError(mCurrentError, /*errorArg*/null, mCurrentRequest);
}
});
}
@@ -299,7 +305,7 @@
mCurrentHandler.post(new Runnable() {
@Override
public void run() {
- mCurrentListener.onError(error, mCurrentRequest);
+ mCurrentListener.onError(error, /*errorArg*/null, mCurrentRequest);
}
});
} else {
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index d01c275..f99928a 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -480,19 +480,15 @@
throw new ServiceSpecificException(ICameraService.ERROR_DISCONNECTED, err);
}
- ArrayList<Surface> surfaces = null;
+ SparseArray<Surface> surfaces = null;
synchronized(mConfigureLock) {
if (!mConfiguring) {
String err = "Cannot end configure, no configuration change in progress.";
Log.e(TAG, err);
throw new ServiceSpecificException(ICameraService.ERROR_INVALID_OPERATION, err);
}
- int numSurfaces = mSurfaces.size();
- if (numSurfaces > 0) {
- surfaces = new ArrayList<>();
- for (int i = 0; i < numSurfaces; ++i) {
- surfaces.add(mSurfaces.valueAt(i));
- }
+ if (mSurfaces != null) {
+ surfaces = mSurfaces.clone();
}
mConfiguring = false;
}
diff --git a/core/java/android/hardware/camera2/legacy/CaptureCollector.java b/core/java/android/hardware/camera2/legacy/CaptureCollector.java
index eb48a01..113927c 100644
--- a/core/java/android/hardware/camera2/legacy/CaptureCollector.java
+++ b/core/java/android/hardware/camera2/legacy/CaptureCollector.java
@@ -19,7 +19,7 @@
import android.util.Log;
import android.util.MutableLong;
import android.util.Pair;
-
+import android.view.Surface;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.TreeSet;
@@ -95,22 +95,28 @@
} else {
// Send buffer dropped errors for each pending buffer if the request has
// started.
- if (mFailedPreview) {
- Log.w(TAG, "Preview buffers dropped for request: " +
- mRequest.getRequestId());
- for (int i = 0; i < mRequest.numPreviewTargets(); i++) {
- CaptureCollector.this.mDeviceState.setCaptureResult(mRequest,
- /*result*/null,
- CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_BUFFER);
- }
- }
- if (mFailedJpeg) {
- Log.w(TAG, "Jpeg buffers dropped for request: " +
- mRequest.getRequestId());
- for (int i = 0; i < mRequest.numJpegTargets(); i++) {
- CaptureCollector.this.mDeviceState.setCaptureResult(mRequest,
- /*result*/null,
- CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_BUFFER);
+ for (Surface targetSurface : mRequest.getRequest().getTargets() ) {
+ try {
+ if (mRequest.jpegType(targetSurface)) {
+ if (mFailedJpeg) {
+ CaptureCollector.this.mDeviceState.setCaptureResult(mRequest,
+ /*result*/null,
+ CameraDeviceImpl.CameraDeviceCallbacks.
+ ERROR_CAMERA_BUFFER,
+ targetSurface);
+ }
+ } else {
+ // preview buffer
+ if (mFailedPreview) {
+ CaptureCollector.this.mDeviceState.setCaptureResult(mRequest,
+ /*result*/null,
+ CameraDeviceImpl.CameraDeviceCallbacks.
+ ERROR_CAMERA_BUFFER,
+ targetSurface);
+ }
+ }
+ } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
+ Log.e(TAG, "Unexpected exception when querying Surface: " + e);
}
}
}
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index 661edd7..6c95869 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -36,6 +36,7 @@
import android.util.Log;
import android.util.Pair;
import android.util.Size;
+import android.util.SparseArray;
import android.view.Surface;
import java.util.ArrayList;
@@ -64,7 +65,7 @@
private final CameraCharacteristics mStaticCharacteristics;
private final ICameraDeviceCallbacks mDeviceCallbacks;
private final CameraDeviceState mDeviceState = new CameraDeviceState();
- private List<Surface> mConfiguredSurfaces;
+ private SparseArray<Surface> mConfiguredSurfaces;
private boolean mClosed = false;
private final ConditionVariable mIdle = new ConditionVariable(/*open*/true);
@@ -89,13 +90,29 @@
public static final int NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW = 1;
private CaptureResultExtras getExtrasFromRequest(RequestHolder holder) {
+ return getExtrasFromRequest(holder,
+ /*errorCode*/CameraDeviceState.NO_CAPTURE_ERROR, /*errorArg*/null);
+ }
+
+ private CaptureResultExtras getExtrasFromRequest(RequestHolder holder,
+ int errorCode, Object errorArg) {
+ int errorStreamId = -1;
+ if (errorCode == CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_BUFFER) {
+ Surface errorTarget = (Surface) errorArg;
+ int indexOfTarget = mConfiguredSurfaces.indexOfValue(errorTarget);
+ if (indexOfTarget < 0) {
+ Log.e(TAG, "Buffer drop error reported for unknown Surface");
+ } else {
+ errorStreamId = mConfiguredSurfaces.keyAt(indexOfTarget);
+ }
+ }
if (holder == null) {
return new CaptureResultExtras(ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE,
ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE);
}
return new CaptureResultExtras(holder.getRequestId(), holder.getSubsequeceId(),
/*afTriggerId*/0, /*precaptureTriggerId*/0, holder.getFrameNumber(),
- /*partialResultCount*/1, /*errorStreamId*/-1);
+ /*partialResultCount*/1, errorStreamId);
}
/**
@@ -105,9 +122,9 @@
private final CameraDeviceState.CameraDeviceStateListener mStateListener =
new CameraDeviceState.CameraDeviceStateListener() {
@Override
- public void onError(final int errorCode, final RequestHolder holder) {
+ public void onError(final int errorCode, final Object errorArg, final RequestHolder holder) {
if (DEBUG) {
- Log.d(TAG, "onError called, errorCode = " + errorCode);
+ Log.d(TAG, "onError called, errorCode = " + errorCode + ", errorArg = " + errorArg);
}
switch (errorCode) {
/*
@@ -125,7 +142,7 @@
}
}
- final CaptureResultExtras extras = getExtrasFromRequest(holder);
+ final CaptureResultExtras extras = getExtrasFromRequest(holder, errorCode, errorArg);
mResultHandler.post(new Runnable() {
@Override
public void run() {
@@ -281,14 +298,17 @@
*
* <p>Every surface in {@code outputs} must be non-{@code null}.</p>
*
- * @param outputs a list of surfaces to set.
+ * @param outputs a list of surfaces to set. LegacyCameraDevice will take ownership of this
+ * list; it must not be modified by the caller once it's passed in.
* @return an error code for this binder operation, or {@link NO_ERROR}
* on success.
*/
- public int configureOutputs(List<Surface> outputs) {
+ public int configureOutputs(SparseArray<Surface> outputs) {
List<Pair<Surface, Size>> sizedSurfaces = new ArrayList<>();
if (outputs != null) {
- for (Surface output : outputs) {
+ int count = outputs.size();
+ for (int i = 0; i < count; i++) {
+ Surface output = outputs.valueAt(i);
if (output == null) {
Log.e(TAG, "configureOutputs - null outputs are not allowed");
return BAD_VALUE;
@@ -353,7 +373,7 @@
}
if (success) {
- mConfiguredSurfaces = outputs != null ? new ArrayList<>(outputs) : null;
+ mConfiguredSurfaces = outputs;
} else {
return LegacyExceptionUtils.INVALID_OPERATION;
}
@@ -659,6 +679,23 @@
return nativeGetSurfaceId(surface);
}
+ static List<Long> getSurfaceIds(SparseArray<Surface> surfaces) {
+ if (surfaces == null) {
+ throw new NullPointerException("Null argument surfaces");
+ }
+ List<Long> surfaceIds = new ArrayList<>();
+ int count = surfaces.size();
+ for (int i = 0; i < count; i++) {
+ long id = getSurfaceId(surfaces.valueAt(i));
+ if (id == 0) {
+ throw new IllegalStateException(
+ "Configured surface had null native GraphicBufferProducer pointer!");
+ }
+ surfaceIds.add(id);
+ }
+ return surfaceIds;
+ }
+
static List<Long> getSurfaceIds(Collection<Surface> surfaces) {
if (surfaces == null) {
throw new NullPointerException("Null argument surfaces");
diff --git a/core/java/android/hardware/camera2/legacy/RequestHolder.java b/core/java/android/hardware/camera2/legacy/RequestHolder.java
index 9b628fb..476c3de 100644
--- a/core/java/android/hardware/camera2/legacy/RequestHolder.java
+++ b/core/java/android/hardware/camera2/legacy/RequestHolder.java
@@ -41,6 +41,8 @@
private final int mNumPreviewTargets;
private volatile boolean mFailed = false;
+ private final Collection<Long> mJpegSurfaceIds;
+
/**
* A builder class for {@link RequestHolder} objects.
*
@@ -150,13 +152,13 @@
*/
public RequestHolder build(long frameNumber) {
return new RequestHolder(mRequestId, mSubsequenceId, mRequest, mRepeating, frameNumber,
- mNumJpegTargets, mNumPreviewTargets);
+ mNumJpegTargets, mNumPreviewTargets, mJpegSurfaceIds);
}
}
private RequestHolder(int requestId, int subsequenceId, CaptureRequest request,
boolean repeating, long frameNumber, int numJpegTargets,
- int numPreviewTargets) {
+ int numPreviewTargets, Collection<Long> jpegSurfaceIds) {
mRepeating = repeating;
mRequest = request;
mRequestId = requestId;
@@ -164,6 +166,7 @@
mFrameNumber = frameNumber;
mNumJpegTargets = numJpegTargets;
mNumPreviewTargets = numPreviewTargets;
+ mJpegSurfaceIds = jpegSurfaceIds;
}
/**
@@ -238,6 +241,17 @@
}
/**
+ * Returns true if the given surface requires jpeg buffers.
+ *
+ * @param s a {@link android.view.Surface} to check.
+ * @return true if the surface requires a jpeg buffer.
+ */
+ public boolean jpegType(Surface s)
+ throws LegacyExceptionUtils.BufferQueueAbandonedException {
+ return LegacyCameraDevice.containsSurfaceId(s, mJpegSurfaceIds);
+ }
+
+ /**
* Mark this request as failed.
*/
public void failRequest() {
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index e8ce3ec..a3fdd56 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -908,8 +908,7 @@
mFaceDetectMapper.mapResultFaces(result, mLastRequest);
if (!holder.requestFailed()) {
- mDeviceState.setCaptureResult(holder, result,
- CameraDeviceState.NO_CAPTURE_ERROR);
+ mDeviceState.setCaptureResult(holder, result);
}
}
if (DEBUG) {
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index fbac58c..47440de 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -16,9 +16,7 @@
package android.hardware.input;
-import com.android.internal.inputmethod.InputMethodSubtypeHandle;
import com.android.internal.os.SomeArgs;
-import com.android.internal.util.ArrayUtils;
import android.annotation.IntDef;
import android.annotation.SdkConstant;
diff --git a/core/java/android/hardware/input/InputManagerInternal.java b/core/java/android/hardware/input/InputManagerInternal.java
index 014e73f..10fc8e6 100644
--- a/core/java/android/hardware/input/InputManagerInternal.java
+++ b/core/java/android/hardware/input/InputManagerInternal.java
@@ -52,4 +52,11 @@
*/
public abstract void onInputMethodSubtypeChanged(int userId,
@Nullable InputMethodInfo inputMethodInfo, @Nullable InputMethodSubtype subtype);
+
+ /**
+ * Toggles Caps Lock state for input device with specific id.
+ *
+ * @param deviceId The id of input device.
+ */
+ public abstract void toggleCapsLock(int deviceId);
}
diff --git a/core/java/android/hardware/location/ContextHubInfo.java b/core/java/android/hardware/location/ContextHubInfo.java
index 644e29f..194b9ee 100644
--- a/core/java/android/hardware/location/ContextHubInfo.java
+++ b/core/java/android/hardware/location/ContextHubInfo.java
@@ -37,6 +37,7 @@
private float mStoppedPowerDrawMw;
private float mSleepPowerDrawMw;
private float mPeakPowerDrawMw;
+ private int mMaxPacketLengthBytes;
private int[] mSupportedSensors;
@@ -46,6 +47,27 @@
}
/**
+ * returns the maximum number of bytes that can be sent per message to the hub
+ *
+ * @return int - maximum bytes that can be transmitted in a
+ * single packet
+ */
+ public int getMaxPacketLengthBytes() {
+ return mMaxPacketLengthBytes;
+ }
+
+ /**
+ * set the context hub unique identifer
+ *
+ * @param bytes - Maximum number of bytes per message
+ *
+ * @hide
+ */
+ public void setMaxPacketLenBytes(int bytes) {
+ mMaxPacketLengthBytes = bytes;
+ }
+
+ /**
* get the context hub unique identifer
*
* @return int - unique system wide identifier
@@ -323,6 +345,24 @@
mMemoryRegions = Arrays.copyOf(memoryRegions, memoryRegions.length);
}
+ @Override
+ public String toString() {
+ String retVal = "";
+ retVal += "Id : " + mId;
+ retVal += ", Name : " + mName;
+ retVal += "\n\tVendor : " + mVendor;
+ retVal += ", ToolChain : " + mToolchain;
+ retVal += "\n\tPlatformVersion : " + mPlatformVersion;
+ retVal += ", StaticSwVersion : " + mStaticSwVersion;
+ retVal += "\n\tPeakMips : " + mPeakMips;
+ retVal += ", StoppedPowerDraw : " + mStoppedPowerDrawMw + " mW";
+ retVal += ", PeakPowerDraw : " + mPeakPowerDrawMw + " mW";
+ retVal += "\n\tSupported sensors : " + Arrays.toString(mSupportedSensors);
+ retVal += "\n\tMemory Regions : " + Arrays.toString(mMemoryRegions);
+
+ return retVal;
+ }
+
private ContextHubInfo(Parcel in) {
mId = in.readInt();
mName = in.readString();
@@ -374,4 +414,4 @@
return new ContextHubInfo[size];
}
};
-}
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 4ddf767..89edaa9 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -43,23 +43,6 @@
private Handler mCallbackHandler;
/**
- * A special context hub identifier meaning any possible hub on the system.
- */
- public static final int ANY_HUB = -1;
- /**
- * A constant denoting a message to load a a Nano App
- */
- public static final int MSG_LOAD_NANO_APP = 1;
- /**
- * A constant denoting a message to unload a a Nano App
- */
- public static final int MSG_UNLOAD_NANO_APP = 2;
- /**
- * A constant denoting a message to send a message
- */
- public static final int MSG_DATA_SEND = 3;
-
- /**
* An interface to receive asynchronous communication from the context hub.
*/
public abstract static class Callback {
@@ -69,7 +52,7 @@
* Callback function called on message receipt from context hub.
*
* @param hubHandle Handle (system-wide unique identifier) of the hub of the message.
- * @param nanoAppHandle Handle (unique identifier) for the app that sent the message.
+ * @param nanoAppHandle Handle (unique identifier) for app instance that sent the message.
* @param message The context hub message.
*
* @see ContextHubMessage
@@ -89,7 +72,7 @@
try {
retVal = getBinder().getContextHubHandles();
} catch (RemoteException e) {
- Log.e(TAG, "Could not fetch context hub handles : " + e);
+ Log.w(TAG, "Could not fetch context hub handles : " + e);
}
return retVal;
}
@@ -107,7 +90,7 @@
try {
retVal = getBinder().getContextHubInfo(hubHandle);
} catch (RemoteException e) {
- Log.e(TAG, "Could not fetch context hub info :" + e);
+ Log.w(TAG, "Could not fetch context hub info :" + e);
}
return retVal;
@@ -126,6 +109,7 @@
*/
public int loadNanoApp(int hubHandle, NanoApp app) {
int retVal = -1;
+
if (app == null) {
return retVal;
}
@@ -133,7 +117,7 @@
try {
retVal = getBinder().loadNanoApp(hubHandle, app);
} catch (RemoteException e) {
- Log.e(TAG, "Could not fetch load nanoApp :" + e);
+ Log.w(TAG, "Could not load nanoApp :" + e);
}
return retVal;
@@ -152,7 +136,7 @@
try {
retVal = getBinder().unloadNanoApp(nanoAppHandle);
} catch (RemoteException e) {
- Log.e(TAG, "Could not fetch unload nanoApp :" + e);
+ Log.w(TAG, "Could not fetch unload nanoApp :" + e);
}
return retVal;
@@ -172,7 +156,7 @@
try {
retVal = getBinder().getNanoAppInstanceInfo(nanoAppHandle);
} catch (RemoteException e) {
- Log.e(TAG, "Could not fetch nanoApp info :" + e);
+ Log.w(TAG, "Could not fetch nanoApp info :" + e);
}
return retVal;
@@ -193,7 +177,7 @@
try {
retVal = getBinder().findNanoAppOnHub(hubHandle, filter);
} catch (RemoteException e) {
- Log.e(TAG, "Could not query nanoApp instance :" + e);
+ Log.w(TAG, "Could not query nanoApp instance :" + e);
}
return retVal;
}
@@ -212,10 +196,14 @@
public int sendMessage(int hubHandle, int nanoAppHandle, ContextHubMessage message) {
int retVal = -1;
+ if (message == null || message.getData() == null) {
+ Log.w(TAG, "null ptr");
+ return retVal;
+ }
try {
retVal = getBinder().sendMessage(hubHandle, nanoAppHandle, message);
} catch (RemoteException e) {
- Log.e(TAG, "Could not fetch send message :" + e.toString());
+ Log.w(TAG, "Could not send message :" + e.toString());
}
return retVal;
@@ -247,7 +235,7 @@
public int registerCallback(Callback callback, Handler handler) {
synchronized(this) {
if (mCallback != null) {
- Log.e(TAG, "Max number of callbacks reached!");
+ Log.w(TAG, "Max number of callbacks reached!");
return -1;
}
mCallback = callback;
@@ -268,7 +256,7 @@
public int unregisterCallback(Callback callback) {
synchronized(this) {
if (callback != mCallback) {
- Log.e(TAG, "Cannot recognize callback!");
+ Log.w(TAG, "Cannot recognize callback!");
return -1;
}
@@ -311,11 +299,11 @@
try {
getBinder().registerCallback(mClientCallback);
} catch (RemoteException e) {
- Log.e(TAG, "Could not register callback:" + e);
+ Log.w(TAG, "Could not register callback:" + e);
}
} else {
- Log.d(TAG, "failed to getService");
+ Log.w(TAG, "failed to getService");
}
}
diff --git a/core/java/android/hardware/location/ContextHubMessage.java b/core/java/android/hardware/location/ContextHubMessage.java
index 954e97d..bca2ae6 100644
--- a/core/java/android/hardware/location/ContextHubMessage.java
+++ b/core/java/android/hardware/location/ContextHubMessage.java
@@ -16,10 +16,10 @@
package android.hardware.location;
-
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.Log;
import java.util.Arrays;
@@ -32,6 +32,9 @@
private int mVersion;
private byte[]mData;
+ private static final String TAG = "ContextHubMessage";
+
+
/**
* Get the message type
*
@@ -106,9 +109,11 @@
private ContextHubMessage(Parcel in) {
mType = in.readInt();
mVersion = in.readInt();
- byte[] byteBuffer = new byte[in.readInt()];
- in.readByteArray(byteBuffer);
+ int bufferLength = in.readInt();
+ mData = new byte[bufferLength];
+ in.readByteArray(mData);
}
+
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mType);
out.writeInt(mVersion);
diff --git a/core/java/android/hardware/location/ContextHubService.java b/core/java/android/hardware/location/ContextHubService.java
index 274babe..2b9b974 100644
--- a/core/java/android/hardware/location/ContextHubService.java
+++ b/core/java/android/hardware/location/ContextHubService.java
@@ -18,9 +18,12 @@
import android.Manifest;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.RemoteException;
import android.util.Log;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
@@ -28,13 +31,30 @@
* @hide
*/
public class ContextHubService extends IContextHubService.Stub {
+ public static final String CONTEXTHUB_SERVICE = "contexthub_service";
private static final String TAG = "ContextHubService";
private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE;
private static final String ENFORCE_HW_PERMISSION_MESSAGE = "Permission '"
- + HARDWARE_PERMISSION + "' not granted to access ContextHub Hardware";
+ + HARDWARE_PERMISSION + "' not granted to access ContextHub Hardware";
- public static final String CONTEXTHUB_SERVICE = "contexthub_service";
+
+ public static final int ANY_HUB = -1;
+ public static final int MSG_LOAD_NANO_APP = 3;
+ public static final int MSG_UNLOAD_NANO_APP = 4;
+
+ private static final String PRE_LOADED_GENERIC_UNKNOWN = "Preloaded app, unknown";
+ private static final String PRE_LOADED_APP_NAME = PRE_LOADED_GENERIC_UNKNOWN;
+ private static final String PRE_LOADED_APP_PUBLISHER = PRE_LOADED_GENERIC_UNKNOWN;
+ private static final int PRE_LOADED_APP_MEM_REQ = 0;
+
+ private static final int MSG_HEADER_SIZE = 4;
+ private static final int MSG_FIELD_TYPE = 0;
+ private static final int MSG_FIELD_VERSION = 1;
+ private static final int MSG_FIELD_HUB_HANDLE = 2;
+ private static final int MSG_FIELD_APP_INSTANCE = 3;
+
+ private static final int OS_APP_INSTANCE = -1;
private final Context mContext;
@@ -42,54 +62,37 @@
private ContextHubInfo[] mContextHubInfo;
private IContextHubCallback mCallback;
+ private native int nativeSendMessage(int[] header, byte[] data);
+ private native ContextHubInfo[] nativeInitialize();
+
+
public ContextHubService(Context context) {
mContext = context;
mContextHubInfo = nativeInitialize();
+ mNanoAppHash = new HashMap<Integer, NanoAppInstanceInfo>();
for (int i = 0; i < mContextHubInfo.length; i++) {
- Log.v(TAG, "ContextHub[" + i + "] id: " + mContextHubInfo[i].getId()
+ Log.d(TAG, "ContextHub[" + i + "] id: " + mContextHubInfo[i].getId()
+ ", name: " + mContextHubInfo[i].getName());
}
}
- private native int nativeSendMessage(int[] header, byte[] data);
- private native ContextHubInfo[] nativeInitialize();
-
@Override
- public int registerCallback(IContextHubCallback callback) throws RemoteException{
+ public int registerCallback(IContextHubCallback callback) throws RemoteException {
checkPermissions();
- mCallback = callback;
- return 0;
- }
-
-
- private int onMessageReceipt(int[] header, byte[] data) {
- if (mCallback != null) {
- // TODO : Defend against unexpected header sizes
- // Add abstraction for magic numbers
- // onMessageRecipt should pass the right arguments
- ContextHubMessage msg = new ContextHubMessage(header[0], header[1], data);
-
- try {
- mCallback.onMessageReceipt(0, 0, msg);
- } catch (Exception e) {
- Log.e(TAG, "Exception " + e + " when calling remote callback");
- return -1;
- }
- } else {
- Log.d(TAG, "Message Callback is NULL");
+ synchronized (this) {
+ mCallback = callback;
}
-
return 0;
}
@Override
public int[] getContextHubHandles() throws RemoteException {
checkPermissions();
- int [] returnArray = new int[mContextHubInfo.length];
+ int[] returnArray = new int[mContextHubInfo.length];
for (int i = 0; i < returnArray.length; ++i) {
- returnArray[i] = i + 1; //valid handles from 1...n
+ returnArray[i] = i;
Log.d(TAG, String.format("Hub %s is mapped to %d",
mContextHubInfo[i].getName(), returnArray[i]));
}
@@ -100,7 +103,6 @@
@Override
public ContextHubInfo getContextHubInfo(int contextHubHandle) throws RemoteException {
checkPermissions();
- contextHubHandle -= 1;
if (!(contextHubHandle >= 0 && contextHubHandle < mContextHubInfo.length)) {
return null; // null means fail
}
@@ -111,21 +113,23 @@
@Override
public int loadNanoApp(int contextHubHandle, NanoApp app) throws RemoteException {
checkPermissions();
- contextHubHandle -= 1;
if (!(contextHubHandle >= 0 && contextHubHandle < mContextHubInfo.length)) {
- return -1; // negative handle are invalid, means failed
+ Log.e(TAG, "Invalid contextHubhandle " + contextHubHandle);
+ return -1;
}
- // Call Native interface here
- int[] msgHeader = new int[8];
- msgHeader[0] = contextHubHandle;
- msgHeader[1] = app.getAppId();
- msgHeader[2] = app.getAppVersion();
- msgHeader[3] = ContextHubManager.MSG_LOAD_NANO_APP;
- msgHeader[4] = 0; // Loading hints
+ int[] msgHeader = new int[MSG_HEADER_SIZE];
+ msgHeader[MSG_FIELD_HUB_HANDLE] = contextHubHandle;
+ msgHeader[MSG_FIELD_APP_INSTANCE] = OS_APP_INSTANCE;
+ msgHeader[MSG_FIELD_VERSION] = 0;
+ msgHeader[MSG_FIELD_TYPE] = MSG_LOAD_NANO_APP;
- return nativeSendMessage(msgHeader, app.getAppBinary());
+ if (nativeSendMessage(msgHeader, app.getAppBinary()) != 0) {
+ return -1;
+ }
+ // Do not add an entry to mNanoAppInstance Hash yet. The HAL may reject the app
+ return 0;
}
@Override
@@ -137,17 +141,23 @@
}
// Call Native interface here
- int[] msgHeader = new int[8];
- msgHeader[0] = info.getContexthubId();
- msgHeader[1] = ContextHubManager.MSG_UNLOAD_NANO_APP;
- msgHeader[2] = info.getHandle();
+ int[] msgHeader = new int[MSG_HEADER_SIZE];
+ msgHeader[MSG_FIELD_HUB_HANDLE] = ANY_HUB;
+ msgHeader[MSG_FIELD_APP_INSTANCE] = OS_APP_INSTANCE;
+ msgHeader[MSG_FIELD_VERSION] = 0;
+ msgHeader[MSG_FIELD_TYPE] = MSG_UNLOAD_NANO_APP;
- return nativeSendMessage(msgHeader, null);
+ if (nativeSendMessage(msgHeader, null) != 0) {
+ return -1;
+ }
+
+ // Do not add an entry to mNanoAppInstance Hash yet. The HAL may reject the app
+ return 0;
}
@Override
public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppInstanceHandle)
- throws RemoteException {
+ throws RemoteException {
checkPermissions();
// This assumes that all the nanoAppInfo is current. This is reasonable
// for the use cases for tightly controlled nanoApps.
@@ -163,10 +173,10 @@
checkPermissions();
ArrayList<Integer> foundInstances = new ArrayList<Integer>();
- for(Integer nanoAppInstance : mNanoAppHash.keySet()) {
+ for (Integer nanoAppInstance: mNanoAppHash.keySet()) {
NanoAppInstanceInfo info = mNanoAppHash.get(nanoAppInstance);
- if(filter.testMatch(info)){
+ if (filter.testMatch(info)) {
foundInstances.add(nanoAppInstance);
}
}
@@ -181,20 +191,93 @@
@Override
public int sendMessage(int hubHandle, int nanoAppHandle, ContextHubMessage msg)
- throws RemoteException {
+ throws RemoteException {
checkPermissions();
- int[] msgHeader = new int[8];
- msgHeader[0] = ContextHubManager.MSG_DATA_SEND;
- msgHeader[1] = hubHandle;
- msgHeader[2] = nanoAppHandle;
- msgHeader[3] = msg.getMsgType();
- msgHeader[4] = msg.getVersion();
+
+ int[] msgHeader = new int[MSG_HEADER_SIZE];
+ msgHeader[MSG_FIELD_HUB_HANDLE] = hubHandle;
+ msgHeader[MSG_FIELD_APP_INSTANCE] = nanoAppHandle;
+ msgHeader[MSG_FIELD_VERSION] = msg.getVersion();
+ msgHeader[MSG_FIELD_TYPE] = msg.getMsgType();
return nativeSendMessage(msgHeader, msg.getData());
}
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mContext.checkCallingOrSelfPermission("android.permission.DUMP")
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump contexthub_service");
+ return;
+ }
+
+ pw.println("Dumping ContextHub Service");
+
+ pw.println("");
+ // dump ContextHubInfo
+ pw.println("=================== CONTEXT HUBS ====================");
+ for (int i = 0; i < mContextHubInfo.length; i++) {
+ pw.println("Handle " + i + " : " + mContextHubInfo[i].toString());
+ }
+ pw.println("");
+ pw.println("=================== NANOAPPS ====================");
+ // Dump nanoAppHash
+ for (Integer nanoAppInstance: mNanoAppHash.keySet()) {
+ pw.println(nanoAppInstance + " : " + mNanoAppHash.get(nanoAppInstance).toString());
+ }
+
+ // dump eventLog
+ }
+
private void checkPermissions() {
mContext.enforceCallingPermission(HARDWARE_PERMISSION, ENFORCE_HW_PERMISSION_MESSAGE);
}
-}
+ private int onMessageReceipt(int[] header, byte[] data) {
+ if (header == null || data == null || header.length < MSG_HEADER_SIZE) {
+ return -1;
+ }
+
+ synchronized (this) {
+ if (mCallback != null) {
+ ContextHubMessage msg = new ContextHubMessage(header[MSG_FIELD_TYPE],
+ header[MSG_FIELD_VERSION],
+ data);
+
+ try {
+ mCallback.onMessageReceipt(header[MSG_FIELD_HUB_HANDLE],
+ header[MSG_FIELD_APP_INSTANCE],
+ msg);
+ } catch (Exception e) {
+ Log.w(TAG, "Exception " + e + " when calling remote callback");
+ return -1;
+ }
+ } else {
+ Log.d(TAG, "Message Callback is NULL");
+ }
+ }
+
+ return 0;
+ }
+
+ private int addAppInstance(int hubHandle, int appInstanceHandle, long appId, int appVersion) {
+ // App Id encodes vendor & version
+ NanoAppInstanceInfo appInfo = new NanoAppInstanceInfo();
+
+ appInfo.setAppId(appId);
+ appInfo.setAppVersion(appVersion);
+ appInfo.setName(PRE_LOADED_APP_NAME);
+ appInfo.setContexthubId(hubHandle);
+ appInfo.setHandle(appInstanceHandle);
+ appInfo.setPublisher(PRE_LOADED_APP_PUBLISHER);
+ appInfo.setNeededExecMemBytes(PRE_LOADED_APP_MEM_REQ);
+ appInfo.setNeededReadMemBytes(PRE_LOADED_APP_MEM_REQ);
+ appInfo.setNeededWriteMemBytes(PRE_LOADED_APP_MEM_REQ);
+
+ mNanoAppHash.put(appInstanceHandle, appInfo);
+ Log.d(TAG, "Added app instance " + appInstanceHandle + " with id " + appId
+ + " version " + appVersion);
+
+ return 0;
+ }
+}
diff --git a/core/java/android/hardware/location/MemoryRegion.java b/core/java/android/hardware/location/MemoryRegion.java
index d100de2..857434e 100644
--- a/core/java/android/hardware/location/MemoryRegion.java
+++ b/core/java/android/hardware/location/MemoryRegion.java
@@ -79,6 +79,33 @@
}
@Override
+ public String toString() {
+ String mask = "";
+
+ if (isReadable()) {
+ mask += "r";
+ } else {
+ mask += "-";
+ }
+
+ if (isWritable()) {
+ mask += "w";
+ } else {
+ mask += "-";
+ }
+
+ if (isExecutable()) {
+ mask += "x";
+ } else {
+ mask += "-";
+ }
+
+ String retVal = "[ " + mSizeBytesFree + "/ " + mSizeBytes + " ] : " + mask;
+
+ return retVal;
+ }
+
+ @Override
public int describeContents() {
return 0;
}
diff --git a/core/java/android/hardware/location/NanoApp.java b/core/java/android/hardware/location/NanoApp.java
index b447b62..c8f3439 100644
--- a/core/java/android/hardware/location/NanoApp.java
+++ b/core/java/android/hardware/location/NanoApp.java
@@ -284,4 +284,14 @@
return new NanoApp[size];
}
};
+
+ @Override
+ public String toString() {
+ String retVal = "Id : " + mAppId;
+ retVal += ", Version : " + mAppVersion;
+ retVal += ", Name : " + mName;
+ retVal += ", Publisher : " + mPublisher;
+
+ return retVal;
+ }
}
diff --git a/core/java/android/hardware/location/NanoAppFilter.java b/core/java/android/hardware/location/NanoAppFilter.java
index 369f9e4..8db70e9 100644
--- a/core/java/android/hardware/location/NanoAppFilter.java
+++ b/core/java/android/hardware/location/NanoAppFilter.java
@@ -20,6 +20,7 @@
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.Log;
/**
* @hide
@@ -27,6 +28,8 @@
@SystemApi
public class NanoAppFilter {
+ private static final String TAG = "NanoAppFilter";
+
// The appId, can be set to APP_ID_ANY
private long mAppId;
@@ -54,6 +57,10 @@
* If this flag is set, only versions strictly less than the version specified shall match.
*/
public static final int FLAGS_VERSION_LESS_THAN = 4;
+ /**
+ * If this flag is set, only versions strictly equal to the
+ * version specified shall match.
+ */
public static final int FLAGS_VERSION_STRICTLY_EQUAL = 8;
/**
@@ -117,14 +124,9 @@
* @return true if this is a match, false otherwise
*/
public boolean testMatch(NanoAppInstanceInfo info) {
- if ((mContextHubId == HUB_ANY || info.getContexthubId() == mContextHubId) &&
+ return (mContextHubId == HUB_ANY || info.getContexthubId() == mContextHubId) &&
(mAppId == APP_ANY || info.getAppId() == mAppId) &&
- // (mAppIdVendorMask == VENDOR_ANY) TODO : Expose Vendor mask cleanly
- (versionsMatch(mVersionRestrictionMask, mAppVersion, info.getAppVersion()))) {
- return true;
- } else {
- return false;
- }
+ (versionsMatch(mVersionRestrictionMask, mAppVersion, info.getAppVersion()));
}
public static final Parcelable.Creator<NanoAppFilter> CREATOR
diff --git a/core/java/android/hardware/location/NanoAppInstanceInfo.java b/core/java/android/hardware/location/NanoAppInstanceInfo.java
index ac62919..e842ec6 100644
--- a/core/java/android/hardware/location/NanoAppInstanceInfo.java
+++ b/core/java/android/hardware/location/NanoAppInstanceInfo.java
@@ -29,7 +29,7 @@
private String mPublisher;
private String mName;
- private int mAppId;
+ private long mAppId;
private int mAppVersion;
private int mNeededReadMemBytes;
@@ -59,6 +59,8 @@
* set the publisher name for the app
*
* @param publisher - name of the publisher
+ *
+ * @hide
*/
public void setPublisher(String publisher) {
mPublisher = publisher;
@@ -77,6 +79,8 @@
* set the name of the app
*
* @param name - name of the app
+ *
+ * @hide
*/
public void setName(String name) {
mName = name;
@@ -87,7 +91,7 @@
*
* @return int - application identifier
*/
- public int getAppId() {
+ public long getAppId() {
return mAppId;
}
@@ -95,8 +99,10 @@
* Set the application identifier
*
* @param appId - application identifier
+ *
+ * @hide
*/
- public void setAppId(int appId) {
+ public void setAppId(long appId) {
mAppId = appId;
}
@@ -113,6 +119,8 @@
* Set the application version
*
* @param appVersion - version of the app
+ *
+ * @hide
*/
public void setAppVersion(int appVersion) {
mAppVersion = appVersion;
@@ -131,6 +139,8 @@
* Set the read memory needed by the app
*
* @param neededReadMemBytes - readable Memory needed in bytes
+ *
+ * @hide
*/
public void setNeededReadMemBytes(int neededReadMemBytes) {
mNeededReadMemBytes = neededReadMemBytes;
@@ -150,6 +160,8 @@
*
* @param neededWriteMemBytes - writable memory needed by the
* app
+ *
+ * @hide
*/
public void setNeededWriteMemBytes(int neededWriteMemBytes) {
mNeededWriteMemBytes = neededWriteMemBytes;
@@ -169,6 +181,8 @@
*
* @param neededExecMemBytes - executable memory needed by the
* app
+ *
+ * @hide
*/
public void setNeededExecMemBytes(int neededExecMemBytes) {
mNeededExecMemBytes = neededExecMemBytes;
@@ -187,6 +201,8 @@
* set the sensors needed by this app
*
* @param neededSensors - all the sensors needed by this app
+ *
+ * @hide
*/
public void setNeededSensors(int[] neededSensors) {
mNeededSensors = neededSensors;
@@ -206,6 +222,8 @@
*
* @param outputEvents - the events that may be generated by
* this app
+ *
+ * @hide
*/
public void setOutputEvents(int[] outputEvents) {
mOutputEvents = outputEvents;
@@ -224,6 +242,8 @@
* set the context hub identifier
*
* @param contexthubId - system wide unique identifier
+ *
+ * @hide
*/
public void setContexthubId(int contexthubId) {
mContexthubId = contexthubId;
@@ -242,6 +262,8 @@
* set the handle for an app instance
*
* @param handle - handle to this instance
+ *
+ * @hide
*/
public void setHandle(int handle) {
mHandle = handle;
@@ -252,7 +274,7 @@
mPublisher = in.readString();
mName = in.readString();
- mAppId = in.readInt();
+ mAppId = in.readLong();
mAppVersion = in.readInt();
mNeededReadMemBytes = in.readInt();
mNeededWriteMemBytes = in.readInt();
@@ -274,7 +296,7 @@
public void writeToParcel(Parcel out, int flags) {
out.writeString(mPublisher);
out.writeString(mName);
- out.writeInt(mAppId);
+ out.writeLong(mAppId);
out.writeInt(mAppVersion);
out.writeInt(mContexthubId);
out.writeInt(mNeededReadMemBytes);
@@ -286,7 +308,6 @@
out.writeInt(mOutputEvents.length);
out.writeIntArray(mOutputEvents);
-
}
public static final Parcelable.Creator<NanoAppInstanceInfo> CREATOR
@@ -299,4 +320,15 @@
return new NanoAppInstanceInfo[size];
}
};
+
+ @Override
+ public String toString() {
+ String retVal = "handle : " + mHandle;
+ retVal += ", Id : 0x" + Long.toHexString(mAppId);
+ retVal += ", Version : " + mAppVersion;
+ retVal += ", Name : " + mName;
+ retVal += ", Publisher : " + mPublisher;
+
+ return retVal;
+ }
}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 6b79a8a..cc201bc 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -295,9 +295,7 @@
boolean mLastShowInputRequested;
int mCandidatesVisibility;
CompletionInfo[] mCurCompletions;
-
- boolean mShowInputForced;
-
+
boolean mFullscreenApplied;
boolean mIsFullscreen;
View mExtractView;
@@ -422,7 +420,6 @@
boolean wasVis = isInputViewShown();
mShowInputFlags = 0;
mShowInputRequested = false;
- mShowInputForced = false;
doHideWindow();
clearInsetOfPreviousIme();
if (resultReceiver != null) {
@@ -439,8 +436,7 @@
public void showSoftInput(int flags, ResultReceiver resultReceiver) {
if (DEBUG) Log.v(TAG, "showSoftInput()");
boolean wasVis = isInputViewShown();
- mShowInputFlags = 0;
- if (onShowInputRequested(flags, false)) {
+ if (dispatchOnShowInputRequested(flags, false)) {
try {
showWindow(true);
} catch (BadTokenException e) {
@@ -721,7 +717,11 @@
mShowImeWithHardKeyboard = Settings.Secure.getInt(mService.getContentResolver(),
Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0 ?
ShowImeWithHardKeyboardType.TRUE : ShowImeWithHardKeyboardType.FALSE;
- mService.updateInputViewShown();
+ // In Android M and prior, state change of
+ // Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD has triggered
+ // #onConfigurationChanged(). For compatibility reasons, we reset the internal
+ // state as if configuration was changed.
+ mService.resetStateForNewConfiguration();
}
}
@@ -817,8 +817,8 @@
mInitialized = false;
mWindowCreated = false;
mShowInputRequested = false;
- mShowInputForced = false;
-
+ mShowInputFlags = 0;
+
mThemeAttrs = obtainStyledAttributes(android.R.styleable.InputMethodService);
mRootView = mInflater.inflate(
com.android.internal.R.layout.input_method, null);
@@ -888,7 +888,10 @@
*/
@Override public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
-
+ resetStateForNewConfiguration();
+ }
+
+ private void resetStateForNewConfiguration() {
boolean visible = mWindowVisible;
int showFlags = mShowInputFlags;
boolean showingInput = mShowInputRequested;
@@ -903,7 +906,7 @@
if (visible) {
if (showingInput) {
// If we were last showing the soft keyboard, try to do so again.
- if (onShowInputRequested(showFlags, true)) {
+ if (dispatchOnShowInputRequested(showFlags, true)) {
showWindow(true);
if (completions != null) {
mCurCompletions = completions;
@@ -1540,20 +1543,41 @@
return false;
}
}
- if ((flags&InputMethod.SHOW_FORCED) != 0) {
- mShowInputForced = true;
- }
return true;
}
-
+
+ /**
+ * A utility method to call {{@link #onShowInputRequested(int, boolean)}} and update internal
+ * states depending on its result. Since {@link #onShowInputRequested(int, boolean)} is
+ * exposed to IME authors as an overridable public method without {@code @CallSuper}, we have
+ * to have this method to ensure that those internal states are always updated no matter how
+ * {@link #onShowInputRequested(int, boolean)} is overridden by the IME author.
+ * @param flags Provides additional information about the show request,
+ * as per {@link InputMethod#showSoftInput InputMethod.showSoftInput()}.
+ * @param configChange This is true if we are re-showing due to a
+ * configuration change.
+ * @return Returns true to indicate that the window should be shown.
+ * @see #onShowInputRequested(int, boolean)
+ */
+ private boolean dispatchOnShowInputRequested(int flags, boolean configChange) {
+ final boolean result = onShowInputRequested(flags, configChange);
+ if (result) {
+ mShowInputFlags = flags;
+ } else {
+ mShowInputFlags = 0;
+ }
+ return result;
+ }
+
public void showWindow(boolean showInput) {
if (DEBUG) Log.v(TAG, "Showing window: showInput=" + showInput
+ " mShowInputRequested=" + mShowInputRequested
+ " mWindowAdded=" + mWindowAdded
+ " mWindowCreated=" + mWindowCreated
+ " mWindowVisible=" + mWindowVisible
- + " mInputStarted=" + mInputStarted);
-
+ + " mInputStarted=" + mInputStarted
+ + " mShowInputFlags=" + mShowInputFlags);
+
if (mInShowWindow) {
Log.w(TAG, "Re-entrance in to showWindow");
return;
@@ -2573,7 +2597,6 @@
p.println(" mShowInputRequested=" + mShowInputRequested
+ " mLastShowInputRequested=" + mLastShowInputRequested
- + " mShowInputForced=" + mShowInputForced
+ " mShowInputFlags=0x" + Integer.toHexString(mShowInputFlags));
p.println(" mCandidatesVisibility=" + mCandidatesVisibility
+ " mFullscreenApplied=" + mFullscreenApplied
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index f0673ff..b452341 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -3021,7 +3021,6 @@
*
* @param networkCallback The {@link NetworkCallback} that the system will call as the
* system default network changes.
- * @hide
*/
public void registerDefaultNetworkCallback(NetworkCallback networkCallback) {
// This works because if the NetworkCapabilities are null,
diff --git a/core/java/android/net/ConnectivityMetricsEvent.aidl b/core/java/android/net/ConnectivityMetricsEvent.aidl
index da17561..a027d7c 100644
--- a/core/java/android/net/ConnectivityMetricsEvent.aidl
+++ b/core/java/android/net/ConnectivityMetricsEvent.aidl
@@ -17,3 +17,4 @@
package android.net;
parcelable ConnectivityMetricsEvent;
+parcelable ConnectivityMetricsEvent.Reference;
diff --git a/core/java/android/net/ConnectivityMetricsEvent.java b/core/java/android/net/ConnectivityMetricsEvent.java
index 098f1e6..b5d67d3 100644
--- a/core/java/android/net/ConnectivityMetricsEvent.java
+++ b/core/java/android/net/ConnectivityMetricsEvent.java
@@ -78,4 +78,42 @@
return String.format("ConnectivityMetricsEvent(%d, %d, %d)", timestamp,
componentTag, eventTag);
}
+
+ /** {@hide} */
+ public static class Reference implements Parcelable {
+
+ public long value;
+
+ public Reference(long ref) {
+ this.value = ref;
+ }
+
+ /** Implement the Parcelable interface */
+ public static final Parcelable.Creator<Reference> CREATOR
+ = new Parcelable.Creator<Reference> (){
+ public Reference createFromParcel(Parcel source) {
+ return new Reference(source.readLong());
+ }
+
+ public Reference[] newArray(int size) {
+ return new Reference[size];
+ }
+ };
+
+ /** Implement the Parcelable interface */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(value);
+ }
+
+ public void readFromParcel(Parcel in) {
+ value = in.readLong();
+ }
+ }
}
diff --git a/core/java/android/net/ConnectivityMetricsLogger.java b/core/java/android/net/ConnectivityMetricsLogger.java
index 3ef8050..eafb8ac 100644
--- a/core/java/android/net/ConnectivityMetricsLogger.java
+++ b/core/java/android/net/ConnectivityMetricsLogger.java
@@ -15,6 +15,7 @@
*/
package android.net;
+import android.os.Bundle;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -28,14 +29,24 @@
public static final String CONNECTIVITY_METRICS_LOGGER_SERVICE = "connectivity_metrics_logger";
// Component Tags
- public static final int COMPONENT_TAG_CONNECTIVITY = 1;
- public static final int COMPONENT_TAG_BLUETOOTH = 2;
- public static final int COMPONENT_TAG_WIFI = 3;
- public static final int COMPONENT_TAG_TELECOM = 4;
- public static final int COMPONENT_TAG_TELEPHONY = 5;
+ public static final int COMPONENT_TAG_CONNECTIVITY = 0;
+ public static final int COMPONENT_TAG_BLUETOOTH = 1;
+ public static final int COMPONENT_TAG_WIFI = 2;
+ public static final int COMPONENT_TAG_TELECOM = 3;
+ public static final int COMPONENT_TAG_TELEPHONY = 4;
+
+ public static final int NUMBER_OF_COMPONENTS = 5;
+
+ // Event Tag
+ public static final int TAG_SKIPPED_EVENTS = -1;
+
+ public static final String DATA_KEY_EVENTS_COUNT = "count";
private IConnectivityMetricsLogger mService;
+ private long mServiceUnblockedTimestampMillis = 0;
+ private int mNumSkippedEvents = 0;
+
public ConnectivityMetricsLogger() {
mService = IConnectivityMetricsLogger.Stub.asInterface(ServiceManager.getService(
CONNECTIVITY_METRICS_LOGGER_SERVICE));
@@ -46,12 +57,51 @@
if (DBG) {
Log.d(TAG, "logEvent(" + componentTag + "," + eventTag + ") Service not ready");
}
- } else {
- try {
- mService.logEvent(new ConnectivityMetricsEvent(timestamp, componentTag, eventTag, data));
- } catch (RemoteException e) {
- Log.e(TAG, "Error logging event " + e.getMessage());
+ return;
+ }
+
+ if (mServiceUnblockedTimestampMillis > 0) {
+ if (System.currentTimeMillis() < mServiceUnblockedTimestampMillis) {
+ // Service is throttling events.
+ // Don't send new events because they will be dropped.
+ mNumSkippedEvents++;
+ return;
}
}
+
+ ConnectivityMetricsEvent skippedEventsEvent = null;
+ if (mNumSkippedEvents > 0) {
+ // Log number of skipped events
+ Bundle b = new Bundle();
+ b.putInt(DATA_KEY_EVENTS_COUNT, mNumSkippedEvents);
+ skippedEventsEvent = new ConnectivityMetricsEvent(mServiceUnblockedTimestampMillis,
+ componentTag, TAG_SKIPPED_EVENTS, b);
+
+ mServiceUnblockedTimestampMillis = 0;
+ }
+
+ ConnectivityMetricsEvent event = new ConnectivityMetricsEvent(timestamp, componentTag,
+ eventTag, data);
+
+ try {
+ long result;
+ if (skippedEventsEvent == null) {
+ result = mService.logEvent(event);
+ } else {
+ result = mService.logEvents(new ConnectivityMetricsEvent[]
+ {skippedEventsEvent, event});
+ }
+
+ if (result == 0) {
+ mNumSkippedEvents = 0;
+ } else {
+ mNumSkippedEvents++;
+ if (result > 0) { // events are throttled
+ mServiceUnblockedTimestampMillis = result;
+ }
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error logging event " + e.getMessage());
+ }
}
}
diff --git a/core/java/android/net/DhcpResults.java b/core/java/android/net/DhcpResults.java
index 97bd5d2..8c5f603 100644
--- a/core/java/android/net/DhcpResults.java
+++ b/core/java/android/net/DhcpResults.java
@@ -40,6 +40,9 @@
public int leaseDuration;
+ /** Link MTU option. 0 means unset. */
+ public int mtu;
+
public DhcpResults() {
super();
}
@@ -57,19 +60,7 @@
serverAddress = source.serverAddress;
vendorInfo = source.vendorInfo;
leaseDuration = source.leaseDuration;
- }
- }
-
- /**
- * Updates the DHCP fields that need to be retained from
- * original DHCP request if the current renewal shows them
- * being empty.
- */
- public void updateFromDhcpRequest(DhcpResults orig) {
- if (orig == null) return;
- if (gateway == null) gateway = orig.gateway;
- if (dnsServers.size() == 0) {
- dnsServers.addAll(orig.dnsServers);
+ mtu = source.mtu;
}
}
@@ -89,6 +80,7 @@
super.clear();
vendorInfo = null;
leaseDuration = 0;
+ mtu = 0;
}
@Override
@@ -98,6 +90,7 @@
str.append(" DHCP server ").append(serverAddress);
str.append(" Vendor info ").append(vendorInfo);
str.append(" lease ").append(leaseDuration).append(" seconds");
+ if (mtu != 0) str.append(" MTU ").append(mtu);
return str.toString();
}
@@ -113,7 +106,8 @@
return super.equals((StaticIpConfiguration) obj) &&
Objects.equals(serverAddress, target.serverAddress) &&
Objects.equals(vendorInfo, target.vendorInfo) &&
- leaseDuration == target.leaseDuration;
+ leaseDuration == target.leaseDuration &&
+ mtu == target.mtu;
}
/** Implement the Parcelable interface */
@@ -134,6 +128,7 @@
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(leaseDuration);
+ dest.writeInt(mtu);
NetworkUtils.parcelInetAddress(dest, serverAddress, flags);
dest.writeString(vendorInfo);
}
@@ -141,6 +136,7 @@
private static void readFromParcel(DhcpResults dhcpResults, Parcel in) {
StaticIpConfiguration.readFromParcel(dhcpResults, in);
dhcpResults.leaseDuration = in.readInt();
+ dhcpResults.mtu = in.readInt();
dhcpResults.serverAddress = (Inet4Address) NetworkUtils.unparcelInetAddress(in);
dhcpResults.vendorInfo = in.readString();
}
diff --git a/core/java/android/net/DnsPinger.java b/core/java/android/net/DnsPinger.java
deleted file mode 100644
index 7acf3f5..0000000
--- a/core/java/android/net/DnsPinger.java
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net;
-
-import android.content.Context;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.SystemClock;
-import android.provider.Settings;
-import android.util.Log;
-
-import com.android.internal.util.Protocol;
-
-import java.io.IOException;
-import java.net.DatagramPacket;
-import java.net.DatagramSocket;
-import java.net.InetAddress;
-import java.net.NetworkInterface;
-import java.net.SocketTimeoutException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Random;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * Performs a simple DNS "ping" by sending a "server status" query packet to the
- * DNS server. As long as the server replies, we consider it a success.
- * <p>
- * We do not use a simple hostname lookup because that could be cached and the
- * API may not differentiate between a time out and a failure lookup (which we
- * really care about).
- * <p>
- *
- * @hide
- */
-public final class DnsPinger extends Handler {
- private static final boolean DBG = false;
-
- private static final int RECEIVE_POLL_INTERVAL_MS = 200;
- private static final int DNS_PORT = 53;
-
- /** Short socket timeout so we don't block one any 'receive' call */
- private static final int SOCKET_TIMEOUT_MS = 1;
-
- /** Used to generate IDs */
- private static final Random sRandom = new Random();
- private static final AtomicInteger sCounter = new AtomicInteger();
-
- private ConnectivityManager mConnectivityManager = null;
- private final Context mContext;
- private final int mConnectionType;
- private final Handler mTarget;
- private final ArrayList<InetAddress> mDefaultDns;
- private String TAG;
-
- //Invalidates old dns requests upon a cancel
- private AtomicInteger mCurrentToken = new AtomicInteger();
-
- private static final int BASE = Protocol.BASE_DNS_PINGER;
-
- /**
- * Async response packet for dns pings.
- * arg1 is the ID of the ping, also returned by {@link #pingDnsAsync(InetAddress, int, int)}
- * arg2 is the delay, or is negative on error.
- */
- public static final int DNS_PING_RESULT = BASE;
- /** An error code for a {@link #DNS_PING_RESULT} packet */
- public static final int TIMEOUT = -1;
- /** An error code for a {@link #DNS_PING_RESULT} packet */
- public static final int SOCKET_EXCEPTION = -2;
-
- /**
- * Send a new ping via a socket. arg1 is ID, arg2 is timeout, obj is InetAddress to ping
- */
- private static final int ACTION_PING_DNS = BASE + 1;
- private static final int ACTION_LISTEN_FOR_RESPONSE = BASE + 2;
- private static final int ACTION_CANCEL_ALL_PINGS = BASE + 3;
-
- private List<ActivePing> mActivePings = new ArrayList<ActivePing>();
- private int mEventCounter;
-
- private class ActivePing {
- DatagramSocket socket;
- int internalId;
- short packetId;
- int timeout;
- Integer result;
- long start = SystemClock.elapsedRealtime();
- }
-
- /* Message argument for ACTION_PING_DNS */
- private class DnsArg {
- InetAddress dns;
- int seq;
-
- DnsArg(InetAddress d, int s) {
- dns = d;
- seq = s;
- }
- }
-
- public DnsPinger(Context context, String TAG, Looper looper,
- Handler target, int connectionType) {
- super(looper);
- this.TAG = TAG;
- mContext = context;
- mTarget = target;
- mConnectionType = connectionType;
- if (!ConnectivityManager.isNetworkTypeValid(connectionType)) {
- throw new IllegalArgumentException("Invalid connectionType in constructor: "
- + connectionType);
- }
- mDefaultDns = new ArrayList<InetAddress>();
- mDefaultDns.add(getDefaultDns());
- mEventCounter = 0;
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case ACTION_PING_DNS:
- DnsArg dnsArg = (DnsArg) msg.obj;
- if (dnsArg.seq != mCurrentToken.get()) {
- break;
- }
- try {
- ActivePing newActivePing = new ActivePing();
- InetAddress dnsAddress = dnsArg.dns;
- newActivePing.internalId = msg.arg1;
- newActivePing.timeout = msg.arg2;
- newActivePing.socket = new DatagramSocket();
- // Set some socket properties
- newActivePing.socket.setSoTimeout(SOCKET_TIMEOUT_MS);
-
- // Try to bind but continue ping if bind fails
- try {
- newActivePing.socket.setNetworkInterface(NetworkInterface.getByName(
- getCurrentLinkProperties().getInterfaceName()));
- } catch (Exception e) {
- loge("sendDnsPing::Error binding to socket " + e);
- }
-
- newActivePing.packetId = (short) sRandom.nextInt();
- byte[] buf = mDnsQuery.clone();
- buf[0] = (byte) (newActivePing.packetId >> 8);
- buf[1] = (byte) newActivePing.packetId;
-
- // Send the DNS query
- DatagramPacket packet = new DatagramPacket(buf,
- buf.length, dnsAddress, DNS_PORT);
- if (DBG) {
- log("Sending a ping " + newActivePing.internalId +
- " to " + dnsAddress.getHostAddress()
- + " with packetId " + newActivePing.packetId + ".");
- }
-
- newActivePing.socket.send(packet);
- mActivePings.add(newActivePing);
- mEventCounter++;
- sendMessageDelayed(obtainMessage(ACTION_LISTEN_FOR_RESPONSE, mEventCounter, 0),
- RECEIVE_POLL_INTERVAL_MS);
- } catch (IOException e) {
- sendResponse(msg.arg1, -9999, SOCKET_EXCEPTION);
- }
- break;
- case ACTION_LISTEN_FOR_RESPONSE:
- if (msg.arg1 != mEventCounter) {
- break;
- }
- for (ActivePing curPing : mActivePings) {
- try {
- /** Each socket will block for {@link #SOCKET_TIMEOUT_MS} in receive() */
- byte[] responseBuf = new byte[2];
- DatagramPacket replyPacket = new DatagramPacket(responseBuf, 2);
- curPing.socket.receive(replyPacket);
- // Check that ID field matches (we're throwing out the rest of the packet)
- if (responseBuf[0] == (byte) (curPing.packetId >> 8) &&
- responseBuf[1] == (byte) curPing.packetId) {
- curPing.result =
- (int) (SystemClock.elapsedRealtime() - curPing.start);
- } else {
- if (DBG) {
- log("response ID didn't match, ignoring packet");
- }
- }
- } catch (SocketTimeoutException e) {
- // A timeout here doesn't mean anything - squelsh this exception
- } catch (Exception e) {
- if (DBG) {
- log("DnsPinger.pingDns got socket exception: " + e);
- }
- curPing.result = SOCKET_EXCEPTION;
- }
- }
- Iterator<ActivePing> iter = mActivePings.iterator();
- while (iter.hasNext()) {
- ActivePing curPing = iter.next();
- if (curPing.result != null) {
- sendResponse(curPing.internalId, curPing.packetId, curPing.result);
- curPing.socket.close();
- iter.remove();
- } else if (SystemClock.elapsedRealtime() >
- curPing.start + curPing.timeout) {
- sendResponse(curPing.internalId, curPing.packetId, TIMEOUT);
- curPing.socket.close();
- iter.remove();
- }
- }
- if (!mActivePings.isEmpty()) {
- sendMessageDelayed(obtainMessage(ACTION_LISTEN_FOR_RESPONSE, mEventCounter, 0),
- RECEIVE_POLL_INTERVAL_MS);
- }
- break;
- case ACTION_CANCEL_ALL_PINGS:
- for (ActivePing activePing : mActivePings)
- activePing.socket.close();
- mActivePings.clear();
- break;
- }
- }
-
- /**
- * Returns a list of DNS addresses, coming from either the link properties of the
- * specified connection or the default system DNS if the link properties has no dnses.
- * @return a non-empty non-null list
- */
- public List<InetAddress> getDnsList() {
- LinkProperties curLinkProps = getCurrentLinkProperties();
- if (curLinkProps == null) {
- loge("getCurLinkProperties:: LP for type" + mConnectionType + " is null!");
- return mDefaultDns;
- }
-
- Collection<InetAddress> dnses = curLinkProps.getDnsServers();
- if (dnses == null || dnses.size() == 0) {
- loge("getDns::LinkProps has null dns - returning default");
- return mDefaultDns;
- }
-
- return new ArrayList<InetAddress>(dnses);
- }
-
- /**
- * Send a ping. The response will come via a {@link #DNS_PING_RESULT} to the handler
- * specified at creation.
- * @param dns address of dns server to ping
- * @param timeout timeout for ping
- * @return an ID field, which will also be included in the {@link #DNS_PING_RESULT} message.
- */
- public int pingDnsAsync(InetAddress dns, int timeout, int delay) {
- int id = sCounter.incrementAndGet();
- sendMessageDelayed(obtainMessage(ACTION_PING_DNS, id, timeout,
- new DnsArg(dns, mCurrentToken.get())), delay);
- return id;
- }
-
- public void cancelPings() {
- mCurrentToken.incrementAndGet();
- obtainMessage(ACTION_CANCEL_ALL_PINGS).sendToTarget();
- }
-
- private void sendResponse(int internalId, int externalId, int responseVal) {
- if(DBG) {
- log("Responding to packet " + internalId +
- " externalId " + externalId +
- " and val " + responseVal);
- }
- mTarget.sendMessage(obtainMessage(DNS_PING_RESULT, internalId, responseVal));
- }
-
- private LinkProperties getCurrentLinkProperties() {
- if (mConnectivityManager == null) {
- mConnectivityManager = (ConnectivityManager) mContext.getSystemService(
- Context.CONNECTIVITY_SERVICE);
- }
-
- return mConnectivityManager.getLinkProperties(mConnectionType);
- }
-
- private InetAddress getDefaultDns() {
- String dns = Settings.Global.getString(mContext.getContentResolver(),
- Settings.Global.DEFAULT_DNS_SERVER);
- if (dns == null || dns.length() == 0) {
- dns = mContext.getResources().getString(
- com.android.internal.R.string.config_default_dns_server);
- }
- try {
- return NetworkUtils.numericToInetAddress(dns);
- } catch (IllegalArgumentException e) {
- loge("getDefaultDns::malformed default dns address");
- return null;
- }
- }
-
- private static final byte[] mDnsQuery = new byte[] {
- 0, 0, // [0-1] is for ID (will set each time)
- 1, 0, // [2-3] are flags. Set byte[2] = 1 for recursion desired (RD) on. Currently on.
- 0, 1, // [4-5] bytes are for number of queries (QCOUNT)
- 0, 0, // [6-7] unused count field for dns response packets
- 0, 0, // [8-9] unused count field for dns response packets
- 0, 0, // [10-11] unused count field for dns response packets
- 3, 'w', 'w', 'w',
- 6, 'g', 'o', 'o', 'g', 'l', 'e',
- 3, 'c', 'o', 'm',
- 0, // null terminator of address (also called empty TLD)
- 0, 1, // QTYPE, set to 1 = A (host address)
- 0, 1 // QCLASS, set to 1 = IN (internet)
- };
-
- private void log(String s) {
- Log.d(TAG, s);
- }
-
- private void loge(String s) {
- Log.e(TAG, s);
- }
-}
diff --git a/core/java/android/net/IConnectivityMetricsLogger.aidl b/core/java/android/net/IConnectivityMetricsLogger.aidl
index 2778671..a83a019 100644
--- a/core/java/android/net/IConnectivityMetricsLogger.aidl
+++ b/core/java/android/net/IConnectivityMetricsLogger.aidl
@@ -16,15 +16,28 @@
package android.net;
+import android.app.PendingIntent;
import android.net.ConnectivityMetricsEvent;
-import android.net.IConnectivityMetricsLoggerSubscriber;
/** {@hide} */
interface IConnectivityMetricsLogger {
- void logEvent(in ConnectivityMetricsEvent event);
- void logEvents(in ConnectivityMetricsEvent[] events);
+ /**
+ * @return 0 on success
+ * <0 if error happened
+ * >0 timestamp after which new events will be accepted
+ */
+ long logEvent(in ConnectivityMetricsEvent event);
+ long logEvents(in ConnectivityMetricsEvent[] events);
- boolean subscribe(in IConnectivityMetricsLoggerSubscriber subscriber);
- void unsubscribe(in IConnectivityMetricsLoggerSubscriber subscriber);
+ /**
+ * @param reference of the last event previously returned. The function will return
+ * events following it.
+ * If 0 then all events will be returned.
+ * After the function call it will contain reference of the last event.
+ */
+ ConnectivityMetricsEvent[] getEvents(inout ConnectivityMetricsEvent.Reference reference);
+
+ boolean register(in PendingIntent newEventsIntent);
+ void unregister(in PendingIntent newEventsIntent);
}
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 9e360e1..20c2168 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -200,14 +200,6 @@
*/
public static final int CMD_PREVENT_AUTOMATIC_RECONNECT = BASE + 15;
- /**
- * Sent by ConnectivityService to the NetworkAgent to install an APF program in the network
- * chipset for use to filter packets.
- *
- * obj = byte[] containing the APF program bytecode.
- */
- public static final int CMD_PUSH_APF_PROGRAM = BASE + 16;
-
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
NetworkCapabilities nc, LinkProperties lp, int score) {
this(looper, context, logTag, ni, nc, lp, score, null);
@@ -327,10 +319,6 @@
preventAutomaticReconnect();
break;
}
- case CMD_PUSH_APF_PROGRAM: {
- installPacketFilter((byte[]) msg.obj);
- break;
- }
}
}
@@ -506,15 +494,6 @@
protected void preventAutomaticReconnect() {
}
- /**
- * Install a packet filter.
- * @param filter an APF program to filter incoming packets.
- * @return {@code true} if filter successfully installed, {@code false} otherwise.
- */
- protected boolean installPacketFilter(byte[] filter) {
- return false;
- }
-
protected void log(String s) {
Log.d(LOG_TAG, "NetworkAgent: " + s);
}
diff --git a/core/java/android/net/NetworkMisc.java b/core/java/android/net/NetworkMisc.java
index 748699e..5511a24 100644
--- a/core/java/android/net/NetworkMisc.java
+++ b/core/java/android/net/NetworkMisc.java
@@ -56,22 +56,6 @@
*/
public String subscriberId;
- /**
- * Version of APF instruction set supported for packet filtering. 0 indicates no support for
- * packet filtering using APF programs.
- */
- public int apfVersionSupported;
-
- /**
- * Maximum size of APF program allowed.
- */
- public int maximumApfProgramSize;
-
- /**
- * Format of packets passed to APF filter. Should be one of ARPHRD_*
- */
- public int apfPacketFormat;
-
public NetworkMisc() {
}
@@ -81,9 +65,6 @@
explicitlySelected = nm.explicitlySelected;
acceptUnvalidated = nm.acceptUnvalidated;
subscriberId = nm.subscriberId;
- apfVersionSupported = nm.apfVersionSupported;
- maximumApfProgramSize = nm.maximumApfProgramSize;
- apfPacketFormat = nm.apfPacketFormat;
}
}
@@ -98,9 +79,6 @@
out.writeInt(explicitlySelected ? 1 : 0);
out.writeInt(acceptUnvalidated ? 1 : 0);
out.writeString(subscriberId);
- out.writeInt(apfVersionSupported);
- out.writeInt(maximumApfProgramSize);
- out.writeInt(apfPacketFormat);
}
public static final Creator<NetworkMisc> CREATOR = new Creator<NetworkMisc>() {
@@ -111,9 +89,6 @@
networkMisc.explicitlySelected = in.readInt() != 0;
networkMisc.acceptUnvalidated = in.readInt() != 0;
networkMisc.subscriberId = in.readString();
- networkMisc.apfVersionSupported = in.readInt();
- networkMisc.maximumApfProgramSize = in.readInt();
- networkMisc.apfPacketFormat = in.readInt();
return networkMisc;
}
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 8738424..e464a4a 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -57,6 +57,10 @@
public static final int RULE_REJECT_METERED = 1;
/** Reject traffic on all networks. */
public static final int RULE_REJECT_ALL = 2;
+ /** Allow traffic on metered networks. */
+ public static final int RULE_ALLOW_METERED = 3;
+ /** Temporarily allow traffic on metered networks because app is on foreground. */
+ public static final int RULE_TEMPORARY_ALLOW_METERED = 4;
public static final int FIREWALL_RULE_DEFAULT = 0;
public static final int FIREWALL_RULE_ALLOW = 1;
diff --git a/core/java/android/net/NetworkScorerAppManager.java b/core/java/android/net/NetworkScorerAppManager.java
index e555fa4..29291ca 100644
--- a/core/java/android/net/NetworkScorerAppManager.java
+++ b/core/java/android/net/NetworkScorerAppManager.java
@@ -19,11 +19,9 @@
import android.Manifest;
import android.Manifest.permission;
import android.annotation.Nullable;
-import android.app.AppOpsManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.UserHandle;
@@ -69,12 +67,32 @@
*/
public final String mConfigurationActivityClassName;
+ /**
+ * Optional class name of the scoring service we can bind to. Null if none is set.
+ */
+ public final String mScoringServiceClassName;
+
public NetworkScorerAppData(String packageName, int packageUid, CharSequence scorerName,
- @Nullable String configurationActivityClassName) {
+ @Nullable String configurationActivityClassName,
+ @Nullable String scoringServiceClassName) {
mScorerName = scorerName;
mPackageName = packageName;
mPackageUid = packageUid;
mConfigurationActivityClassName = configurationActivityClassName;
+ mScoringServiceClassName = scoringServiceClassName;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("NetworkScorerAppData{");
+ sb.append("mPackageName='").append(mPackageName).append('\'');
+ sb.append(", mPackageUid=").append(mPackageUid);
+ sb.append(", mScorerName=").append(mScorerName);
+ sb.append(", mConfigurationActivityClassName='").append(mConfigurationActivityClassName)
+ .append('\'');
+ sb.append(", mScoringServiceClassName='").append(mScoringServiceClassName).append('\'');
+ sb.append('}');
+ return sb.toString();
}
}
@@ -128,18 +146,27 @@
Intent intent = new Intent(NetworkScoreManager.ACTION_CUSTOM_ENABLE);
intent.setPackage(receiverInfo.packageName);
List<ResolveInfo> configActivities = pm.queryIntentActivities(intent, 0 /* flags */);
- if (!configActivities.isEmpty()) {
+ if (configActivities != null && !configActivities.isEmpty()) {
ActivityInfo activityInfo = configActivities.get(0).activityInfo;
if (activityInfo != null) {
configurationActivityClassName = activityInfo.name;
}
}
+ // Find the scoring service class we can bind to, if any.
+ String scoringServiceClassName = null;
+ Intent serviceIntent = new Intent(NetworkScoreManager.ACTION_SCORE_NETWORKS);
+ serviceIntent.setPackage(receiverInfo.packageName);
+ ResolveInfo resolveServiceInfo = pm.resolveService(serviceIntent, 0 /* flags */);
+ if (resolveServiceInfo != null && resolveServiceInfo.serviceInfo != null) {
+ scoringServiceClassName = resolveServiceInfo.serviceInfo.name;
+ }
+
// NOTE: loadLabel will attempt to load the receiver's label and fall back to the
// app label if none is present.
scorers.add(new NetworkScorerAppData(receiverInfo.packageName,
receiverInfo.applicationInfo.uid, receiverInfo.loadLabel(pm),
- configurationActivityClassName));
+ configurationActivityClassName, scoringServiceClassName));
}
return scorers;
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index d847cd0..b32b2cc 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -18,6 +18,7 @@
import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
import static android.net.ConnectivityManager.TYPE_ETHERNET;
+import static android.net.ConnectivityManager.TYPE_PROXY;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
import static android.net.ConnectivityManager.TYPE_WIMAX;
@@ -68,6 +69,7 @@
public static final int MATCH_MOBILE_WILDCARD = 6;
public static final int MATCH_WIFI_WILDCARD = 7;
public static final int MATCH_BLUETOOTH = 8;
+ public static final int MATCH_PROXY = 9;
/**
* Set of {@link NetworkInfo#getType()} that reflect data usage.
@@ -157,6 +159,14 @@
return new NetworkTemplate(MATCH_BLUETOOTH, null, null);
}
+ /**
+ * Template to combine all {@link ConnectivityManager#TYPE_PROXY} style
+ * networks together.
+ */
+ public static NetworkTemplate buildTemplateProxy() {
+ return new NetworkTemplate(MATCH_PROXY, null, null);
+ }
+
private final int mMatchRule;
private final String mSubscriberId;
@@ -293,6 +303,8 @@
return matchesWifiWildcard(ident);
case MATCH_BLUETOOTH:
return matchesBluetooth(ident);
+ case MATCH_PROXY:
+ return matchesProxy(ident);
default:
throw new IllegalArgumentException("unknown network template");
}
@@ -401,6 +413,13 @@
return false;
}
+ /**
+ * Check if matches Proxy network template.
+ */
+ private boolean matchesProxy(NetworkIdentity ident) {
+ return ident.mType == TYPE_PROXY;
+ }
+
private static String getMatchRuleName(int matchRule) {
switch (matchRule) {
case MATCH_MOBILE_3G_LOWER:
@@ -419,6 +438,8 @@
return "WIFI_WILDCARD";
case MATCH_BLUETOOTH:
return "BLUETOOTH";
+ case MATCH_PROXY:
+ return "PROXY";
default:
return "UNKNOWN";
}
diff --git a/core/java/android/net/metrics/CaptivePortalCheckResultEvent.java b/core/java/android/net/metrics/CaptivePortalCheckResultEvent.java
new file mode 100644
index 0000000..2239a25
--- /dev/null
+++ b/core/java/android/net/metrics/CaptivePortalCheckResultEvent.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.metrics;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * {@hide}
+ */
+public class CaptivePortalCheckResultEvent extends IpConnectivityEvent implements Parcelable {
+ public static final String TAG = "CaptivePortalCheckResultEvent";
+
+ private int mNetId;
+ private int mResult;
+
+ public CaptivePortalCheckResultEvent(int netId, int result) {
+ mNetId = netId;
+ mResult = result;
+ }
+
+ public CaptivePortalCheckResultEvent(Parcel in) {
+ mNetId = in.readInt();
+ mResult = in.readInt();
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mNetId);
+ out.writeInt(mResult);
+ }
+
+ public static final Parcelable.Creator<CaptivePortalCheckResultEvent> CREATOR
+ = new Parcelable.Creator<CaptivePortalCheckResultEvent>() {
+ public CaptivePortalCheckResultEvent createFromParcel(Parcel in) {
+ return new CaptivePortalCheckResultEvent(in);
+ }
+
+ public CaptivePortalCheckResultEvent[] newArray(int size) {
+ return new CaptivePortalCheckResultEvent[size];
+ }
+ };
+
+ public static void logEvent(int netId, int result) {
+ IpConnectivityEvent.logEvent(IpConnectivityEvent.IPCE_NETMON_CHECK_RESULT,
+ new CaptivePortalCheckResultEvent(netId, result));
+ }
+};
diff --git a/core/java/android/net/metrics/CaptivePortalStateChangeEvent.java b/core/java/android/net/metrics/CaptivePortalStateChangeEvent.java
new file mode 100644
index 0000000..00808c1
--- /dev/null
+++ b/core/java/android/net/metrics/CaptivePortalStateChangeEvent.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.metrics;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * {@hide}
+ */
+public class CaptivePortalStateChangeEvent extends IpConnectivityEvent implements Parcelable {
+ public static final String TAG = "CaptivePortalStateChangeEvent";
+
+ public static final int NETWORK_MONITOR_CONNECTED = 0;
+ public static final int NETWORK_MONITOR_DISCONNECTED = 1;
+ public static final int NETWORK_MONITOR_VALIDATED = 2;
+ private int mState;
+
+ public CaptivePortalStateChangeEvent(int state) {
+ mState = state;
+ }
+
+ public CaptivePortalStateChangeEvent(Parcel in) {
+ mState = in.readInt();
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mState);
+ }
+
+ public static final Parcelable.Creator<CaptivePortalStateChangeEvent> CREATOR
+ = new Parcelable.Creator<CaptivePortalStateChangeEvent>() {
+ public CaptivePortalStateChangeEvent createFromParcel(Parcel in) {
+ return new CaptivePortalStateChangeEvent(in);
+ }
+
+ public CaptivePortalStateChangeEvent[] newArray(int size) {
+ return new CaptivePortalStateChangeEvent[size];
+ }
+ };
+
+ public static void logEvent(int state) {
+ IpConnectivityEvent.logEvent(IpConnectivityEvent.IPCE_NETMON_STATE_CHANGE,
+ new CaptivePortalStateChangeEvent(state));
+ }
+};
diff --git a/core/java/android/net/metrics/ConnectivityServiceChangeEvent.java b/core/java/android/net/metrics/ConnectivityServiceChangeEvent.java
new file mode 100644
index 0000000..c6fcb2d
--- /dev/null
+++ b/core/java/android/net/metrics/ConnectivityServiceChangeEvent.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.metrics;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * {@hide}
+ */
+public class ConnectivityServiceChangeEvent extends IpConnectivityEvent implements Parcelable {
+ public static final String TAG = "ConnectivityServiceChangeEvent";
+
+ private int mNetId;
+
+ public ConnectivityServiceChangeEvent(int netId) {
+ mNetId = netId;
+ }
+
+ public ConnectivityServiceChangeEvent(Parcel in) {
+ mNetId = in.readInt();
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mNetId);
+ }
+
+ public static final Parcelable.Creator<ConnectivityServiceChangeEvent> CREATOR
+ = new Parcelable.Creator<ConnectivityServiceChangeEvent>() {
+ public ConnectivityServiceChangeEvent createFromParcel(Parcel in) {
+ return new ConnectivityServiceChangeEvent(in);
+ }
+
+ public ConnectivityServiceChangeEvent[] newArray(int size) {
+ return new ConnectivityServiceChangeEvent[size];
+ }
+ };
+
+ public static void logEvent(int netId) {
+ IpConnectivityEvent.logEvent(IpConnectivityEvent.IPCE_CONSRV_DEFAULT_NET_CHANGE,
+ new ConnectivityServiceChangeEvent(netId));
+ }
+};
diff --git a/core/java/android/net/metrics/DhcpClientEvent.java b/core/java/android/net/metrics/DhcpClientEvent.java
new file mode 100644
index 0000000..7b44664
--- /dev/null
+++ b/core/java/android/net/metrics/DhcpClientEvent.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.metrics;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * {@hide}
+ */
+public class DhcpClientEvent extends IpConnectivityEvent implements Parcelable {
+ public static final String TAG = "DhcpClientEvent";
+
+ private String mIfName;
+ private String mMsg;
+
+ public DhcpClientEvent(String ifName, String msg) {
+ mIfName = ifName;
+ mMsg = msg;
+ }
+
+ public DhcpClientEvent(Parcel in) {
+ mIfName = in.readString();
+ mMsg = in.readString();
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(mIfName);
+ out.writeString(mMsg);
+ }
+
+ public static final Parcelable.Creator<DhcpClientEvent> CREATOR
+ = new Parcelable.Creator<DhcpClientEvent>() {
+ public DhcpClientEvent createFromParcel(Parcel in) {
+ return new DhcpClientEvent(in);
+ }
+
+ public DhcpClientEvent[] newArray(int size) {
+ return new DhcpClientEvent[size];
+ }
+ };
+
+ public static void logEvent(int eventType, String ifName, String msg) {
+ IpConnectivityEvent.logEvent(eventType, new DhcpClientEvent(ifName, msg));
+ }
+};
diff --git a/core/java/android/net/metrics/IpConnectivityEvent.java b/core/java/android/net/metrics/IpConnectivityEvent.java
new file mode 100644
index 0000000..ec42890
--- /dev/null
+++ b/core/java/android/net/metrics/IpConnectivityEvent.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.metrics;
+
+import android.net.ConnectivityMetricsLogger;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * {@hide}
+ */
+public class IpConnectivityEvent implements Parcelable {
+ // IPRM = IpReachabilityMonitor
+ // DHCP = DhcpClient
+ // NETMON = NetworkMonitorEvent
+ // CONSRV = ConnectivityServiceEvent
+ public static final String TAG = "IpConnectivityEvent";
+ public static final int IPCE_IPRM_BASE = 0*1024;
+ public static final int IPCE_DHCP_BASE = 1*1024;
+ public static final int IPCE_NETMON_BASE = 2*1024;
+ public static final int IPCE_CONSRV_BASE = 3*1024;
+
+ public static final int IPCE_IPRM_PROBE_RESULT = IPCE_IPRM_BASE + 0;
+ public static final int IPCE_IPRM_MESSAGE_RECEIVED = IPCE_IPRM_BASE + 1;
+ public static final int IPCE_DHCP_RECV_ERROR = IPCE_DHCP_BASE + 0;
+ public static final int IPCE_DHCP_PARSE_ERROR = IPCE_DHCP_BASE + 1;
+ public static final int IPCE_DHCP_TIMEOUT = IPCE_DHCP_BASE + 2;
+ public static final int IPCE_DHCP_STATE_CHANGE = IPCE_DHCP_BASE + 3;
+ public static final int IPCE_NETMON_STATE_CHANGE = IPCE_NETMON_BASE + 0;
+ public static final int IPCE_NETMON_CHECK_RESULT = IPCE_NETMON_BASE + 1;
+ public static final int IPCE_CONSRV_DEFAULT_NET_CHANGE = IPCE_CONSRV_BASE + 0;
+
+ private static ConnectivityMetricsLogger mMetricsLogger = new ConnectivityMetricsLogger();
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ }
+
+ public static void logEvent(int tag, IpConnectivityEvent event) {
+ long timestamp = System.currentTimeMillis();
+ mMetricsLogger.logEvent(timestamp, ConnectivityMetricsLogger.COMPONENT_TAG_CONNECTIVITY,
+ tag, event);
+ }
+};
diff --git a/core/java/android/net/metrics/IpReachabilityMonitorMessageEvent.java b/core/java/android/net/metrics/IpReachabilityMonitorMessageEvent.java
new file mode 100644
index 0000000..e71b0be
--- /dev/null
+++ b/core/java/android/net/metrics/IpReachabilityMonitorMessageEvent.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.metrics;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * {@hide}
+ */
+public class IpReachabilityMonitorMessageEvent extends IpConnectivityEvent
+ implements Parcelable {
+ public static final String TAG = "IpReachabilityMonitorMessageEvent";
+
+ private String mIfName;
+ private String mDestination;
+ private int mMsgType;
+ private int mNudState;
+
+ public IpReachabilityMonitorMessageEvent(String ifName, String destination, int msgType,
+ int nudState) {
+ mIfName = ifName;
+ mDestination = destination;
+ mMsgType = msgType;
+ mNudState = nudState;
+ }
+
+ public IpReachabilityMonitorMessageEvent(Parcel in) {
+ mIfName = in.readString();
+ mDestination = in.readString();
+ mMsgType = in.readInt();
+ mNudState = in.readInt();
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(mIfName);
+ out.writeString(mDestination);
+ out.writeInt(mMsgType);
+ out.writeInt(mNudState);
+ }
+
+ public static final Parcelable.Creator<IpReachabilityMonitorMessageEvent> CREATOR
+ = new Parcelable.Creator<IpReachabilityMonitorMessageEvent>() {
+ public IpReachabilityMonitorMessageEvent createFromParcel(Parcel in) {
+ return new IpReachabilityMonitorMessageEvent(in);
+ }
+
+ public IpReachabilityMonitorMessageEvent[] newArray(int size) {
+ return new IpReachabilityMonitorMessageEvent[size];
+ }
+ };
+
+ public static void logEvent(String ifName, String destination, int msgType, int nudState) {
+ IpConnectivityEvent.logEvent(IpConnectivityEvent.IPCE_IPRM_MESSAGE_RECEIVED,
+ new IpReachabilityMonitorMessageEvent(ifName, destination, msgType, nudState));
+ }
+};
diff --git a/core/java/android/net/metrics/IpReachabilityMonitorProbeEvent.java b/core/java/android/net/metrics/IpReachabilityMonitorProbeEvent.java
new file mode 100644
index 0000000..182b778
--- /dev/null
+++ b/core/java/android/net/metrics/IpReachabilityMonitorProbeEvent.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.metrics;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * {@hide}
+ */
+public class IpReachabilityMonitorProbeEvent extends IpConnectivityEvent
+ implements Parcelable {
+ public static final String TAG = "IpReachabilityMonitorProbeEvent";
+
+ private String mIfName;
+ private String mDestination;
+ private boolean mSuccess;
+
+ public IpReachabilityMonitorProbeEvent(String ifName, String destination, boolean success) {
+ mIfName = ifName;
+ mDestination = destination;
+ mSuccess = success;
+ }
+
+ public IpReachabilityMonitorProbeEvent(Parcel in) {
+ mIfName = in.readString();
+ mDestination = in.readString();
+ mSuccess = in.readByte() > 0 ? true : false;
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(mIfName);
+ out.writeString(mDestination);
+ out.writeByte((byte)(mSuccess ? 1 : 0));
+ }
+
+ public static final Parcelable.Creator<IpReachabilityMonitorProbeEvent> CREATOR
+ = new Parcelable.Creator<IpReachabilityMonitorProbeEvent>() {
+ public IpReachabilityMonitorProbeEvent createFromParcel(Parcel in) {
+ return new IpReachabilityMonitorProbeEvent(in);
+ }
+
+ public IpReachabilityMonitorProbeEvent[] newArray(int size) {
+ return new IpReachabilityMonitorProbeEvent[size];
+ }
+ };
+
+ public static void logEvent(String ifName, String destination, boolean success) {
+ IpConnectivityEvent.logEvent(IpConnectivityEvent.IPCE_IPRM_PROBE_RESULT,
+ new IpReachabilityMonitorProbeEvent(ifName, destination, success));
+ }
+};
diff --git a/core/java/android/net/nsd/NsdServiceInfo.java b/core/java/android/net/nsd/NsdServiceInfo.java
index 6fdb0d0..4a06fb1 100644
--- a/core/java/android/net/nsd/NsdServiceInfo.java
+++ b/core/java/android/net/nsd/NsdServiceInfo.java
@@ -16,8 +16,11 @@
package android.net.nsd;
+import android.annotation.NonNull;
import android.os.Parcelable;
import android.os.Parcel;
+import android.text.TextUtils;
+import android.util.Base64;
import android.util.Log;
import android.util.ArrayMap;
@@ -95,8 +98,99 @@
mPort = p;
}
+ /**
+ * Unpack txt information from a base-64 encoded byte array.
+ *
+ * @param rawRecords The raw base64 encoded records string read from netd.
+ *
+ * @hide
+ */
+ public void setTxtRecords(@NonNull String rawRecords) {
+ byte[] txtRecordsRawBytes = Base64.decode(rawRecords, Base64.DEFAULT);
+
+ // There can be multiple TXT records after each other. Each record has to following format:
+ //
+ // byte type required meaning
+ // ------------------- ------------------- -------- ----------------------------------
+ // 0 unsigned 8 bit yes size of record excluding this byte
+ // 1 - n ASCII but not '=' yes key
+ // n + 1 '=' optional separator of key and value
+ // n + 2 - record size uninterpreted bytes optional value
+ //
+ // Example legal records:
+ // [11, 'm', 'y', 'k', 'e', 'y', '=', 0x0, 0x4, 0x65, 0x7, 0xff]
+ // [17, 'm', 'y', 'K', 'e', 'y', 'W', 'i', 't', 'h', 'N', 'o', 'V', 'a', 'l', 'u', 'e', '=']
+ // [12, 'm', 'y', 'B', 'o', 'o', 'l', 'e', 'a', 'n', 'K', 'e', 'y']
+ //
+ // Example corrupted records
+ // [3, =, 1, 2] <- key is empty
+ // [3, 0, =, 2] <- key contains non-ASCII character. We handle this by replacing the
+ // invalid characters instead of skipping the record.
+ // [30, 'a', =, 2] <- length exceeds total left over bytes in the TXT records array, we
+ // handle this by reducing the length of the record as needed.
+ int pos = 0;
+ while (pos < txtRecordsRawBytes.length) {
+ // recordLen is an unsigned 8 bit value
+ int recordLen = txtRecordsRawBytes[pos] & 0xff;
+ pos += 1;
+
+ try {
+ if (recordLen == 0) {
+ throw new IllegalArgumentException("Zero sized txt record");
+ } else if (pos + recordLen > txtRecordsRawBytes.length) {
+ Log.w(TAG, "Corrupt record length (pos = " + pos + "): " + recordLen);
+ recordLen = txtRecordsRawBytes.length - pos;
+ }
+
+ // Decode key-value records
+ String key = null;
+ byte[] value = null;
+ int valueLen = 0;
+ for (int i = pos; i < pos + recordLen; i++) {
+ if (key == null) {
+ if (txtRecordsRawBytes[i] == '=') {
+ key = new String(txtRecordsRawBytes, pos, i - pos,
+ StandardCharsets.US_ASCII);
+ }
+ } else {
+ if (value == null) {
+ value = new byte[recordLen - key.length() - 1];
+ }
+ value[valueLen] = txtRecordsRawBytes[i];
+ valueLen++;
+ }
+ }
+
+ // If '=' was not found we have a boolean record
+ if (key == null) {
+ key = new String(txtRecordsRawBytes, pos, recordLen, StandardCharsets.US_ASCII);
+ }
+
+ if (TextUtils.isEmpty(key)) {
+ // Empty keys are not allowed (RFC6763 6.4)
+ throw new IllegalArgumentException("Invalid txt record (key is empty)");
+ }
+
+ if (getAttributes().containsKey(key)) {
+ // When we have a duplicate record, the later ones are ignored (RFC6763 6.4)
+ throw new IllegalArgumentException("Invalid txt record (duplicate key \"" + key + "\")");
+ }
+
+ setAttribute(key, value);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "While parsing txt records (pos = " + pos + "): " + e.getMessage());
+ }
+
+ pos += recordLen;
+ }
+ }
+
/** @hide */
public void setAttribute(String key, byte[] value) {
+ if (TextUtils.isEmpty(key)) {
+ throw new IllegalArgumentException("Key cannot be empty");
+ }
+
// Key must be printable US-ASCII, excluding =.
for (int i = 0; i < key.length(); ++i) {
char character = key.charAt(i);
@@ -177,10 +271,10 @@
}
/** @hide */
- public byte[] getTxtRecord() {
+ public @NonNull byte[] getTxtRecord() {
int txtRecordSize = getTxtRecordSize();
if (txtRecordSize == 0) {
- return null;
+ return new byte[]{};
}
byte[] txtRecord = new byte[txtRecordSize];
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index 6e50155..b6c919e 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -20,6 +20,7 @@
import android.util.ArrayMap;
import android.util.Log;
import android.util.MathUtils;
+import android.util.Slog;
import java.io.Serializable;
import java.util.ArrayList;
@@ -229,7 +230,7 @@
}
if (sShouldDefuse && (mFlags & FLAG_DEFUSABLE) == 0) {
- Log.wtf(TAG, "Attempting to unparcel a Bundle while in transit; this may "
+ Slog.wtf(TAG, "Attempting to unparcel a Bundle while in transit; this may "
+ "clobber all data inside!", new Throwable());
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index e730ad8..c452837 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -30,6 +30,8 @@
import android.telephony.SignalStrength;
import android.text.format.DateFormat;
import android.util.ArrayMap;
+import android.util.MutableBoolean;
+import android.util.Pair;
import android.util.Printer;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -210,6 +212,7 @@
private static final String WIFI_CONTROLLER_DATA = "wfcd";
private static final String GLOBAL_BLUETOOTH_CONTROLLER_DATA = "gble";
private static final String BLUETOOTH_CONTROLLER_DATA = "ble";
+ private static final String BLUETOOTH_MISC_DATA = "blem";
private static final String MISC_DATA = "m";
private static final String GLOBAL_NETWORK_DATA = "gn";
private static final String GLOBAL_MODEM_CONTROLLER_DATA = "gmcd";
@@ -445,19 +448,41 @@
public abstract Timer getForegroundActivityTimer();
public abstract Timer getBluetoothScanTimer();
- // Time this uid has any processes in the top state.
+ // Note: the following times are disjoint. They can be added together to find the
+ // total time a uid has had any processes running at all.
+
+ /**
+ * Time this uid has any processes in the top state (or above such as persistent).
+ */
public static final int PROCESS_STATE_TOP = 0;
- // Time this uid has any process with a started out bound foreground service.
+ /**
+ * Time this uid has any process with a started out bound foreground service, but
+ * none in the "top" state.
+ */
public static final int PROCESS_STATE_FOREGROUND_SERVICE = 1;
- // Time this uid has any process that is top while the device is sleeping.
+ /**
+ * Time this uid has any process that is top while the device is sleeping, but none
+ * in the "foreground service" or better state.
+ */
public static final int PROCESS_STATE_TOP_SLEEPING = 2;
- // Time this uid has any process in an active foreground state.
+ /**
+ * Time this uid has any process in an active foreground state, but none in the
+ * "top sleeping" or better state.
+ */
public static final int PROCESS_STATE_FOREGROUND = 3;
- // Time this uid has any process in an active background state.
+ /**
+ * Time this uid has any process in an active background state, but none in the
+ * "foreground" or better state.
+ */
public static final int PROCESS_STATE_BACKGROUND = 4;
- // Time this uid has any processes running at all.
+ /**
+ * Time this uid has any processes that are sitting around cached, not in one of the
+ * other active states.
+ */
public static final int PROCESS_STATE_CACHED = 5;
- // Total number of process states we track.
+ /**
+ * Total number of process states we track.
+ */
public static final int NUM_PROCESS_STATE = 6;
static final String[] PROCESS_STATE_NAMES = {
@@ -3069,6 +3094,15 @@
dumpControllerActivityLine(pw, uid, category, WIFI_CONTROLLER_DATA,
u.getWifiControllerActivity(), which);
+ // Dump Bluetooth scan data, per UID.
+ final long bleScanTimeUs = u.getBluetoothScanTimer().getTotalTimeLocked(
+ rawRealtime, which);
+ final int bleScanCount = u.getBluetoothScanTimer().getCountLocked(which);
+ if (bleScanTimeUs != 0 || bleScanCount != 0) {
+ dumpLine(pw, uid, category, BLUETOOTH_MISC_DATA,
+ bleScanTimeUs / 1000, bleScanCount);
+ }
+
dumpControllerActivityLine(pw, uid, category, BLUETOOTH_CONTROLLER_DATA,
u.getBluetoothControllerActivity(), which);
@@ -5317,26 +5351,28 @@
}
if (apps != null) {
- SparseArray<ArrayList<String>> uids = new SparseArray<ArrayList<String>>();
+ SparseArray<Pair<ArrayList<String>, MutableBoolean>> uids = new SparseArray<>();
for (int i=0; i<apps.size(); i++) {
ApplicationInfo ai = apps.get(i);
- ArrayList<String> pkgs = uids.get(ai.uid);
+ Pair<ArrayList<String>, MutableBoolean> pkgs = uids.get(
+ UserHandle.getAppId(ai.uid));
if (pkgs == null) {
- pkgs = new ArrayList<String>();
- uids.put(ai.uid, pkgs);
+ pkgs = new Pair<>(new ArrayList<String>(), new MutableBoolean(false));
+ uids.put(UserHandle.getAppId(ai.uid), pkgs);
}
- pkgs.add(ai.packageName);
+ pkgs.first.add(ai.packageName);
}
SparseArray<? extends Uid> uidStats = getUidStats();
final int NU = uidStats.size();
String[] lineArgs = new String[2];
for (int i=0; i<NU; i++) {
- int uid = uidStats.keyAt(i);
- ArrayList<String> pkgs = uids.get(uid);
- if (pkgs != null) {
- for (int j=0; j<pkgs.size(); j++) {
+ int uid = UserHandle.getAppId(uidStats.keyAt(i));
+ Pair<ArrayList<String>, MutableBoolean> pkgs = uids.get(uid);
+ if (pkgs != null && !pkgs.second.value) {
+ pkgs.second.value = true;
+ for (int j=0; j<pkgs.first.size(); j++) {
lineArgs[0] = Integer.toString(uid);
- lineArgs[1] = pkgs.get(j);
+ lineArgs[1] = pkgs.first.get(j);
dumpLine(pw, 0 /* uid */, "i" /* category */, UID_DATA,
(Object[])lineArgs);
}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index de8b690..d0029e1 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -91,6 +91,12 @@
/** The name of the hardware (from the kernel command line or /proc). */
public static final String HARDWARE = getString("ro.hardware");
+ /**
+ * Whether this build was for an emulator device.
+ * @hide
+ */
+ public static final boolean IS_EMULATOR = getString("ro.kernel.qemu").equals("1");
+
/** A hardware serial number, if available. Alphanumeric only, case-insensitive. */
public static final String SERIAL = getString("ro.serialno");
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index 05dd48b..1128074 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -208,6 +208,18 @@
}
/**
+ * Removes any entry with the given key from the mapping of this Bundle.
+ *
+ * @param key a String key
+ */
+ public void remove(String key) {
+ super.remove(key);
+ if ((mFlags & FLAG_HAS_FDS) != 0) {
+ mFlags &= ~FLAG_HAS_FDS_KNOWN;
+ }
+ }
+
+ /**
* Inserts all mappings from the given Bundle into this Bundle.
*
* @param bundle a Bundle
@@ -288,6 +300,8 @@
if (fdFound) {
mFlags |= FLAG_HAS_FDS;
+ } else {
+ mFlags &= ~FLAG_HAS_FDS;
}
mFlags |= FLAG_HAS_FDS_KNOWN;
}
@@ -315,6 +329,8 @@
mMap.removeAt(i);
}
}
+ mFlags |= FLAG_HAS_FDS_KNOWN;
+ mFlags &= ~FLAG_HAS_FDS;
}
/**
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index 878b7a0..3c7c962 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -231,6 +231,18 @@
mAsynchronous = async;
}
+ /** {@hide} */
+ public String getTraceName(Message message) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append(getClass().getName()).append(": ");
+ if (message.callback != null) {
+ sb.append(message.callback.getClass().getName());
+ } else {
+ sb.append("#").append(message.what);
+ }
+ return sb.toString();
+ }
+
/**
* Returns a string representing the name of the specified message.
* The default implementation will either return the class name of the
@@ -739,8 +751,8 @@
message.callback.run();
}
- final MessageQueue mQueue;
final Looper mLooper;
+ final MessageQueue mQueue;
final Callback mCallback;
final boolean mAsynchronous;
IMessenger mMessenger;
diff --git a/core/java/android/os/HardwarePropertiesManager.java b/core/java/android/os/HardwarePropertiesManager.java
index 0f2e33d..9d362d6 100644
--- a/core/java/android/os/HardwarePropertiesManager.java
+++ b/core/java/android/os/HardwarePropertiesManager.java
@@ -48,7 +48,8 @@
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({
- TEMPERATURE_CURRENT, TEMPERATURE_THROTTLING, TEMPERATURE_SHUTDOWN
+ TEMPERATURE_CURRENT, TEMPERATURE_THROTTLING, TEMPERATURE_SHUTDOWN,
+ TEMPERATURE_THROTTLING_BELOW_VR_MIN
})
public @interface TemperatureSource {}
@@ -77,6 +78,12 @@
/** Get shutdown temperature threshold. */
public static final int TEMPERATURE_SHUTDOWN = 2;
+ /**
+ * Get throttling temperature threshold above which minimum clockrates for VR mode will not be
+ * met.
+ */
+ public static final int TEMPERATURE_THROTTLING_BELOW_VR_MIN = 3;
+
/** Undefined temperature constant. */
public static final float UNDEFINED_TEMPERATURE = -Float.MAX_VALUE;
@@ -96,12 +103,14 @@
* {@link #DEVICE_TEMPERATURE_GPU}, {@link #DEVICE_TEMPERATURE_BATTERY} or {@link
* #DEVICE_TEMPERATURE_SKIN}.
* @param source source of requested device temperature, one of {@link #TEMPERATURE_CURRENT},
- * {@link #TEMPERATURE_THROTTLING} or {@link #TEMPERATURE_SHUTDOWN}.
+ * {@link #TEMPERATURE_THROTTLING}, {@link #TEMPERATURE_THROTTLING_BELOW_VR_MIN} or
+ * {@link #TEMPERATURE_SHUTDOWN}.
* @return an array of requested float device temperatures. Temperature equals to
* {@link #UNDEFINED_TEMPERATURE} if undefined.
* Empty if platform doesn't provide the queried temperature.
*
- * @throws SecurityException if a non profile or device owner tries to call this method.
+ * @throws SecurityException if something other than the profile or device owner, or the
+ * current VR service tries to retrieve information provided by this service.
*/
public @NonNull float[] getDeviceTemperatures(@DeviceTemperatureType int type,
@TemperatureSource int source) {
@@ -114,6 +123,7 @@
case TEMPERATURE_CURRENT:
case TEMPERATURE_THROTTLING:
case TEMPERATURE_SHUTDOWN:
+ case TEMPERATURE_THROTTLING_BELOW_VR_MIN:
try {
return mService.getDeviceTemperatures(mContext.getOpPackageName(), type,
source);
@@ -137,7 +147,8 @@
* each unplugged core.
* Empty if CPU usage is not supported on this system.
*
- * @throws SecurityException if a non profile or device owner tries to call this method.
+ * @throws SecurityException if something other than the profile or device owner, or the
+ * current VR service tries to retrieve information provided by this service.
*/
public @NonNull CpuUsageInfo[] getCpuUsages() {
try {
@@ -153,7 +164,8 @@
* @return an array of float fan speeds in RPM. Empty if there are no fans or fan speed is not
* supported on this system.
*
- * @throws SecurityException if a non profile or device owner tries to call this method.
+ * @throws SecurityException if something other than the profile or device owner, or the
+ * current VR service tries to retrieve information provided by this service.
*/
public @NonNull float[] getFanSpeeds() {
try {
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 100f6a1..68b0a9f 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -293,7 +293,9 @@
/**
* Control network activity of a UID over interfaces with a quota limit.
*/
- void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces);
+ void setUidMeteredNetworkBlacklist(int uid, boolean enable);
+ void setUidMeteredNetworkWhitelist(int uid, boolean enable);
+ boolean setDataSaverModeEnabled(boolean enable);
void setUidCleartextNetworkPolicy(int uid, int policy);
@@ -326,11 +328,6 @@
*/
void setDnsServersForNetwork(int netId, in String[] servers, String domains);
- /**
- * Flush the DNS cache associated with the specified network.
- */
- void flushNetworkDnsCache(int netId);
-
void setFirewallEnabled(boolean enabled);
boolean isFirewallEnabled();
void setFirewallInterfaceRule(String iface, boolean allow);
diff --git a/core/java/android/net/IConnectivityMetricsLoggerSubscriber.aidl b/core/java/android/os/IProgressListener.aidl
similarity index 67%
copy from core/java/android/net/IConnectivityMetricsLoggerSubscriber.aidl
copy to core/java/android/os/IProgressListener.aidl
index a2c62cd..ad58a7c 100644
--- a/core/java/android/net/IConnectivityMetricsLoggerSubscriber.aidl
+++ b/core/java/android/os/IProgressListener.aidl
@@ -11,15 +11,16 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
*/
-package android.net;
+package android.os;
-import android.net.ConnectivityMetricsEvent;
+import android.os.Bundle;
-/** {@hide} */
-oneway interface IConnectivityMetricsLoggerSubscriber {
-
- void onEvents(in ConnectivityMetricsEvent[] events);
+/** @hide */
+oneway interface IProgressListener {
+ void onStarted(int id, in Bundle extras);
+ void onProgress(int id, int progress, in Bundle extras);
+ void onFinished(int id, in Bundle extras);
}
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index 34c880f..b58ff1f 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -72,6 +72,7 @@
final Thread mThread;
private Printer mLogging;
+ private long mTraceTag;
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
@@ -139,13 +140,23 @@
}
// This must be in a local variable, in case a UI event sets the logger
- Printer logging = me.mLogging;
+ final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
- msg.target.dispatchMessage(msg);
+ final long traceTag = me.mTraceTag;
+ if (traceTag != 0) {
+ Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
+ }
+ try {
+ msg.target.dispatchMessage(msg);
+ } finally {
+ if (traceTag != 0) {
+ Trace.traceEnd(traceTag);
+ }
+ }
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
@@ -208,6 +219,11 @@
mLogging = printer;
}
+ /** {@hide} */
+ public void setTraceTag(long traceTag) {
+ mTraceTag = traceTag;
+ }
+
/**
* Quits the looper.
* <p>
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 8bc903b..92edc62 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -221,11 +221,9 @@
/**
* Wake lock level: Enables Sustained Performance Mode.
* <p>
- * This is used by Gaming and VR applications to ensure the device provides
+ * This is used by Gaming and VR applications to ensure the device
* will provide consistent performance over a large amount of time.
* </p>
- *
- * {@hide}
*/
public static final int SUSTAINED_PERFORMANCE_WAKE_LOCK = 0x00000100;
@@ -1033,6 +1031,16 @@
}
/**
+ * Returns True if the device supports Sustained Performance Mode.
+ * Applications Should check if the device supports this mode, before
+ * using {@link #SUSTAINED_PERFORMANCE_WAKE_LOCK}.
+ */
+ public boolean isSustainedPerformanceModeSupported() {
+ return mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_sustainedPerformanceModeSupported);
+ }
+
+ /**
* Intent that is broadcast when the state of {@link #isPowerSaveMode()} changes.
* This broadcast is only sent to registered receivers.
*/
diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java
index 80e6146..bf03cce 100644
--- a/core/java/android/os/UpdateEngine.java
+++ b/core/java/android/os/UpdateEngine.java
@@ -87,7 +87,7 @@
}
@SystemApi
- public boolean bind(final UpdateEngineCallback callback, final Handler handler) throws RemoteException {
+ public boolean bind(final UpdateEngineCallback callback, final Handler handler) {
IUpdateEngineCallback updateEngineCallback = new IUpdateEngineCallback.Stub() {
@Override
public void onStatusUpdate(final int status, final float percent) {
@@ -118,31 +118,60 @@
}
};
- return mUpdateEngine.bind(updateEngineCallback);
+ try {
+ return mUpdateEngine.bind(updateEngineCallback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
@SystemApi
- public boolean bind(final UpdateEngineCallback callback) throws RemoteException {
+ public boolean bind(final UpdateEngineCallback callback) {
return bind(callback, null);
}
@SystemApi
- public void applyPayload(String url, long offset, long size, String[] headerKeyValuePairs) throws RemoteException {
- mUpdateEngine.applyPayload(url, offset, size, headerKeyValuePairs);
+ public void applyPayload(String url, long offset, long size, String[] headerKeyValuePairs) {
+ try {
+ mUpdateEngine.applyPayload(url, offset, size, headerKeyValuePairs);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
@SystemApi
- public void cancel() throws RemoteException {
- mUpdateEngine.cancel();
+ public void cancel() {
+ try {
+ mUpdateEngine.cancel();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
@SystemApi
- public void suspend() throws RemoteException {
- mUpdateEngine.suspend();
+ public void suspend() {
+ try {
+ mUpdateEngine.suspend();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
@SystemApi
- public void resume() throws RemoteException {
- mUpdateEngine.resume();
+ public void resume() {
+ try {
+ mUpdateEngine.resume();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @SystemApi
+ public void resetStatus() {
+ try {
+ mUpdateEngine.resetStatus();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
}
diff --git a/core/java/android/os/health/TimerStat.java b/core/java/android/os/health/TimerStat.java
index fc51b60..b9d8874 100644
--- a/core/java/android/os/health/TimerStat.java
+++ b/core/java/android/os/health/TimerStat.java
@@ -27,7 +27,7 @@
* object to be constructed, even internally, but the getTimers method on
* {@link android.os.health.HealthStats} does require TimerStat objects.
*/
-public class TimerStat implements Parcelable {
+public final class TimerStat implements Parcelable {
private int mCount;
private long mTime;
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 22aec63..f2e316c 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -34,8 +34,8 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.provider.Settings;
-import android.security.KeyStore;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
@@ -49,6 +49,7 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
@@ -101,6 +102,10 @@
/** {@hide} */
public static final int FLAG_FOR_WRITE = 1 << 8;
+ /** {@hide} */
+ public static final int FLAG_REAL_STATE = 1 << 9;
+ /** {@hide} */
+ public static final int FLAG_INCLUDE_INVISIBLE = 1 << 10;
private final Context mContext;
private final ContentResolver mResolver;
@@ -859,11 +864,31 @@
}
/**
- * Gets the list of shared/external storage volumes available to the current user.
+ * Return the list of shared/external storage volumes available to the
+ * current user. This includes both the primary shared storage device and
+ * any attached external volumes including SD cards and USB drives.
*
- * <p>It always contains the primary storage volume, plus any additional external volume(s)
- * available in the device, such as SD cards or attached USB drives.
+ * @see Environment#getExternalStorageDirectory()
+ * @see StorageVolume#createAccessIntent(String)
*/
+ public @NonNull List<StorageVolume> getStorageVolumes() {
+ final ArrayList<StorageVolume> res = new ArrayList<>();
+ Collections.addAll(res,
+ getVolumeList(UserHandle.myUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE));
+ return res;
+ }
+
+ /**
+ * Return the primary shared/external storage volume available to the
+ * current user. This volume is the same storage device returned by
+ * {@link Environment#getExternalStorageDirectory()} and
+ * {@link Context#getExternalFilesDir(String)}.
+ */
+ public @NonNull StorageVolume getPrimaryStorageVolume() {
+ return getVolumeList(UserHandle.myUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE)[0];
+ }
+
+ /** @removed */
public @NonNull StorageVolume[] getVolumeList() {
return getVolumeList(mContext.getUserId(), 0);
}
@@ -912,9 +937,7 @@
return paths;
}
- /**
- * Gets the primary shared/external storage volume available to the current user.
- */
+ /** @removed */
public @NonNull StorageVolume getPrimaryVolume() {
return getPrimaryVolume(getVolumeList());
}
@@ -1155,23 +1178,7 @@
/** {@hide} */
public static File maybeTranslateEmulatedPathToInternal(File path) {
- final IMountService mountService = IMountService.Stub.asInterface(
- ServiceManager.getService("mount"));
- try {
- final VolumeInfo[] vols = mountService.getVolumes(0);
- for (VolumeInfo vol : vols) {
- if ((vol.getType() == VolumeInfo.TYPE_EMULATED
- || vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.isMountedReadable()) {
- final File internalPath = FileUtils.rewriteAfterRename(vol.getPath(),
- vol.getInternalPath(), path);
- if (internalPath != null && internalPath.exists()) {
- return internalPath;
- }
- }
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ // Disabled now that FUSE has been replaced by sdcardfs
return path;
}
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index 17176ec..7b0d2a4 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -16,7 +16,6 @@
package android.os.storage;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.Intent;
@@ -66,8 +65,8 @@
* broad access to all files contained on a storage device.
* </ul>
*
- * <p>It can be obtained through {@link StorageManager#getVolumeList()} and
- * {@link StorageManager#getPrimaryVolume()} and also as an extra in some broadcasts
+ * <p>It can be obtained through {@link StorageManager#getStorageVolumes()} and
+ * {@link StorageManager#getPrimaryStorageVolume()} and also as an extra in some broadcasts
* (see {@link #EXTRA_STORAGE_VOLUME}).
*
* <p>
@@ -316,27 +315,34 @@
* To gain access to descendants (child, grandchild, etc) documents, use
* {@link DocumentsContract#buildDocumentUriUsingTree(Uri, String)}, or
* {@link DocumentsContract#buildChildDocumentsUriUsingTree(Uri, String)} with the returned URI.
- *
- * <b>If your application only needs to store internal data, consider using
+ * <p>
+ * If your application only needs to store internal data, consider using
* {@link Context#getExternalFilesDirs(String) Context.getExternalFilesDirs},
- * {@link Context#getExternalCacheDirs()}, or
- * {@link Context#getExternalMediaDirs()}, which require no permissions to read or write.
+ * {@link Context#getExternalCacheDirs()}, or {@link Context#getExternalMediaDirs()}, which
+ * require no permissions to read or write.
+ * <p>
+ * Access to the entire volume is only available for non-primary volumes (for the primary
+ * volume, apps can use the {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} and
+ * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} permissions) and should be used
+ * with caution, since users are more likely to deny access when asked for entire volume access
+ * rather than specific directories.
*
- * <strong>NOTE: </strong>requesting access to the entire volume is not recommended and it will
- * result in a stronger message displayed to the user, which may cause the user to reject
- * the request.
- *
- * @param directoryName must be one of
- * {@link Environment#DIRECTORY_MUSIC}, {@link Environment#DIRECTORY_PODCASTS},
- * {@link Environment#DIRECTORY_RINGTONES}, {@link Environment#DIRECTORY_ALARMS},
- * {@link Environment#DIRECTORY_NOTIFICATIONS}, {@link Environment#DIRECTORY_PICTURES},
- * {@link Environment#DIRECTORY_MOVIES}, {@link Environment#DIRECTORY_DOWNLOADS},
- * {@link Environment#DIRECTORY_DCIM}, or {@link Environment#DIRECTORY_DOCUMENTS}, or
- * {code null} to request access to the entire volume.
- *
+ * @param directoryName must be one of {@link Environment#DIRECTORY_MUSIC},
+ * {@link Environment#DIRECTORY_PODCASTS}, {@link Environment#DIRECTORY_RINGTONES},
+ * {@link Environment#DIRECTORY_ALARMS}, {@link Environment#DIRECTORY_NOTIFICATIONS},
+ * {@link Environment#DIRECTORY_PICTURES}, {@link Environment#DIRECTORY_MOVIES},
+ * {@link Environment#DIRECTORY_DOWNLOADS}, {@link Environment#DIRECTORY_DCIM}, or
+ * {@link Environment#DIRECTORY_DOCUMENTS}, or {code null} to request access to the
+ * entire volume.
+ * @return intent to request access, or {@code null} if the requested directory is invalid for
+ * that volume.
* @see DocumentsContract
*/
- public Intent createAccessIntent(@NonNull String directoryName) {
+ public @Nullable Intent createAccessIntent(String directoryName) {
+ if ((isPrimary() && directoryName == null) ||
+ (directoryName != null && !Environment.isStandardDirectory(directoryName))) {
+ return null;
+ }
final Intent intent = new Intent(ACTION_OPEN_EXTERNAL_DIRECTORY);
intent.putExtra(EXTRA_STORAGE_VOLUME, this);
intent.putExtra(EXTRA_DIRECTORY_NAME, directoryName);
diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java
index d41bc07..b1cad05 100644
--- a/core/java/android/preference/Preference.java
+++ b/core/java/android/preference/Preference.java
@@ -1184,10 +1184,9 @@
/**
* Called when the Preference hierarchy has been attached to the
- * {@link PreferenceActivity} or {@link PreferenceFragment}. This can
- * also be called when this Preference has been attached to a group
- * that was already attached to the {@link PreferenceActivity} or
- * {@link PreferenceFragment}.
+ * {@link PreferenceActivity}. This can also be called when this
+ * Preference has been attached to a group that was already attached
+ * to the {@link PreferenceActivity}.
*/
protected void onAttachedToActivity() {
// At this point, the hierarchy that this preference is in is connected
@@ -1195,16 +1194,6 @@
registerDependency();
}
- /**
- * Called when the Preference hierarchy has been detached from the
- * {@link PreferenceActivity} or {@link PreferenceFragment}. This can
- * also be called when this Preference has been removed from a group
- * that was already attached to the {@link PreferenceActivity} or
- * {@link PreferenceFragment}.
- */
- protected void onDetachedFromActivity() {
- }
-
private void registerDependency() {
if (TextUtils.isEmpty(mDependencyKey)) return;
diff --git a/core/java/android/preference/PreferenceGroup.java b/core/java/android/preference/PreferenceGroup.java
index 13c3661..f17506b 100644
--- a/core/java/android/preference/PreferenceGroup.java
+++ b/core/java/android/preference/PreferenceGroup.java
@@ -186,11 +186,7 @@
private boolean removePreferenceInt(Preference preference) {
synchronized(this) {
preference.onPrepareForRemoval();
- boolean success = mPreferenceList.remove(preference);
- if (mAttachedToActivity) {
- preference.onDetachedFromActivity();
- }
- return success;
+ return mPreferenceList.remove(preference);
}
}
@@ -266,7 +262,7 @@
protected boolean isOnSameScreenAsChildren() {
return true;
}
-
+
@Override
protected void onAttachedToActivity() {
super.onAttachedToActivity();
@@ -283,17 +279,11 @@
}
@Override
- protected void onDetachedFromActivity() {
- super.onDetachedFromActivity();
-
+ protected void onPrepareForRemoval() {
+ super.onPrepareForRemoval();
+
// We won't be attached to the activity anymore
mAttachedToActivity = false;
-
- // Dispatch to all contained preferences
- final int preferenceCount = getPreferenceCount();
- for (int i = 0; i < preferenceCount; i++) {
- getPreference(i).onDetachedFromActivity();
- }
}
@Override
diff --git a/core/java/android/preference/PreferenceManager.java b/core/java/android/preference/PreferenceManager.java
index 47dc6c3..1a6b06f 100644
--- a/core/java/android/preference/PreferenceManager.java
+++ b/core/java/android/preference/PreferenceManager.java
@@ -412,6 +412,41 @@
}
/**
+ * Indicates if the storage location used internally by this class is the
+ * default provided by the hosting {@link Context}.
+ *
+ * @see #setStorageDefault()
+ * @see #setStorageDeviceProtected()
+ */
+ public boolean isStorageDefault() {
+ return mStorage == STORAGE_DEFAULT;
+ }
+
+ /**
+ * Indicates if the storage location used internally by this class is backed
+ * by device-protected storage.
+ *
+ * @see #setStorageDefault()
+ * @see #setStorageDeviceProtected()
+ */
+ public boolean isStorageDeviceProtected() {
+ return mStorage == STORAGE_DEVICE_PROTECTED;
+ }
+
+ /**
+ * Indicates if the storage location used internally by this class is backed
+ * by credential-protected storage.
+ *
+ * @see #setStorageDefault()
+ * @see #setStorageDeviceProtected()
+ * @hide
+ */
+ @SystemApi
+ public boolean isStorageCredentialProtected() {
+ return mStorage == STORAGE_CREDENTIAL_PROTECTED;
+ }
+
+ /**
* Gets a SharedPreferences instance that preferences managed by this will
* use.
*
@@ -484,9 +519,6 @@
*/
boolean setPreferences(PreferenceScreen preferenceScreen) {
if (preferenceScreen != mPreferenceScreen) {
- if (mPreferenceScreen != null) {
- mPreferenceScreen.onDetachedFromActivity();
- }
mPreferenceScreen = preferenceScreen;
return true;
}
@@ -795,11 +827,7 @@
*/
void dispatchActivityDestroy() {
List<OnActivityDestroyListener> list = null;
-
- if (mPreferenceScreen != null) {
- mPreferenceScreen.onDetachedFromActivity();
- mPreferenceScreen = null;
- }
+
synchronized (this) {
if (mActivityDestroyListeners != null) {
list = new ArrayList<OnActivityDestroyListener>(mActivityDestroyListeners);
diff --git a/core/java/android/print/IPrintManager.aidl b/core/java/android/print/IPrintManager.aidl
index 5eb8cc2..d7c267b 100644
--- a/core/java/android/print/IPrintManager.aidl
+++ b/core/java/android/print/IPrintManager.aidl
@@ -24,9 +24,11 @@
import android.print.PrintJobId;
import android.print.IPrintJobStateChangeListener;
import android.print.IPrintServicesChangeListener;
+import android.printservice.recommendation.IRecommendationsChangeListener;
import android.print.PrinterId;
import android.print.PrintJobInfo;
import android.print.PrintAttributes;
+import android.printservice.recommendation.RecommendationInfo;
import android.printservice.PrintServiceInfo;
/**
@@ -73,7 +75,6 @@
* Get the print services.
*
* @param selectionFlags flags selecting which services to get
- * @param selectedService if not null, the id of the print service to get
* @param userId the id of the user requesting the services
*
* @return the list of selected print services.
@@ -89,6 +90,37 @@
*/
void setPrintServiceEnabled(in ComponentName service, boolean isEnabled, int userId);
+ /**
+ * Listen for changes to the print service recommendations.
+ *
+ * @param listener the listener to add
+ * @param userId the id of the user listening
+ *
+ * @see android.print.PrintManager#getPrintServiceRecommendations
+ */
+ void addPrintServiceRecommendationsChangeListener(in IRecommendationsChangeListener listener,
+ int userId);
+
+ /**
+ * Stop listening for changes to the print service recommendations.
+ *
+ * @param listener the listener to remove
+ * @param userId the id of the user requesting the removal
+ *
+ * @see android.print.PrintManager#getPrintServiceRecommendations
+ */
+ void removePrintServiceRecommendationsChangeListener(in IRecommendationsChangeListener listener,
+ int userId);
+
+ /**
+ * Get the print service recommendations.
+ *
+ * @param userId the id of the user requesting the recommendations
+ *
+ * @return the list of selected print services.
+ */
+ List<RecommendationInfo> getPrintServiceRecommendations(int userId);
+
void createPrinterDiscoverySession(in IPrinterDiscoveryObserver observer, int userId);
void startPrinterDiscovery(in IPrinterDiscoveryObserver observer,
in List<PrinterId> priorityList, int userId);
diff --git a/core/java/android/print/IPrintSpooler.aidl b/core/java/android/print/IPrintSpooler.aidl
index 469a4ea..63bf885 100644
--- a/core/java/android/print/IPrintSpooler.aidl
+++ b/core/java/android/print/IPrintSpooler.aidl
@@ -61,6 +61,15 @@
void setStatus(in PrintJobId printJobId, in CharSequence status);
/**
+ * Set the status of this print job
+ *
+ * @param printJobId The print job to update
+ * @param status The new status as a string resource
+ * @param appPackageName App package name the resource belongs to
+ */
+ void setStatusRes(in PrintJobId printJobId, int status, in CharSequence appPackageName);
+
+ /**
* Handle that a custom icon for a printer was loaded.
*
* @param printerId the id of the printer the icon belongs to
diff --git a/core/java/android/print/PageRange.java b/core/java/android/print/PageRange.java
index 57c7718..2941283 100644
--- a/core/java/android/print/PageRange.java
+++ b/core/java/android/print/PageRange.java
@@ -32,6 +32,9 @@
*/
public static final PageRange ALL_PAGES = new PageRange(0, Integer.MAX_VALUE);
+ /** @hide */
+ public static final PageRange[] ALL_PAGES_ARRAY = new PageRange[]{PageRange.ALL_PAGES};
+
private final int mStart;
private final int mEnd;
diff --git a/core/java/android/print/PrintAttributes.java b/core/java/android/print/PrintAttributes.java
index e9c196d..721c94e 100644
--- a/core/java/android/print/PrintAttributes.java
+++ b/core/java/android/print/PrintAttributes.java
@@ -20,6 +20,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.StringRes;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources.NotFoundException;
@@ -31,6 +32,7 @@
import android.util.Log;
import com.android.internal.R;
+import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -49,7 +51,7 @@
@IntDef(flag = true, value = {
COLOR_MODE_MONOCHROME, COLOR_MODE_COLOR
})
- public @interface ColorMode {
+ @interface ColorMode {
}
/** Color mode: Monochrome color scheme, for example one color is used. */
public static final int COLOR_MODE_MONOCHROME = 1 << 0;
@@ -64,7 +66,7 @@
@IntDef(flag = true, value = {
DUPLEX_MODE_NONE, DUPLEX_MODE_LONG_EDGE, DUPLEX_MODE_SHORT_EDGE
})
- public @interface DuplexMode {
+ @interface DuplexMode {
}
/** Duplex mode: No duplexing. */
public static final int DUPLEX_MODE_NONE = 1 << 0;
@@ -76,23 +78,29 @@
private static final int VALID_DUPLEX_MODES =
DUPLEX_MODE_NONE | DUPLEX_MODE_LONG_EDGE | DUPLEX_MODE_SHORT_EDGE;
- private MediaSize mMediaSize;
- private Resolution mResolution;
- private Margins mMinMargins;
+ private @Nullable MediaSize mMediaSize;
+ private @Nullable Resolution mResolution;
+ private @Nullable Margins mMinMargins;
- private int mColorMode;
- private int mDuplexMode;
+ private @IntRange(from = 0) int mColorMode;
+ private @IntRange(from = 0) int mDuplexMode;
PrintAttributes() {
/* hide constructor */
}
private PrintAttributes(@NonNull Parcel parcel) {
- mMediaSize = (parcel.readInt() == 1) ? MediaSize.createFromParcel(parcel) : null;
- mResolution = (parcel.readInt() == 1) ? Resolution.createFromParcel(parcel) : null;
- mMinMargins = (parcel.readInt() == 1) ? Margins.createFromParcel(parcel) : null;
+ mMediaSize = (parcel.readInt() == 1) ? MediaSize.createFromParcel(parcel) : null;
+ mResolution = (parcel.readInt() == 1) ? Resolution.createFromParcel(parcel) : null;
+ mMinMargins = (parcel.readInt() == 1) ? Margins.createFromParcel(parcel) : null;
mColorMode = parcel.readInt();
+ if (mColorMode != 0) {
+ enforceValidColorMode(mColorMode);
+ }
mDuplexMode = parcel.readInt();
+ if (mDuplexMode != 0) {
+ enforceValidDuplexMode(mDuplexMode);
+ }
}
/**
@@ -179,7 +187,7 @@
* @see #COLOR_MODE_COLOR
* @see #COLOR_MODE_MONOCHROME
*/
- public @ColorMode int getColorMode() {
+ public @IntRange(from = 0) int getColorMode() {
return mColorMode;
}
@@ -214,13 +222,13 @@
/**
* Gets the duplex mode.
*
- * @return The duplex mode.
+ * @return The duplex mode or zero if not set.
*
* @see #DUPLEX_MODE_NONE
* @see #DUPLEX_MODE_LONG_EDGE
* @see #DUPLEX_MODE_SHORT_EDGE
*/
- public @DuplexMode int getDuplexMode() {
+ public @IntRange(from = 0) int getDuplexMode() {
return mDuplexMode;
}
@@ -448,7 +456,7 @@
private static final String LOG_TAG = "MediaSize";
private static final Map<String, MediaSize> sIdToMediaSizeMap =
- new ArrayMap<String, MediaSize>();
+ new ArrayMap<>();
/**
* Unknown media size in portrait mode.
@@ -781,15 +789,15 @@
new MediaSize("JPN_YOU4", "android",
R.string.mediasize_japanese_you4, 4134, 9252);
- private final String mId;
+ private final @NonNull String mId;
/**@hide */
- public final String mLabel;
+ public final @NonNull String mLabel;
/**@hide */
- public final String mPackageName;
+ public final @Nullable String mPackageName;
/**@hide */
- public final int mLabelResId;
- private final int mWidthMils;
- private final int mHeightMils;
+ public final @StringRes int mLabelResId;
+ private final @IntRange(from = 1) int mWidthMils;
+ private final @IntRange(from = 1) int mHeightMils;
/**
* Creates a new instance.
@@ -808,29 +816,7 @@
*/
public MediaSize(String id, String packageName, int labelResId,
int widthMils, int heightMils) {
- if (TextUtils.isEmpty(id)) {
- throw new IllegalArgumentException("id cannot be empty.");
- }
- if (TextUtils.isEmpty(packageName)) {
- throw new IllegalArgumentException("packageName cannot be empty.");
- }
- if (labelResId <= 0) {
- throw new IllegalArgumentException("labelResId must be greater than zero.");
- }
- if (widthMils <= 0) {
- throw new IllegalArgumentException("widthMils "
- + "cannot be less than or equal to zero.");
- }
- if (heightMils <= 0) {
- throw new IllegalArgumentException("heightMils "
- + "cannot be less than or euqual to zero.");
- }
- mPackageName = packageName;
- mId = id;
- mLabelResId = labelResId;
- mWidthMils = widthMils;
- mHeightMils = heightMils;
- mLabel = null;
+ this(id, null, packageName, widthMils, heightMils, labelResId);
// Build this mapping only for predefined media sizes.
sIdToMediaSizeMap.put(mId, this);
@@ -851,26 +837,7 @@
*/
public MediaSize(@NonNull String id, @NonNull String label,
@IntRange(from = 1) int widthMils, @IntRange(from = 1) int heightMils) {
- if (TextUtils.isEmpty(id)) {
- throw new IllegalArgumentException("id cannot be empty.");
- }
- if (TextUtils.isEmpty(label)) {
- throw new IllegalArgumentException("label cannot be empty.");
- }
- if (widthMils <= 0) {
- throw new IllegalArgumentException("widthMils "
- + "cannot be less than or equal to zero.");
- }
- if (heightMils <= 0) {
- throw new IllegalArgumentException("heightMils "
- + "cannot be less than or euqual to zero.");
- }
- mId = id;
- mLabel = label;
- mWidthMils = widthMils;
- mHeightMils = heightMils;
- mLabelResId = 0;
- mPackageName = null;
+ this(id, label, null, widthMils, heightMils, 0);
}
/**
@@ -890,15 +857,37 @@
return definedMediaSizes;
}
- /** @hide */
- public MediaSize(String id, String label, String packageName,
- int widthMils, int heightMils, int labelResId) {
+ /**
+ * Creates a new instance.
+ *
+ * @param id The unique media size id. It is unique amongst other media sizes
+ * supported by the printer.
+ * @param label The <strong>localized</strong> human readable label.
+ * @param packageName The name of the creating package.
+ * @param widthMils The width in mils (thousands of an inch).
+ * @param heightMils The height in mils (thousands of an inch).
+ * @param labelResId The resource if of a human readable label.
+ *
+ * @throws IllegalArgumentException If the id is empty or the label is unset
+ * or the widthMils is less than or equal to zero or the heightMils is less
+ * than or equal to zero.
+ *
+ * @hide
+ */
+ public MediaSize(String id, String label, String packageName, int widthMils, int heightMils,
+ int labelResId) {
mPackageName = packageName;
- mId = id;
+ mId = Preconditions.checkStringNotEmpty(id, "id cannot be empty.");
mLabelResId = labelResId;
- mWidthMils = widthMils;
- mHeightMils = heightMils;
+ mWidthMils = Preconditions.checkArgumentPositive(widthMils, "widthMils cannot be " +
+ "less than or equal to zero.");
+ mHeightMils = Preconditions.checkArgumentPositive(heightMils, "heightMils cannot be " +
+ "less than or equal to zero.");
mLabel = label;
+
+ // The label has to be either a string ot a StringRes
+ Preconditions.checkArgument(!TextUtils.isEmpty(label) !=
+ (!TextUtils.isEmpty(packageName) && labelResId != 0), "label cannot be empty.");
}
/**
@@ -926,10 +915,7 @@
try {
return packageManager.getResourcesForApplication(
mPackageName).getString(mLabelResId);
- } catch (NotFoundException nfe) {
- Log.w(LOG_TAG, "Could not load resouce" + mLabelResId
- + " from package " + mPackageName);
- } catch (NameNotFoundException nnfee) {
+ } catch (NotFoundException | NameNotFoundException e) {
Log.w(LOG_TAG, "Could not load resouce" + mLabelResId
+ " from package " + mPackageName);
}
@@ -1084,10 +1070,10 @@
* the one with 300 DPI resolution.
*/
public static final class Resolution {
- private final String mId;
- private final String mLabel;
- private final int mHorizontalDpi;
- private final int mVerticalDpi;
+ private final @NonNull String mId;
+ private final @NonNull String mLabel;
+ private final @IntRange(from = 1) int mHorizontalDpi;
+ private final @IntRange(from = 1) int mVerticalDpi;
/**
* Creates a new instance.
@@ -1244,8 +1230,7 @@
* @param rightMils The right margin in mils (thousands of an inch).
* @param bottomMils The bottom margin in mils (thousands of an inch).
*/
- public Margins(@IntRange(from = 0) int leftMils, @IntRange(from = 0) int topMils,
- @IntRange(from = 0) int rightMils, @IntRange(from = 0) int bottomMils) {
+ public Margins(int leftMils, int topMils, int rightMils, int bottomMils) {
mTopMils = topMils;
mLeftMils = leftMils;
mRightMils = rightMils;
@@ -1257,7 +1242,7 @@
*
* @return The left margin.
*/
- public @IntRange(from = 0) int getLeftMils() {
+ public int getLeftMils() {
return mLeftMils;
}
@@ -1266,7 +1251,7 @@
*
* @return The top margin.
*/
- public @IntRange(from = 0) int getTopMils() {
+ public int getTopMils() {
return mTopMils;
}
@@ -1275,7 +1260,7 @@
*
* @return The right margin.
*/
- public @IntRange(from = 0) int getRightMils() {
+ public int getRightMils() {
return mRightMils;
}
@@ -1284,7 +1269,7 @@
*
* @return The bottom margin.
*/
- public @IntRange(from = 0) int getBottomMils() {
+ public int getBottomMils() {
return mBottomMils;
}
diff --git a/core/java/android/print/PrintDocumentInfo.java b/core/java/android/print/PrintDocumentInfo.java
index db3b6f4..bec6f29 100644
--- a/core/java/android/print/PrintDocumentInfo.java
+++ b/core/java/android/print/PrintDocumentInfo.java
@@ -22,6 +22,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -114,8 +115,8 @@
*/
public static final int CONTENT_TYPE_PHOTO = 1;
- private String mName;
- private int mPageCount;
+ private @NonNull String mName;
+ private @IntRange(from = -1) int mPageCount;
private int mContentType;
private long mDataSize;
@@ -144,10 +145,11 @@
* @param parcel Data from which to initialize.
*/
private PrintDocumentInfo(Parcel parcel) {
- mName = parcel.readString();
+ mName = Preconditions.checkStringNotEmpty(parcel.readString());
mPageCount = parcel.readInt();
+ Preconditions.checkArgument(mPageCount == PAGE_COUNT_UNKNOWN || mPageCount > 0);
mContentType = parcel.readInt();
- mDataSize = parcel.readLong();
+ mDataSize = Preconditions.checkArgumentNonnegative(parcel.readLong());
}
/**
@@ -180,7 +182,7 @@
* @see #CONTENT_TYPE_DOCUMENT
* @see #CONTENT_TYPE_PHOTO
*/
- public @ContentType int getContentType() {
+ public int getContentType() {
return mContentType;
}
@@ -262,13 +264,13 @@
builder.append("PrintDocumentInfo{");
builder.append("name=").append(mName);
builder.append(", pageCount=").append(mPageCount);
- builder.append(", contentType=").append(contentTyepToString(mContentType));
+ builder.append(", contentType=").append(contentTypeToString(mContentType));
builder.append(", dataSize=").append(mDataSize);
builder.append("}");
return builder.toString();
}
- private String contentTyepToString(int contentType) {
+ private String contentTypeToString(int contentType) {
switch (contentType) {
case CONTENT_TYPE_DOCUMENT: {
return "CONTENT_TYPE_DOCUMENT";
diff --git a/core/java/android/print/PrintJobInfo.java b/core/java/android/print/PrintJobInfo.java
index 7e3a72f..f134943 100644
--- a/core/java/android/print/PrintJobInfo.java
+++ b/core/java/android/print/PrintJobInfo.java
@@ -21,7 +21,10 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.StringRes;
import android.annotation.TestApi;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -181,7 +184,11 @@
private float mProgress;
/** A short string describing the status of this job. */
- private CharSequence mStatus;
+ private @Nullable CharSequence mStatus;
+
+ /** A string resource describing the status of this job. */
+ private @StringRes int mStatusRes;
+ private @Nullable CharSequence mStatusResAppPackageName;
/** Advanced printer specific options. */
private Bundle mAdvancedOptions;
@@ -210,6 +217,8 @@
mDocumentInfo = other.mDocumentInfo;
mProgress = other.mProgress;
mStatus = other.mStatus;
+ mStatusRes = other.mStatusRes;
+ mStatusResAppPackageName = other.mStatusResAppPackageName;
mCanceling = other.mCanceling;
mAdvancedOptions = other.mAdvancedOptions;
}
@@ -235,8 +244,14 @@
mDocumentInfo = (PrintDocumentInfo) parcel.readParcelable(null);
mProgress = parcel.readFloat();
mStatus = parcel.readCharSequence();
+ mStatusRes = parcel.readInt();
+ mStatusResAppPackageName = parcel.readCharSequence();
mCanceling = (parcel.readInt() == 1);
mAdvancedOptions = parcel.readBundle();
+
+ if (mAdvancedOptions != null) {
+ Preconditions.checkArgument(!mAdvancedOptions.containsKey(null));
+ }
}
/**
@@ -370,10 +385,28 @@
* @hide
*/
public void setStatus(@Nullable CharSequence status) {
+ mStatusRes = 0;
+ mStatusResAppPackageName = null;
+
mStatus = status;
}
/**
+ * Sets the status of the print job.
+ *
+ * @param status The new status as a string resource
+ * @param appPackageName App package name the resource belongs to
+ *
+ * @hide
+ */
+ public void setStatus(@StringRes int status, @NonNull CharSequence appPackageName) {
+ mStatus = null;
+
+ mStatusRes = status;
+ mStatusResAppPackageName = appPackageName;
+ }
+
+ /**
* Sets the owning application id.
*
* @return The owning app id.
@@ -633,6 +666,8 @@
parcel.writeParcelable(mDocumentInfo, 0);
parcel.writeFloat(mProgress);
parcel.writeCharSequence(mStatus);
+ parcel.writeInt(mStatusRes);
+ parcel.writeCharSequence(mStatusResAppPackageName);
parcel.writeInt(mCanceling ? 1 : 0);
parcel.writeBundle(mAdvancedOptions);
}
@@ -659,6 +694,9 @@
builder.append(", progress: " + mProgress);
builder.append(", status: " + (mStatus != null
? mStatus.toString() : null));
+ builder.append(", statusRes: " + mStatusRes);
+ builder.append(", statusResAppPackageName: " + (mStatusResAppPackageName != null
+ ? mStatusResAppPackageName.toString() : null));
builder.append("}");
return builder.toString();
}
@@ -707,12 +745,23 @@
/**
* Get the status of this job.
*
+ * @param pm Package manager used to resolve the string
+ *
* @return the status of this job or null if not set
* @hide
*/
@TestApi
- public @Nullable CharSequence getStatus() {
- return mStatus;
+ public @Nullable CharSequence getStatus(@NonNull PackageManager pm) {
+ if (mStatusRes == 0) {
+ return mStatus;
+ } else {
+ try {
+ return pm.getResourcesForApplication(mStatusResAppPackageName.toString())
+ .getString(mStatusRes);
+ } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
+ return null;
+ }
+ }
}
/**
@@ -789,6 +838,8 @@
* @param value The option value.
*/
public void putAdvancedOption(@NonNull String key, @Nullable String value) {
+ Preconditions.checkNotNull(key, "key cannot be null");
+
if (mPrototype.mAdvancedOptions == null) {
mPrototype.mAdvancedOptions = new Bundle();
}
diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java
index 25fc968..71f0bd6 100644
--- a/core/java/android/print/PrintManager.java
+++ b/core/java/android/print/PrintManager.java
@@ -36,12 +36,15 @@
import android.print.PrintDocumentAdapter.LayoutResultCallback;
import android.print.PrintDocumentAdapter.WriteResultCallback;
import android.printservice.PrintServiceInfo;
+import android.printservice.recommendation.IRecommendationsChangeListener;
+import android.printservice.recommendation.RecommendationInfo;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import com.android.internal.os.SomeArgs;
+import com.android.internal.util.Preconditions;
import libcore.io.IoUtils;
import java.lang.ref.WeakReference;
@@ -113,6 +116,7 @@
private static final int MSG_NOTIFY_PRINT_JOB_STATE_CHANGED = 1;
private static final int MSG_NOTIFY_PRINT_SERVICES_CHANGED = 2;
+ private static final int MSG_NOTIFY_PRINT_SERVICE_RECOMMENDATIONS_CHANGED = 3;
/**
* Package name of print spooler.
@@ -202,6 +206,9 @@
mPrintJobStateChangeListeners;
private Map<PrintServicesChangeListener, PrintServicesChangeListenerWrapper>
mPrintServicesChangeListeners;
+ private Map<PrintServiceRecommendationsChangeListener,
+ PrintServiceRecommendationsChangeListenerWrapper>
+ mPrintServiceRecommendationsChangeListeners;
/** @hide */
public interface PrintJobStateChangeListener {
@@ -223,6 +230,15 @@
public void onPrintServicesChanged();
}
+ /** @hide */
+ public interface PrintServiceRecommendationsChangeListener {
+
+ /**
+ * Callback notifying that the print service recommendations changed.
+ */
+ void onPrintServiceRecommendationsChanged();
+ }
+
/**
* Creates a new instance.
*
@@ -260,7 +276,14 @@
listener.onPrintServicesChanged();
}
} break;
-
+ case MSG_NOTIFY_PRINT_SERVICE_RECOMMENDATIONS_CHANGED: {
+ PrintServiceRecommendationsChangeListenerWrapper wrapper =
+ (PrintServiceRecommendationsChangeListenerWrapper) message.obj;
+ PrintServiceRecommendationsChangeListener listener = wrapper.getListener();
+ if (listener != null) {
+ listener.onPrintServiceRecommendationsChanged();
+ }
+ } break;
}
}
};
@@ -539,13 +562,14 @@
* @see android.print.PrintManager#getPrintServices
*/
void addPrintServicesChangeListener(@NonNull PrintServicesChangeListener listener) {
+ Preconditions.checkNotNull(listener);
+
if (mService == null) {
Log.w(LOG_TAG, "Feature android.software.print not available");
return;
}
if (mPrintServicesChangeListeners == null) {
- mPrintServicesChangeListeners = new ArrayMap<PrintServicesChangeListener,
- PrintServicesChangeListenerWrapper>();
+ mPrintServicesChangeListeners = new ArrayMap<>();
}
PrintServicesChangeListenerWrapper wrappedListener =
new PrintServicesChangeListenerWrapper(listener, mHandler);
@@ -565,6 +589,8 @@
* @see android.print.PrintManager#getPrintServices
*/
void removePrintServicesChangeListener(@NonNull PrintServicesChangeListener listener) {
+ Preconditions.checkNotNull(listener);
+
if (mService == null) {
Log.w(LOG_TAG, "Feature android.software.print not available");
return;
@@ -588,7 +614,6 @@
}
}
-
/**
* Gets the list of print services, but does not register for updates. The user has to register
* for updates by itself, or use {@link PrintServicesLoader}.
@@ -596,7 +621,7 @@
* @param selectionFlags flags selecting which services to get. Either
* {@link #ENABLED_SERVICES},{@link #DISABLED_SERVICES}, or both.
*
- * @return The enabled service list or an empty list.
+ * @return The print service list or an empty list.
*
* @see #addPrintServicesChangeListener(PrintServicesChangeListener)
* @see #removePrintServicesChangeListener(PrintServicesChangeListener)
@@ -604,6 +629,8 @@
* @hide
*/
public @NonNull List<PrintServiceInfo> getPrintServices(int selectionFlags) {
+ Preconditions.checkFlagsArgument(selectionFlags, ALL_SERVICES);
+
try {
List<PrintServiceInfo> services = mService.getPrintServices(selectionFlags, mUserId);
if (services != null) {
@@ -616,6 +643,92 @@
}
/**
+ * Listen for changes to the print service recommendations.
+ *
+ * @param listener the listener to add
+ *
+ * @see android.print.PrintManager#getPrintServiceRecommendations
+ */
+ void addPrintServiceRecommendationsChangeListener(
+ @NonNull PrintServiceRecommendationsChangeListener listener) {
+ Preconditions.checkNotNull(listener);
+
+ if (mService == null) {
+ Log.w(LOG_TAG, "Feature android.software.print not available");
+ return;
+ }
+ if (mPrintServiceRecommendationsChangeListeners == null) {
+ mPrintServiceRecommendationsChangeListeners = new ArrayMap<>();
+ }
+ PrintServiceRecommendationsChangeListenerWrapper wrappedListener =
+ new PrintServiceRecommendationsChangeListenerWrapper(listener, mHandler);
+ try {
+ mService.addPrintServiceRecommendationsChangeListener(wrappedListener, mUserId);
+ mPrintServiceRecommendationsChangeListeners.put(listener, wrappedListener);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Stop listening for changes to the print service recommendations.
+ *
+ * @param listener the listener to remove
+ *
+ * @see android.print.PrintManager#getPrintServiceRecommendations
+ */
+ void removePrintServiceRecommendationsChangeListener(
+ @NonNull PrintServiceRecommendationsChangeListener listener) {
+ Preconditions.checkNotNull(listener);
+
+ if (mService == null) {
+ Log.w(LOG_TAG, "Feature android.software.print not available");
+ return;
+ }
+ if (mPrintServiceRecommendationsChangeListeners == null) {
+ return;
+ }
+ PrintServiceRecommendationsChangeListenerWrapper wrappedListener =
+ mPrintServiceRecommendationsChangeListeners.remove(listener);
+ if (wrappedListener == null) {
+ return;
+ }
+ if (mPrintServiceRecommendationsChangeListeners.isEmpty()) {
+ mPrintServiceRecommendationsChangeListeners = null;
+ }
+ wrappedListener.destroy();
+ try {
+ mService.removePrintServiceRecommendationsChangeListener(wrappedListener, mUserId);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets the list of print service recommendations, but does not register for updates. The user
+ * has to register for updates by itself, or use {@link PrintServiceRecommendationsLoader}.
+ *
+ * @return The print service recommendations list or an empty list.
+ *
+ * @see #addPrintServiceRecommendationsChangeListener
+ * @see #removePrintServiceRecommendationsChangeListener
+ *
+ * @hide
+ */
+ public @NonNull List<RecommendationInfo> getPrintServiceRecommendations() {
+ try {
+ List<RecommendationInfo> recommendations =
+ mService.getPrintServiceRecommendations(mUserId);
+ if (recommendations != null) {
+ return recommendations;
+ }
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ return Collections.emptyList();
+ }
+
+ /**
* @hide
*/
public PrinterDiscoverySession createPrinterDiscoverySession() {
@@ -1242,4 +1355,37 @@
return mWeakListener.get();
}
}
+
+ /**
+ * @hide
+ */
+ public static final class PrintServiceRecommendationsChangeListenerWrapper extends
+ IRecommendationsChangeListener.Stub {
+ private final WeakReference<PrintServiceRecommendationsChangeListener> mWeakListener;
+ private final WeakReference<Handler> mWeakHandler;
+
+ public PrintServiceRecommendationsChangeListenerWrapper(
+ PrintServiceRecommendationsChangeListener listener, Handler handler) {
+ mWeakListener = new WeakReference<>(listener);
+ mWeakHandler = new WeakReference<>(handler);
+ }
+
+ @Override
+ public void onRecommendationsChanged() {
+ Handler handler = mWeakHandler.get();
+ PrintServiceRecommendationsChangeListener listener = mWeakListener.get();
+ if (handler != null && listener != null) {
+ handler.obtainMessage(MSG_NOTIFY_PRINT_SERVICE_RECOMMENDATIONS_CHANGED,
+ this).sendToTarget();
+ }
+ }
+
+ public void destroy() {
+ mWeakListener.clear();
+ }
+
+ public PrintServiceRecommendationsChangeListener getListener() {
+ return mWeakListener.get();
+ }
+ }
}
diff --git a/core/java/android/print/PrintServiceRecommendationsLoader.java b/core/java/android/print/PrintServiceRecommendationsLoader.java
new file mode 100644
index 0000000..bb5d065
--- /dev/null
+++ b/core/java/android/print/PrintServiceRecommendationsLoader.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.Loader;
+import android.os.Handler;
+import android.os.Message;
+import android.printservice.recommendation.RecommendationInfo;
+import com.android.internal.util.Preconditions;
+
+import java.util.List;
+
+/**
+ * Loader for the list of print service recommendations.
+ *
+ * @hide
+ */
+public class PrintServiceRecommendationsLoader extends Loader<List<RecommendationInfo>> {
+ /** The print manager to be used by this object */
+ private final @NonNull PrintManager mPrintManager;
+
+ /** Handler to sequentialize the delivery of the results to the main thread */
+ private final Handler mHandler;
+
+ /** Listens for updates to the data from the platform */
+ private PrintManager.PrintServiceRecommendationsChangeListener mListener;
+
+ /**
+ * Create a new PrintServicesLoader.
+ *
+ * @param printManager The print manager supplying the data
+ * @param context Context of the using object
+ */
+ public PrintServiceRecommendationsLoader(@NonNull PrintManager printManager,
+ @NonNull Context context) {
+ super(Preconditions.checkNotNull(context));
+ mHandler = new MyHandler();
+ mPrintManager = Preconditions.checkNotNull(printManager);
+ }
+
+ @Override
+ protected void onForceLoad() {
+ queueNewResult();
+ }
+
+ /**
+ * Read the print service recommendations and queue it to be delivered on the main thread.
+ */
+ private void queueNewResult() {
+ Message m = mHandler.obtainMessage(0);
+ m.obj = mPrintManager.getPrintServiceRecommendations();
+ mHandler.sendMessage(m);
+ }
+
+ @Override
+ protected void onStartLoading() {
+ mListener = new PrintManager.PrintServiceRecommendationsChangeListener() {
+ @Override
+ public void onPrintServiceRecommendationsChanged() {
+ queueNewResult();
+ }
+ };
+
+ mPrintManager.addPrintServiceRecommendationsChangeListener(mListener);
+
+ // Immediately deliver a result
+ deliverResult(mPrintManager.getPrintServiceRecommendations());
+ }
+
+ @Override
+ protected void onStopLoading() {
+ if (mListener != null) {
+ mPrintManager.removePrintServiceRecommendationsChangeListener(mListener);
+ mListener = null;
+ }
+
+ if (mHandler != null) {
+ mHandler.removeMessages(0);
+ }
+ }
+
+ @Override
+ protected void onReset() {
+ onStopLoading();
+ }
+
+ /**
+ * Handler to sequentialize all the updates to the main thread.
+ */
+ private class MyHandler extends Handler {
+ /**
+ * Create a new handler on the main thread.
+ */
+ public MyHandler() {
+ super(getContext().getMainLooper());
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (isStarted()) {
+ deliverResult((List<RecommendationInfo>) msg.obj);
+ }
+ }
+ }
+}
diff --git a/core/java/android/print/PrintServicesLoader.java b/core/java/android/print/PrintServicesLoader.java
index ed41114..60d7d66 100644
--- a/core/java/android/print/PrintServicesLoader.java
+++ b/core/java/android/print/PrintServicesLoader.java
@@ -22,6 +22,7 @@
import android.os.Handler;
import android.os.Message;
import android.printservice.PrintServiceInfo;
+import com.android.internal.util.Preconditions;
import java.util.List;
@@ -46,13 +47,16 @@
/**
* Create a new PrintServicesLoader.
*
+ * @param printManager The print manager supplying the data
+ * @param context Context of the using object
* @param selectionFlags What type of services to load.
*/
public PrintServicesLoader(@NonNull PrintManager printManager, @NonNull Context context,
int selectionFlags) {
- super(context);
- mPrintManager = printManager;
- mSelectionFlags = selectionFlags;
+ super(Preconditions.checkNotNull(context));
+ mPrintManager = Preconditions.checkNotNull(printManager);
+ mSelectionFlags = Preconditions.checkFlagsArgument(selectionFlags,
+ PrintManager.ALL_SERVICES);
}
@Override
diff --git a/core/java/android/print/PrinterCapabilitiesInfo.java b/core/java/android/print/PrinterCapabilitiesInfo.java
index d13879b..01c23f6 100644
--- a/core/java/android/print/PrinterCapabilitiesInfo.java
+++ b/core/java/android/print/PrinterCapabilitiesInfo.java
@@ -24,11 +24,13 @@
import android.print.PrintAttributes.Margins;
import android.print.PrintAttributes.MediaSize;
import android.print.PrintAttributes.Resolution;
+import com.android.internal.util.Preconditions;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.function.IntConsumer;
/**
* This class represents the capabilities of a printer. Instances
@@ -55,9 +57,9 @@
private static final Margins DEFAULT_MARGINS = new Margins(0, 0, 0, 0);
- private Margins mMinMargins = DEFAULT_MARGINS;
- private List<MediaSize> mMediaSizes;
- private List<Resolution> mResolutions;
+ private @NonNull Margins mMinMargins = DEFAULT_MARGINS;
+ private @NonNull List<MediaSize> mMediaSizes;
+ private @NonNull List<Resolution> mResolutions;
private int mColorModes;
private int mDuplexModes;
@@ -205,15 +207,37 @@
return builder.build();
}
+ /**
+ * Call enforceSingle for each bit in the mask.
+ *
+ * @param mask The mask
+ * @param enforceSingle The function to call
+ */
+ private static void enforceValidMask(int mask, IntConsumer enforceSingle) {
+ int current = mask;
+ while (current > 0) {
+ final int currentMode = (1 << Integer.numberOfTrailingZeros(current));
+ current &= ~currentMode;
+ enforceSingle.accept(currentMode);
+ }
+ }
+
private PrinterCapabilitiesInfo(Parcel parcel) {
- mMinMargins = readMargins(parcel);
+ mMinMargins = Preconditions.checkNotNull(readMargins(parcel));
readMediaSizes(parcel);
readResolutions(parcel);
mColorModes = parcel.readInt();
+ enforceValidMask(mColorModes,
+ (currentMode) -> PrintAttributes.enforceValidColorMode(currentMode));
+
mDuplexModes = parcel.readInt();
+ enforceValidMask(mDuplexModes,
+ (currentMode) -> PrintAttributes.enforceValidDuplexMode(currentMode));
readDefaults(parcel);
+ Preconditions.checkArgument(mMediaSizes.size() > mDefaults[PROPERTY_MEDIA_SIZE]);
+ Preconditions.checkArgument(mResolutions.size() > mDefaults[PROPERTY_RESOLUTION]);
}
@Override
@@ -537,12 +561,8 @@
*/
public @NonNull Builder setColorModes(@ColorMode int colorModes,
@ColorMode int defaultColorMode) {
- int currentModes = colorModes;
- while (currentModes > 0) {
- final int currentMode = (1 << Integer.numberOfTrailingZeros(currentModes));
- currentModes &= ~currentMode;
- PrintAttributes.enforceValidColorMode(currentMode);
- }
+ enforceValidMask(colorModes,
+ (currentMode) -> PrintAttributes.enforceValidColorMode(currentMode));
PrintAttributes.enforceValidColorMode(defaultColorMode);
mPrototype.mColorModes = colorModes;
mPrototype.mDefaults[PROPERTY_COLOR_MODE] = defaultColorMode;
@@ -568,12 +588,8 @@
*/
public @NonNull Builder setDuplexModes(@DuplexMode int duplexModes,
@DuplexMode int defaultDuplexMode) {
- int currentModes = duplexModes;
- while (currentModes > 0) {
- final int currentMode = (1 << Integer.numberOfTrailingZeros(currentModes));
- currentModes &= ~currentMode;
- PrintAttributes.enforceValidDuplexMode(currentMode);
- }
+ enforceValidMask(duplexModes,
+ (currentMode) -> PrintAttributes.enforceValidDuplexMode(currentMode));
PrintAttributes.enforceValidDuplexMode(defaultDuplexMode);
mPrototype.mDuplexModes = duplexModes;
mPrototype.mDefaults[PROPERTY_DUPLEX_MODE] = defaultDuplexMode;
diff --git a/core/java/android/print/PrinterInfo.java b/core/java/android/print/PrinterInfo.java
index 0d2d9f4..1ee6389 100644
--- a/core/java/android/print/PrinterInfo.java
+++ b/core/java/android/print/PrinterInfo.java
@@ -467,10 +467,12 @@
* {@link android.printservice.PrinterDiscoverySession#onRequestCustomPrinterIcon}.
* </p>
*
+ * @param hasCustomPrinterIcon If the printer has a custom icon or not.
+ *
* @return This builder.
*/
- public @NonNull Builder setHasCustomPrinterIcon() {
- mHasCustomPrinterIcon = true;
+ public @NonNull Builder setHasCustomPrinterIcon(boolean hasCustomPrinterIcon) {
+ mHasCustomPrinterIcon = hasCustomPrinterIcon;
return this;
}
diff --git a/core/java/android/printservice/IPrintServiceClient.aidl b/core/java/android/printservice/IPrintServiceClient.aidl
index 0ae1e18..f0ea6ae 100644
--- a/core/java/android/printservice/IPrintServiceClient.aidl
+++ b/core/java/android/printservice/IPrintServiceClient.aidl
@@ -52,6 +52,15 @@
*/
void setStatus(in PrintJobId printJobId, in CharSequence status);
+ /**
+ * Set the status of this print job
+ *
+ * @param printJobId The print job to update
+ * @param status The new status as a string resource
+ * @param appPackageName The app package name the string belongs to
+ */
+ void setStatusRes(in PrintJobId printJobId, int status, in CharSequence appPackageName);
+
void onPrintersAdded(in ParceledListSlice printers);
void onPrintersRemoved(in ParceledListSlice printerIds);
diff --git a/core/java/android/printservice/PrintJob.java b/core/java/android/printservice/PrintJob.java
index 6414b6a..7a7ca23 100644
--- a/core/java/android/printservice/PrintJob.java
+++ b/core/java/android/printservice/PrintJob.java
@@ -20,11 +20,14 @@
import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.StringRes;
+import android.content.Context;
import android.os.RemoteException;
import android.print.PrintJobId;
import android.print.PrintJobInfo;
import android.text.TextUtils;
import android.util.Log;
+import com.android.internal.util.Preconditions;
/**
* This class represents a print job from the perspective of a print
@@ -45,7 +48,12 @@
private PrintJobInfo mCachedInfo;
- PrintJob(@NonNull PrintJobInfo jobInfo, @NonNull IPrintServiceClient client) {
+ /** Context that created the object */
+ private final Context mContext;
+
+ PrintJob(@NonNull Context context, @NonNull PrintJobInfo jobInfo,
+ @NonNull IPrintServiceClient client) {
+ mContext = context;
mCachedInfo = jobInfo;
mPrintServiceClient = client;
mDocument = new PrintDocument(mCachedInfo.getId(), client,
@@ -216,11 +224,10 @@
}
/**
- * Blocks the print job. You should call this method if {@link
- * #isStarted()} or {@link #isBlocked()} returns true and you need
- * to block the print job. For example, the user has to add some
- * paper to continue printing. To resume the print job call {@link
- * #start()}.
+ * Blocks the print job. You should call this method if {@link #isStarted()} returns true and
+ * you need to block the print job. For example, the user has to add some paper to continue
+ * printing. To resume the print job call {@link #start()}. To change the reason call
+ * {@link #setStatus(CharSequence)}.
*
* @param reason The human readable, short, and translated reason why the print job is blocked.
* @return Whether the job was blocked.
@@ -233,9 +240,7 @@
PrintService.throwIfNotCalledOnMainThread();
PrintJobInfo info = getInfo();
final int state = info.getState();
- if (state == PrintJobInfo.STATE_STARTED
- || (state == PrintJobInfo.STATE_BLOCKED
- && !TextUtils.equals(info.getStatus(), reason))) {
+ if (state == PrintJobInfo.STATE_STARTED || state == PrintJobInfo.STATE_BLOCKED) {
return setState(PrintJobInfo.STATE_BLOCKED, reason);
}
return false;
@@ -320,6 +325,9 @@
/**
* Sets the status of this print job. This should be a human readable, short, and translated
* description of the current state of the print job.
+ * <p />
+ * This overrides any previously set status set via {@link #setStatus(CharSequence)},
+ * {@link #setStatus(int)}, {@link #block(String)}, or {@link #fail(String)},
*
* @param status The new status. If null the status will be empty.
*/
@@ -335,6 +343,29 @@
}
/**
+ * Sets the status of this print job as a string resource.
+ * <p />
+ * This overrides any previously set status set via {@link #setStatus(CharSequence)},
+ * {@link #setStatus(int)}, {@link #block(String)}, or {@link #fail(String)},
+ * <p />
+ * To clear the status use {@link #setStatus(CharSequence) <code>setStatus(null)</code>}
+ *
+ * @param status The new status as a String resource.
+ */
+ @MainThread
+ public void setStatus(@StringRes int status) {
+ PrintService.throwIfNotCalledOnMainThread();
+ Preconditions.checkArgument(status != 0, "status has to be != 0");
+
+ try {
+ mPrintServiceClient.setStatusRes(mCachedInfo.getId(), status,
+ mContext.getPackageName());
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error setting status for job: " + mCachedInfo.getId(), re);
+ }
+ }
+
+ /**
* Sets a tag that is valid in the context of a {@link PrintService}
* and is not interpreted by the system. For example, a print service
* may set as a tag the key of the print job returned by a remote
diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java
index 62d214e..8f73518 100644
--- a/core/java/android/printservice/PrintService.java
+++ b/core/java/android/printservice/PrintService.java
@@ -329,7 +329,7 @@
final int printJobInfoCount = printJobInfos.size();
printJobs = new ArrayList<PrintJob>(printJobInfoCount);
for (int i = 0; i < printJobInfoCount; i++) {
- printJobs.add(new PrintJob(printJobInfos.get(i), mClient));
+ printJobs.add(new PrintJob(this, printJobInfos.get(i), mClient));
}
}
if (printJobs != null) {
@@ -549,7 +549,7 @@
+ getPackageName());
}
PrintJobInfo printJobInfo = (PrintJobInfo) message.obj;
- onRequestCancelPrintJob(new PrintJob(printJobInfo, mClient));
+ onRequestCancelPrintJob(new PrintJob(PrintService.this, printJobInfo, mClient));
} break;
case MSG_ON_PRINTJOB_QUEUED: {
@@ -561,7 +561,7 @@
if (DEBUG) {
Log.i(LOG_TAG, "Queued: " + printJobInfo);
}
- onPrintJobQueued(new PrintJob(printJobInfo, mClient));
+ onPrintJobQueued(new PrintJob(PrintService.this, printJobInfo, mClient));
} break;
case MSG_SET_CLIENT: {
diff --git a/core/java/android/printservice/PrinterDiscoverySession.java b/core/java/android/printservice/PrinterDiscoverySession.java
index cd5a903..7b9533d 100644
--- a/core/java/android/printservice/PrinterDiscoverySession.java
+++ b/core/java/android/printservice/PrinterDiscoverySession.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.content.pm.ParceledListSlice;
+import android.os.CancellationSignal;
import android.os.RemoteException;
import android.print.PrinterCapabilitiesInfo;
import android.print.PrinterId;
@@ -412,11 +413,13 @@
* service.
*
* @param printerId The printer to icon belongs to.
+ * @param cancellationSignal Signal used to cancel the request
* @param callback Callback for returning the icon to the print spooler.
*
* @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon()
*/
public void onRequestCustomPrinterIcon(@NonNull PrinterId printerId,
+ @NonNull CancellationSignal cancellationSignal,
@NonNull CustomPrinterIconCallback callback) {
}
@@ -533,7 +536,7 @@
if (!mIsDestroyed && mObserver != null) {
CustomPrinterIconCallback callback = new CustomPrinterIconCallback(printerId,
mObserver);
- onRequestCustomPrinterIcon(printerId, callback);
+ onRequestCustomPrinterIcon(printerId, new CancellationSignal(), callback);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/ResetBackgroundScrimEvent.java b/core/java/android/printservice/recommendation/IRecommendationService.aidl
similarity index 60%
copy from packages/SystemUI/src/com/android/systemui/recents/events/ui/ResetBackgroundScrimEvent.java
copy to core/java/android/printservice/recommendation/IRecommendationService.aidl
index 863f40b..ce9ea6f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/ResetBackgroundScrimEvent.java
+++ b/core/java/android/printservice/recommendation/IRecommendationService.aidl
@@ -14,13 +14,17 @@
* limitations under the License.
*/
-package com.android.systemui.recents.events.ui;
+package android.printservice.recommendation;
-import com.android.systemui.recents.events.EventBus;
+import android.printservice.recommendation.IRecommendationServiceCallbacks;
/**
- * This is sent to reset the background scrim back to the initial state.
+ * Interface for communication with the print service recommendation service.
+ *
+ * @see android.print.IPrintServiceRecommendationServiceCallbacks
+ *
+ * @hide
*/
-public class ResetBackgroundScrimEvent extends EventBus.Event {
- // Simple event
+oneway interface IRecommendationService {
+ void registerCallbacks(in IRecommendationServiceCallbacks callbacks);
}
diff --git a/core/java/android/printservice/recommendation/IRecommendationServiceCallbacks.aidl b/core/java/android/printservice/recommendation/IRecommendationServiceCallbacks.aidl
new file mode 100644
index 0000000..9528654
--- /dev/null
+++ b/core/java/android/printservice/recommendation/IRecommendationServiceCallbacks.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.printservice.recommendation;
+
+import android.printservice.recommendation.RecommendationInfo;
+
+/**
+ * Callbacks for communication with the print service recommendation service.
+ *
+ * @see android.print.IPrintServiceRecommendationService
+ *
+ * @hide
+ */
+oneway interface IRecommendationServiceCallbacks {
+ /**
+ * Update the print service recommendations.
+ *
+ * @param recommendations the new print service recommendations
+ */
+ void onRecommendationsUpdated(in List<RecommendationInfo> recommendations);
+}
diff --git a/core/java/android/net/IConnectivityMetricsLoggerSubscriber.aidl b/core/java/android/printservice/recommendation/IRecommendationsChangeListener.aidl
similarity index 72%
rename from core/java/android/net/IConnectivityMetricsLoggerSubscriber.aidl
rename to core/java/android/printservice/recommendation/IRecommendationsChangeListener.aidl
index a2c62cd..8ca5c69 100644
--- a/core/java/android/net/IConnectivityMetricsLoggerSubscriber.aidl
+++ b/core/java/android/printservice/recommendation/IRecommendationsChangeListener.aidl
@@ -14,12 +14,13 @@
* limitations under the License.
*/
-package android.net;
+package android.printservice.recommendation;
-import android.net.ConnectivityMetricsEvent;
-
-/** {@hide} */
-oneway interface IConnectivityMetricsLoggerSubscriber {
-
- void onEvents(in ConnectivityMetricsEvent[] events);
+/**
+ * Interface for observing changes of the print service recommendations.
+ *
+ * @hide
+ */
+oneway interface IRecommendationsChangeListener {
+ void onRecommendationsChanged();
}
diff --git a/core/java/android/net/IConnectivityMetricsLoggerSubscriber.aidl b/core/java/android/printservice/recommendation/RecommendationInfo.aidl
similarity index 62%
copy from core/java/android/net/IConnectivityMetricsLoggerSubscriber.aidl
copy to core/java/android/printservice/recommendation/RecommendationInfo.aidl
index a2c62cd..f21d0bf 100644
--- a/core/java/android/net/IConnectivityMetricsLoggerSubscriber.aidl
+++ b/core/java/android/printservice/recommendation/RecommendationInfo.aidl
@@ -1,11 +1,11 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
+/**
+ * Copyright (c) 2016, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,12 +14,9 @@
* limitations under the License.
*/
-package android.net;
+package android.printservice.recommendation;
-import android.net.ConnectivityMetricsEvent;
-
-/** {@hide} */
-oneway interface IConnectivityMetricsLoggerSubscriber {
-
- void onEvents(in ConnectivityMetricsEvent[] events);
-}
+/**
+ * @hide
+ */
+parcelable RecommendationInfo;
diff --git a/core/java/android/printservice/recommendation/RecommendationInfo.java b/core/java/android/printservice/recommendation/RecommendationInfo.java
new file mode 100644
index 0000000..65d534e
--- /dev/null
+++ b/core/java/android/printservice/recommendation/RecommendationInfo.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.printservice.recommendation;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.printservice.PrintService;
+import com.android.internal.util.Preconditions;
+
+/**
+ * A recommendation to install a {@link PrintService print service}.
+ *
+ * @hide
+ */
+@SystemApi
+public final class RecommendationInfo implements Parcelable {
+ /** Package name of the print service. */
+ private @NonNull final CharSequence mPackageName;
+
+ /** Display name of the print service. */
+ private @NonNull final CharSequence mName;
+
+ /** Number of printers the print service would discover if installed. */
+ private @IntRange(from = 0) final int mNumDiscoveredPrinters;
+
+ /** If the service detects printer from multiple vendors. */
+ private final boolean mRecommendsMultiVendorService;
+
+ /**
+ * Create a new recommendation.
+ *
+ * @param packageName Package name of the print service
+ * @param name Display name of the print service
+ * @param numDiscoveredPrinters Number of printers the print service would discover if
+ * installed
+ * @param recommendsMultiVendorService If the service detects printer from multiple vendor
+ */
+ public RecommendationInfo(@NonNull CharSequence packageName, @NonNull CharSequence name,
+ @IntRange(from = 0) int numDiscoveredPrinters, boolean recommendsMultiVendorService) {
+ mPackageName = Preconditions.checkStringNotEmpty(packageName);
+ mName = Preconditions.checkStringNotEmpty(name);
+ mNumDiscoveredPrinters = Preconditions.checkArgumentNonnegative(numDiscoveredPrinters);
+ mRecommendsMultiVendorService = recommendsMultiVendorService;
+ }
+
+ /**
+ * Create a new recommendation from a parcel.
+ *
+ * @param parcel The parcel containing the data
+ *
+ * @see #CREATOR
+ */
+ private RecommendationInfo(@NonNull Parcel parcel) {
+ this(parcel.readCharSequence(), parcel.readCharSequence(), parcel.readInt(),
+ parcel.readByte() != 0);
+ }
+
+ /**
+ * @return The package name the recommendations recommends.
+ */
+ public CharSequence getPackageName() {
+ return mPackageName;
+ }
+
+ /**
+ * @return Whether the recommended print service detects printers of more than one vendor.
+ */
+ public boolean recommendsMultiVendorService() {
+ return mRecommendsMultiVendorService;
+ }
+
+ /**
+ * @return The number of printer the print service would detect.
+ */
+ public int getNumDiscoveredPrinters() {
+ return mNumDiscoveredPrinters;
+ }
+
+ /**
+ * @return The name of the recommended print service.
+ */
+ public CharSequence getName() {
+ return mName;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeCharSequence(mPackageName);
+ dest.writeCharSequence(mName);
+ dest.writeInt(mNumDiscoveredPrinters);
+ dest.writeByte((byte) (mRecommendsMultiVendorService ? 1 : 0));
+ }
+
+ /**
+ * Utility class used to create new print service recommendation objects from parcels.
+ *
+ * @see #RecommendationInfo(Parcel)
+ */
+ public static final Creator<RecommendationInfo> CREATOR =
+ new Creator<RecommendationInfo>() {
+ @Override
+ public RecommendationInfo createFromParcel(Parcel in) {
+ return new RecommendationInfo(in);
+ }
+
+ @Override
+ public RecommendationInfo[] newArray(int size) {
+ return new RecommendationInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/printservice/recommendation/RecommendationService.java b/core/java/android/printservice/recommendation/RecommendationService.java
new file mode 100644
index 0000000..b7ea512
--- /dev/null
+++ b/core/java/android/printservice/recommendation/RecommendationService.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.printservice.recommendation;
+
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.List;
+
+/**
+ * Base class for the print service recommendation services.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class RecommendationService extends Service {
+ private static final String LOG_TAG = "PrintServiceRecS";
+
+ /** Used to push onConnect and onDisconnect on the main thread */
+ private Handler mHandler;
+
+ /**
+ * The {@link Intent} action that must be declared as handled by a service in its manifest for
+ * the system to recognize it as a print service recommendation service.
+ *
+ * @hide
+ */
+ public static final String SERVICE_INTERFACE =
+ "android.printservice.recommendation.RecommendationService";
+
+ /** Registered callbacks, only modified on main thread */
+ private IRecommendationServiceCallbacks mCallbacks;
+
+ @Override
+ protected void attachBaseContext(Context base) {
+ super.attachBaseContext(base);
+
+ mHandler = new MyHandler();
+ }
+
+ /**
+ * Update the print service recommendations.
+ *
+ * @param recommendations The new set of recommendations
+ */
+ public final void updateRecommendations(@Nullable List<RecommendationInfo> recommendations) {
+ mHandler.obtainMessage(MyHandler.MSG_UPDATE, recommendations).sendToTarget();
+ }
+
+ @Override
+ public final IBinder onBind(Intent intent) {
+ return new IRecommendationService.Stub() {
+ @Override
+ public void registerCallbacks(IRecommendationServiceCallbacks callbacks) {
+ // The callbacks come in order of the caller on oneway calls. Hence while the caller
+ // cannot know at what time the connection is made, he can know the ordering of
+ // connection and disconnection.
+ //
+ // Similar he cannot know when the disconnection is processed, hence he has to
+ // handle callbacks after calling disconnect.
+ if (callbacks != null) {
+ mHandler.obtainMessage(MyHandler.MSG_CONNECT, callbacks).sendToTarget();
+ } else {
+ mHandler.obtainMessage(MyHandler.MSG_DISCONNECT).sendToTarget();
+ }
+ }
+ };
+ }
+
+ /**
+ * Called when the client connects to the recommendation service.
+ */
+ public abstract void onConnected();
+
+ /**
+ * Called when the client disconnects from the recommendation service.
+ */
+ public abstract void onDisconnected();
+
+ private class MyHandler extends Handler {
+ static final int MSG_CONNECT = 1;
+ static final int MSG_DISCONNECT = 2;
+ static final int MSG_UPDATE = 3;
+
+ MyHandler() {
+ super(Looper.getMainLooper());
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_CONNECT:
+ mCallbacks = (IRecommendationServiceCallbacks) msg.obj;
+ onConnected();
+ break;
+ case MSG_DISCONNECT:
+ onDisconnected();
+ mCallbacks = null;
+ break;
+ case MSG_UPDATE:
+ // Note that there might be a connection change in progress. In this case the
+ // message is handled as before the change. This is acceptable as the caller of
+ // the connection change has not guarantee when the connection change binder
+ // transaction is actually processed.
+ try {
+ mCallbacks.onRecommendationsUpdated((List<RecommendationInfo>) msg.obj);
+ } catch (RemoteException | NullPointerException e) {
+ Log.e(LOG_TAG, "Could not update recommended services", e);
+ }
+ break;
+ }
+ }
+ }
+}
diff --git a/core/java/android/provider/BlockedNumberContract.java b/core/java/android/provider/BlockedNumberContract.java
index 6fe0189..e90dc9c 100644
--- a/core/java/android/provider/BlockedNumberContract.java
+++ b/core/java/android/provider/BlockedNumberContract.java
@@ -15,6 +15,7 @@
*/
package android.provider;
+import android.annotation.WorkerThread;
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
@@ -109,6 +110,8 @@
* Uri uri = getContentResolver().insert(BlockedNumbers.CONTENT_URI, values);
* getContentResolver().delete(uri, null, null);
* </pre>
+ * To check if a particular number is blocked, use the method
+ * {@link #isBlocked(Context, String)}.
* </p>
* </dd>
* <dt><b>Query</b></dt>
@@ -120,8 +123,12 @@
* new String[]{BlockedNumbers.COLUMN_ID, BlockedNumbers.COLUMN_ORIGINAL_NUMBER,
* BlockedNumbers.COLUMN_E164_NUMBER}, null, null, null);
* </pre>
- * To check if a particular number is blocked, use the method
- * {@link #isBlocked(Context, String)}.
+ * </p>
+ * </dd>
+ * <dt><b>Unblock</b></dt>
+ * <dd>
+ * <p>
+ * Use the method {@link #unblock(Context, String)} to unblock numbers.
* </p>
* </dd>
*
@@ -206,9 +213,15 @@
public static final String METHOD_IS_BLOCKED = "is_blocked";
/** @hide */
+ public static final String METHOD_UNBLOCK= "unblock";
+
+ /** @hide */
public static final String RES_NUMBER_IS_BLOCKED = "blocked";
/** @hide */
+ public static final String RES_NUM_ROWS_DELETED = "num_deleted";
+
+ /** @hide */
public static final String METHOD_CAN_CURRENT_USER_BLOCK_NUMBERS =
"can_current_user_block_numbers";
@@ -217,9 +230,15 @@
/**
* Returns whether a given number is in the blocked list.
+ *
+ * <p> This matches the {@code phoneNumber} against the
+ * {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} column, and the E164 representation of the
+ * {@code phoneNumber} with the {@link BlockedNumbers#COLUMN_E164_NUMBER} column.
+ *
* <p> Note that if the {@link #canCurrentUserBlockNumbers} is {@code false} for the user
* context {@code context}, this method will throw an {@link UnsupportedOperationException}.
*/
+ @WorkerThread
public static boolean isBlocked(Context context, String phoneNumber) {
final Bundle res = context.getContentResolver().call(
AUTHORITY_URI, METHOD_IS_BLOCKED, phoneNumber, null);
@@ -227,6 +246,30 @@
}
/**
+ * Unblocks the {@code phoneNumber} if it is blocked.
+ *
+ * <p> Returns the number of rows deleted in the blocked number provider as a result of unblock.
+ *
+ * <p> This deletes all rows where the {@code phoneNumber} matches the
+ * {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} column or the E164 representation of the
+ * {@code phoneNumber} matches the {@link BlockedNumbers#COLUMN_E164_NUMBER} column.
+ *
+ * <p>To delete rows based on exact match with specific columns such as
+ * {@link BlockedNumbers#COLUMN_ID} use
+ * {@link android.content.ContentProvider#delete(Uri, String, String[])} with
+ * {@link BlockedNumbers#CONTENT_URI} URI.
+ *
+ * <p> Note that if the {@link #canCurrentUserBlockNumbers} is {@code false} for the user
+ * context {@code context}, this method will throw an {@link UnsupportedOperationException}.
+ */
+ @WorkerThread
+ public static int unblock(Context context, String phoneNumber) {
+ final Bundle res = context.getContentResolver().call(
+ AUTHORITY_URI, METHOD_UNBLOCK, phoneNumber, null);
+ return res.getInt(RES_NUM_ROWS_DELETED, 0);
+ }
+
+ /**
* Returns {@code true} if blocking numbers is supported for the current user.
* <p> Typically, blocking numbers is only supported for one user at a time.
*/
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index e2ae133..8ac185a 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -440,6 +440,13 @@
public static final String POST_DIAL_DIGITS = "post_dial_digits";
/**
+ * For an incoming call, the secondary line number the call was received via.
+ * When a SIM card has multiple phone numbers associated with it, the via number indicates
+ * which of the numbers associated with the SIM was called.
+ */
+ public static final String VIA_NUMBER = "via_number";
+
+ /**
* Indicates that the entry will be copied from primary user to other users.
* <P>Type: INTEGER</P>
*
@@ -485,10 +492,10 @@
public static Uri addCall(CallerInfo ci, Context context, String number,
int presentation, int callType, int features, PhoneAccountHandle accountHandle,
long start, int duration, Long dataUsage) {
- return addCall(ci, context, number, /* postDialDigits =*/ "", presentation,
- callType, features, accountHandle,
- start, duration, dataUsage, /* addForAllUsers =*/ false,
- /* userToBeInsertedTo =*/ null, /* is_read =*/ false);
+ return addCall(ci, context, number, /* postDialDigits =*/ "", /* viaNumber =*/ "",
+ presentation, callType, features, accountHandle, start, duration,
+ dataUsage, /* addForAllUsers =*/ false, /* userToBeInsertedTo =*/ null,
+ /* is_read =*/ false);
}
@@ -499,6 +506,8 @@
* if the contact is unknown.
* @param context the context used to get the ContentResolver
* @param number the phone number to be added to the calls db
+ * @param viaNumber the secondary number that the incoming call received with. If the
+ * call was received with the SIM assigned number, then this field must be ''.
* @param presentation enum value from PhoneConstants.PRESENTATION_xxx, which
* is set by the network and denotes the number presenting rules for
* "allowed", "payphone", "restricted" or "unknown"
@@ -519,12 +528,12 @@
* {@hide}
*/
public static Uri addCall(CallerInfo ci, Context context, String number,
- String postDialDigits, int presentation, int callType, int features,
- PhoneAccountHandle accountHandle, long start, int duration, Long dataUsage,
- boolean addForAllUsers, UserHandle userToBeInsertedTo) {
- return addCall(ci, context, number, postDialDigits, presentation, callType, features,
- accountHandle, start, duration, dataUsage, addForAllUsers, userToBeInsertedTo,
- /* is_read =*/ false);
+ String postDialDigits, String viaNumber, int presentation, int callType,
+ int features, PhoneAccountHandle accountHandle, long start, int duration,
+ Long dataUsage, boolean addForAllUsers, UserHandle userToBeInsertedTo) {
+ return addCall(ci, context, number, postDialDigits, viaNumber, presentation, callType,
+ features, accountHandle, start, duration, dataUsage, addForAllUsers,
+ userToBeInsertedTo, /* is_read =*/ false);
}
/**
@@ -536,6 +545,8 @@
* @param number the phone number to be added to the calls db
* @param postDialDigits the post-dial digits that were dialed after the number,
* if it was outgoing. Otherwise it is ''.
+ * @param viaNumber the secondary number that the incoming call received with. If the
+ * call was received with the SIM assigned number, then this field must be ''.
* @param presentation enum value from PhoneConstants.PRESENTATION_xxx, which
* is set by the network and denotes the number presenting rules for
* "allowed", "payphone", "restricted" or "unknown"
@@ -560,9 +571,10 @@
* {@hide}
*/
public static Uri addCall(CallerInfo ci, Context context, String number,
- String postDialDigits, int presentation, int callType, int features,
- PhoneAccountHandle accountHandle, long start, int duration, Long dataUsage,
- boolean addForAllUsers, UserHandle userToBeInsertedTo, boolean is_read) {
+ String postDialDigits, String viaNumber, int presentation, int callType,
+ int features, PhoneAccountHandle accountHandle, long start, int duration,
+ Long dataUsage, boolean addForAllUsers, UserHandle userToBeInsertedTo,
+ boolean is_read) {
if (VERBOSE_LOG) {
Log.v(LOG_TAG, String.format("Add call: number=%s, user=%s, for all=%s",
number, userToBeInsertedTo, addForAllUsers));
@@ -618,6 +630,7 @@
values.put(NUMBER, number);
values.put(POST_DIAL_DIGITS, postDialDigits);
+ values.put(VIA_NUMBER, viaNumber);
values.put(NUMBER_PRESENTATION, Integer.valueOf(numberPresentation));
values.put(TYPE, Integer.valueOf(callType));
values.put(FEATURES, features);
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 4412459..aba3972 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -112,9 +112,8 @@
* thumbnail should be rotated.
*
* @see MediaStore.Images.ImageColumns#ORIENTATION
- * @hide
*/
- public static final String EXTRA_ORIENTATION = "android.content.extra.ORIENTATION";
+ public static final String EXTRA_ORIENTATION = "android.provider.extra.ORIENTATION";
/**
* Overrides the default prompt text in DocumentsUI when set in an intent.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d8937b4..2a3c3fe 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -322,6 +322,20 @@
"android.settings.PRIVACY_SETTINGS";
/**
+ * Activity Action: Show settings to allow configuration of VPN.
+ * <p>
+ * In some cases, a matching Activity may not exist, so ensure you
+ * safeguard against this.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_VPN_SETTINGS =
+ "android.settings.VPN_SETTINGS";
+
+ /**
* Activity Action: Show settings to allow configuration of Wi-Fi.
* <p>
* In some cases, a matching Activity may not exist, so ensure you
@@ -1555,9 +1569,8 @@
* @return true if the calling app can draw on top of other apps, false otherwise.
*/
public static boolean canDrawOverlays(Context context) {
- int uid = Binder.getCallingUid();
- return Settings.isCallingPackageAllowedToDrawOverlays(context, uid, Settings
- .getPackageNameForUid(context, uid), false);
+ return Settings.isCallingPackageAllowedToDrawOverlays(context, Process.myUid(),
+ context.getOpPackageName(), false);
}
/**
@@ -3871,9 +3884,8 @@
* @return true if the calling app can write to system settings, false otherwise
*/
public static boolean canWrite(Context context) {
- int uid = Binder.getCallingUid();
- return isCallingPackageAllowedToWriteSettings(context, uid, getPackageNameForUid(
- context, uid), false);
+ return isCallingPackageAllowedToWriteSettings(context, Process.myUid(),
+ context.getOpPackageName(), false);
}
}
@@ -3977,6 +3989,7 @@
MOVED_TO_GLOBAL.add(Settings.Global.WIFI_SAVED_STATE);
MOVED_TO_GLOBAL.add(Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS);
MOVED_TO_GLOBAL.add(Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED);
+ MOVED_TO_GLOBAL.add(Settings.Global.WIFI_VERBOSE_LOGGING_ENABLED);
MOVED_TO_GLOBAL.add(Settings.Global.WIFI_ENHANCED_AUTO_JOIN);
MOVED_TO_GLOBAL.add(Settings.Global.WIFI_NETWORK_SHOW_RSSI);
MOVED_TO_GLOBAL.add(Settings.Global.WIFI_WATCHDOG_ON);
@@ -6075,7 +6088,6 @@
MOUNT_UMS_AUTOSTART,
MOUNT_UMS_PROMPT,
MOUNT_UMS_NOTIFY_ENABLED,
- UI_NIGHT_MODE,
SLEEP_TIMEOUT,
DOUBLE_TAP_TO_WAKE,
WAKE_GESTURE_ENABLED,
@@ -7245,6 +7257,14 @@
"wifi_suspend_optimizations_enabled";
/**
+ * Setting to enable verbose logging in Wi-Fi; disabled by default, and setting to 1
+ * will enable it. In the future, additional values may be supported.
+ * @hide
+ */
+ public static final String WIFI_VERBOSE_LOGGING_ENABLED =
+ "wifi_verbose_logging_enabled";
+
+ /**
* The maximum number of times we will retry a connection to an access
* point for which we have failed in acquiring an IP address from DHCP.
* A value of N means that we will make N+1 connection attempts in all.
@@ -7666,6 +7686,9 @@
BLUETOOTH_MAP_PRIORITY_PREFIX = "bluetooth_map_priority_";
/** {@hide} */
public static final String
+ BLUETOOTH_PBAP_CLIENT_PRIORITY_PREFIX = "bluetooth_pbap_client_priority_";
+ /** {@hide} */
+ public static final String
BLUETOOTH_SAP_PRIORITY_PREFIX = "bluetooth_sap_priority_";
/**
@@ -7699,6 +7722,16 @@
public static final String DEVICE_IDLE_CONSTANTS = "device_idle_constants";
/**
+ * Device Idle (Doze) specific settings for watches. See {@code #DEVICE_IDLE_CONSTANTS}
+ *
+ * <p>
+ * Type: string
+ * @hide
+ * @see com.android.server.DeviceIdleController.Constants
+ */
+ public static final String DEVICE_IDLE_CONSTANTS_WATCH = "device_idle_constants_watch";
+
+ /**
* App standby (app idle) specific settings.
* This is encoded as a key=value list, separated by commas. Ex:
*
@@ -7811,6 +7844,14 @@
}
/**
+ * Get the key that retrieves a bluetooth pbap client priority.
+ * @hide
+ */
+ public static final String getBluetoothPbapClientPriorityKey(String address) {
+ return BLUETOOTH_PBAP_CLIENT_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT);
+ }
+
+ /**
* Get the key that retrieves a bluetooth map priority.
* @hide
*/
@@ -8265,6 +8306,16 @@
public static final String BOOT_COUNT = "boot_count";
/**
+ * Whether the safe boot is disallowed.
+ *
+ * <p>This setting should have the identical value as the corresponding user restriction.
+ * The purpose of the setting is to make the restriction available in early boot stages
+ * before the user restrictions are loaded.
+ * @hide
+ */
+ public static final String SAFE_BOOT_DISALLOWED = "safe_boot_disallowed";
+
+ /**
* Settings to backup. This is here so that it's in the same place as the settings
* keys and easy to update.
*
diff --git a/core/java/android/security/NetworkSecurityPolicy.java b/core/java/android/security/NetworkSecurityPolicy.java
index 733a092..9530aca 100644
--- a/core/java/android/security/NetworkSecurityPolicy.java
+++ b/core/java/android/security/NetworkSecurityPolicy.java
@@ -16,6 +16,7 @@
package android.security;
+import android.annotation.TestApi;
import android.content.Context;
import android.content.pm.PackageManager;
import android.security.net.config.ApplicationConfig;
@@ -104,4 +105,13 @@
ManifestConfigSource source = new ManifestConfigSource(appContext);
return new ApplicationConfig(source);
}
+
+ /**
+ * Handle an update to the system or user certificate stores.
+ * @hide
+ */
+ @TestApi
+ public void handleTrustStorageUpdate() {
+ ApplicationConfig.getDefaultInstance().handleTrustStorageUpdate();
+ }
}
diff --git a/core/java/android/security/net/config/ApplicationConfig.java b/core/java/android/security/net/config/ApplicationConfig.java
index 4de36cd..fadea56 100644
--- a/core/java/android/security/net/config/ApplicationConfig.java
+++ b/core/java/android/security/net/config/ApplicationConfig.java
@@ -17,6 +17,7 @@
package android.security.net.config;
import android.util.Pair;
+import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import javax.net.ssl.X509TrustManager;
@@ -146,6 +147,20 @@
return getConfigForHostname(hostname).isCleartextTrafficPermitted();
}
+ public void handleTrustStorageUpdate() {
+ ensureInitialized();
+ mDefaultConfig.handleTrustStorageUpdate();
+ if (mConfigs != null) {
+ Set<NetworkSecurityConfig> updatedConfigs =
+ new HashSet<NetworkSecurityConfig>(mConfigs.size());
+ for (Pair<Domain, NetworkSecurityConfig> entry : mConfigs) {
+ if (updatedConfigs.add(entry.second)) {
+ entry.second.handleTrustStorageUpdate();
+ }
+ }
+ }
+ }
+
private void ensureInitialized() {
synchronized(mLock) {
if (mInitialized) {
diff --git a/core/java/android/security/net/config/CertificateSource.java b/core/java/android/security/net/config/CertificateSource.java
index f3272e4..4bcc405 100644
--- a/core/java/android/security/net/config/CertificateSource.java
+++ b/core/java/android/security/net/config/CertificateSource.java
@@ -25,4 +25,5 @@
X509Certificate findBySubjectAndPublicKey(X509Certificate cert);
X509Certificate findByIssuerAndSignature(X509Certificate cert);
Set<X509Certificate> findAllByIssuerAndSignature(X509Certificate cert);
+ void handleTrustStorageUpdate();
}
diff --git a/core/java/android/security/net/config/CertificatesEntryRef.java b/core/java/android/security/net/config/CertificatesEntryRef.java
index 742d430..45cd0f0 100644
--- a/core/java/android/security/net/config/CertificatesEntryRef.java
+++ b/core/java/android/security/net/config/CertificatesEntryRef.java
@@ -64,4 +64,8 @@
public Set<X509Certificate> findAllCertificatesByIssuerAndSignature(X509Certificate cert) {
return mSource.findAllByIssuerAndSignature(cert);
}
+
+ public void handleTrustStorageUpdate() {
+ mSource.handleTrustStorageUpdate();
+ }
}
diff --git a/core/java/android/security/net/config/DirectoryCertificateSource.java b/core/java/android/security/net/config/DirectoryCertificateSource.java
index b2c068c..e3c9d65 100644
--- a/core/java/android/security/net/config/DirectoryCertificateSource.java
+++ b/core/java/android/security/net/config/DirectoryCertificateSource.java
@@ -126,6 +126,13 @@
});
}
+ @Override
+ public void handleTrustStorageUpdate() {
+ synchronized (mLock) {
+ mCertificates = null;
+ }
+ }
+
private static interface CertSelector {
boolean match(X509Certificate cert);
}
diff --git a/core/java/android/security/net/config/KeyStoreCertificateSource.java b/core/java/android/security/net/config/KeyStoreCertificateSource.java
index ba5dd83..c68f385 100644
--- a/core/java/android/security/net/config/KeyStoreCertificateSource.java
+++ b/core/java/android/security/net/config/KeyStoreCertificateSource.java
@@ -105,4 +105,9 @@
}
return certs;
}
+
+ @Override
+ public void handleTrustStorageUpdate() {
+ // Nothing to do.
+ }
}
diff --git a/core/java/android/security/net/config/NetworkSecurityConfig.java b/core/java/android/security/net/config/NetworkSecurityConfig.java
index 6d6a92a..b3a37d0 100644
--- a/core/java/android/security/net/config/NetworkSecurityConfig.java
+++ b/core/java/android/security/net/config/NetworkSecurityConfig.java
@@ -117,12 +117,6 @@
}
}
- void onTrustStoreChange() {
- synchronized (mAnchorsLock) {
- mAnchors = null;
- }
- }
-
/** @hide */
public TrustAnchor findTrustAnchorBySubjectAndPublicKey(X509Certificate cert) {
for (CertificatesEntryRef ref : mCertificatesEntryRefs) {
@@ -154,6 +148,16 @@
return certs;
}
+ public void handleTrustStorageUpdate() {
+ synchronized (mAnchorsLock) {
+ mAnchors = null;
+ for (CertificatesEntryRef ref : mCertificatesEntryRefs) {
+ ref.handleTrustStorageUpdate();
+ }
+ }
+ getTrustManager().handleTrustStorageUpdate();
+ }
+
/**
* Return a {@link Builder} for the default {@code NetworkSecurityConfig}.
*
diff --git a/core/java/android/security/net/config/NetworkSecurityTrustManager.java b/core/java/android/security/net/config/NetworkSecurityTrustManager.java
index 81cad79..3c292ca 100644
--- a/core/java/android/security/net/config/NetworkSecurityTrustManager.java
+++ b/core/java/android/security/net/config/NetworkSecurityTrustManager.java
@@ -20,6 +20,7 @@
import android.util.ArrayMap;
import java.io.IOException;
+import java.net.Socket;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.GeneralSecurityException;
@@ -29,14 +30,15 @@
import java.util.Map;
import java.util.Set;
-import javax.net.ssl.X509TrustManager;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.X509ExtendedTrustManager;
/**
- * {@link X509TrustManager} that implements the trust anchor and pinning for a
+ * {@link X509ExtendedTrustManager} that implements the trust anchor and pinning for a
* given {@link NetworkSecurityConfig}.
* @hide
*/
-public class NetworkSecurityTrustManager implements X509TrustManager {
+public class NetworkSecurityTrustManager extends X509ExtendedTrustManager {
// TODO: Replace this with a general X509TrustManager and use duck-typing.
private final TrustManagerImpl mDelegate;
private final NetworkSecurityConfig mNetworkSecurityConfig;
@@ -68,9 +70,37 @@
}
@Override
+ public void checkClientTrusted(X509Certificate[] certs, String authType, Socket socket)
+ throws CertificateException {
+ mDelegate.checkClientTrusted(certs, authType, socket);
+ }
+
+ @Override
+ public void checkClientTrusted(X509Certificate[] certs, String authType, SSLEngine engine)
+ throws CertificateException {
+ mDelegate.checkClientTrusted(certs, authType, engine);
+ }
+
+ @Override
public void checkServerTrusted(X509Certificate[] certs, String authType)
throws CertificateException {
- checkServerTrusted(certs, authType, null);
+ checkServerTrusted(certs, authType, (String) null);
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] certs, String authType, Socket socket)
+ throws CertificateException {
+ List<X509Certificate> trustedChain =
+ mDelegate.getTrustedChainForServer(certs, authType, socket);
+ checkPins(trustedChain);
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] certs, String authType, SSLEngine engine)
+ throws CertificateException {
+ List<X509Certificate> trustedChain =
+ mDelegate.getTrustedChainForServer(certs, authType, engine);
+ checkPins(trustedChain);
}
/**
@@ -157,4 +187,11 @@
return mIssuers.clone();
}
}
+
+ public void handleTrustStorageUpdate() {
+ synchronized (mIssuersLock) {
+ mIssuers = null;
+ mDelegate.handleTrustStorageUpdate();
+ }
+ }
}
diff --git a/core/java/android/security/net/config/ResourceCertificateSource.java b/core/java/android/security/net/config/ResourceCertificateSource.java
index 22fbee2..78669c5 100644
--- a/core/java/android/security/net/config/ResourceCertificateSource.java
+++ b/core/java/android/security/net/config/ResourceCertificateSource.java
@@ -115,4 +115,9 @@
}
return certs;
}
+
+ @Override
+ public void handleTrustStorageUpdate() {
+ // Nothing to do, resource sources never change.
+ }
}
diff --git a/core/java/android/security/net/config/RootTrustManager.java b/core/java/android/security/net/config/RootTrustManager.java
index b4e58e6..19f6887 100644
--- a/core/java/android/security/net/config/RootTrustManager.java
+++ b/core/java/android/security/net/config/RootTrustManager.java
@@ -16,24 +16,28 @@
package android.security.net.config;
+import java.net.Socket;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.List;
-import javax.net.ssl.X509TrustManager;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.X509ExtendedTrustManager;
/**
- * {@link X509TrustManager} based on an {@link ApplicationConfig}.
+ * {@link X509ExtendedTrustManager} based on an {@link ApplicationConfig}.
*
- * <p>This {@code X509TrustManager} delegates to the specific trust manager for the hostname
- * being used for the connection (See {@link ApplicationConfig#getConfigForHostname(String)} and
+ * <p>This trust manager delegates to the specific trust manager for the hostname being used for
+ * the connection (See {@link ApplicationConfig#getConfigForHostname(String)} and
* {@link NetworkSecurityTrustManager}).</p>
*
* Note that if the {@code ApplicationConfig} has per-domain configurations the hostname aware
* {@link #checkServerTrusted(X509Certificate[], String String)} must be used instead of the normal
* non-aware call.
* @hide */
-public class RootTrustManager implements X509TrustManager {
+public class RootTrustManager extends X509ExtendedTrustManager {
private final ApplicationConfig mConfig;
public RootTrustManager(ApplicationConfig config) {
@@ -53,6 +57,54 @@
}
@Override
+ public void checkClientTrusted(X509Certificate[] certs, String authType, Socket socket)
+ throws CertificateException {
+ // Use the default configuration for all client authentication. Domain specific configs are
+ // only for use in checking server trust not client trust.
+ NetworkSecurityConfig config = mConfig.getConfigForHostname("");
+ config.getTrustManager().checkClientTrusted(certs, authType, socket);
+ }
+
+ @Override
+ public void checkClientTrusted(X509Certificate[] certs, String authType, SSLEngine engine)
+ throws CertificateException {
+ // Use the default configuration for all client authentication. Domain specific configs are
+ // only for use in checking server trust not client trust.
+ NetworkSecurityConfig config = mConfig.getConfigForHostname("");
+ config.getTrustManager().checkClientTrusted(certs, authType, engine);
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] certs, String authType, Socket socket)
+ throws CertificateException {
+ if (socket instanceof SSLSocket) {
+ SSLSocket sslSocket = (SSLSocket) socket;
+ SSLSession session = sslSocket.getHandshakeSession();
+ if (session == null) {
+ throw new CertificateException("Not in handshake; no session available");
+ }
+ String host = session.getPeerHost();
+ NetworkSecurityConfig config = mConfig.getConfigForHostname(host);
+ config.getTrustManager().checkServerTrusted(certs, authType, socket);
+ } else {
+ // Not an SSLSocket, use the hostname unaware checkServerTrusted.
+ checkServerTrusted(certs, authType);
+ }
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] certs, String authType, SSLEngine engine)
+ throws CertificateException {
+ SSLSession session = engine.getHandshakeSession();
+ if (session == null) {
+ throw new CertificateException("Not in handshake; no session available");
+ }
+ String host = session.getPeerHost();
+ NetworkSecurityConfig config = mConfig.getConfigForHostname(host);
+ config.getTrustManager().checkServerTrusted(certs, authType, engine);
+ }
+
+ @Override
public void checkServerTrusted(X509Certificate[] certs, String authType)
throws CertificateException {
if (mConfig.hasPerDomainConfigs()) {
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 7af0b05..0557d13 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -35,7 +35,6 @@
import android.view.ActionMode;
import android.view.Display;
import android.view.KeyEvent;
-import android.view.KeyboardShortcutGroup;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
diff --git a/core/java/android/service/vr/IVrManager.aidl b/core/java/android/service/vr/IVrManager.aidl
new file mode 100644
index 0000000..62ecab3
--- /dev/null
+++ b/core/java/android/service/vr/IVrManager.aidl
@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.vr;
+
+import android.service.vr.IVrStateCallbacks;
+
+/** @hide */
+interface IVrManager {
+
+ /**
+ * Add a callback to be notified when VR mode state changes.
+ *
+ * @param cb the callback instance to add.
+ */
+ void registerListener(in IVrStateCallbacks cb);
+
+ /**
+ * Remove the callack from the current set of registered callbacks.
+ *
+ * @param cb the callback to remove.
+ */
+ void unregisterListener(in IVrStateCallbacks cb);
+
+ /**
+ * Return current VR mode state.
+ *
+ * @return {@code true} if VR mode is enabled.
+ */
+ boolean getVrModeState();
+
+}
+
diff --git a/core/java/android/net/IConnectivityMetricsLoggerSubscriber.aidl b/core/java/android/service/vr/IVrStateCallbacks.aidl
similarity index 62%
copy from core/java/android/net/IConnectivityMetricsLoggerSubscriber.aidl
copy to core/java/android/service/vr/IVrStateCallbacks.aidl
index a2c62cd..c4fdcd0 100644
--- a/core/java/android/net/IConnectivityMetricsLoggerSubscriber.aidl
+++ b/core/java/android/service/vr/IVrStateCallbacks.aidl
@@ -1,11 +1,11 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
+/**
+ * Copyright (c) 2016, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,12 +14,11 @@
* limitations under the License.
*/
-package android.net;
+package android.service.vr;
-import android.net.ConnectivityMetricsEvent;
+/** @hide */
+oneway interface IVrStateCallbacks {
-/** {@hide} */
-oneway interface IConnectivityMetricsLoggerSubscriber {
+ void onVrStateChanged(in boolean enabled);
- void onEvents(in ConnectivityMetricsEvent[] events);
}
diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java
index a1bc2d1..7f0021d 100644
--- a/core/java/android/text/BoringLayout.java
+++ b/core/java/android/text/BoringLayout.java
@@ -183,8 +183,10 @@
if (includepad) {
spacing = metrics.bottom - metrics.top;
+ mDesc = metrics.bottom;
} else {
spacing = metrics.descent - metrics.ascent;
+ mDesc = metrics.descent;
}
mBottom = spacing;
@@ -208,8 +210,6 @@
mTopPadding = metrics.top - metrics.ascent;
mBottomPadding = metrics.bottom - metrics.descent;
}
-
- mDesc = spacing + mBottomPadding + (includepad ? metrics.top : metrics.ascent);
}
/**
@@ -300,6 +300,8 @@
Metrics fm = metrics;
if (fm == null) {
fm = new Metrics();
+ } else {
+ fm.reset();
}
TextLine line = TextLine.obtain();
@@ -414,8 +416,6 @@
mEllipsizedCount = end - start;
}
- private static final char FIRST_RIGHT_TO_LEFT = '\u0590';
-
private String mDirect;
private Paint mPaint;
@@ -430,5 +430,14 @@
@Override public String toString() {
return super.toString() + " width=" + width;
}
+
+ private void reset() {
+ top = 0;
+ bottom = 0;
+ ascent = 0;
+ descent = 0;
+ width = 0;
+ leading = 0;
+ }
}
}
diff --git a/core/java/android/text/Hyphenator.java b/core/java/android/text/Hyphenator.java
index 085613f..356804e 100644
--- a/core/java/android/text/Hyphenator.java
+++ b/core/java/android/text/Hyphenator.java
@@ -152,6 +152,9 @@
{"en-UM", "en-US"}, // English (United States Minor Outlying Islands)
{"en-VI", "en-US"}, // English (Virgin Islands)
+ // All English locales other than those falling back to en-US are mapped to en-GB.
+ {"en", "en-GB"},
+
// For German, we're assuming the 1996 (and later) orthography by default.
{"de", "de-1996"},
// Liechtenstein uses the Swiss hyphenation rules for the 1901 orthography.
@@ -160,6 +163,9 @@
// Norwegian is very probably Norwegian Bokmål.
{"no", "nb"},
+ // Use mn-Cyrl. According to CLDR's likelySubtags.xml, mn is most likely to be mn-Cyrl.
+ {"mn", "mn-Cyrl"}, // Mongolian
+
// Fall back to Ethiopic script for languages likely to be written in Ethiopic.
// Data is from CLDR's likelySubtags.xml.
// TODO: Convert this to a mechanism using ICU4J's ULocale#addLikelySubtags().
@@ -182,15 +188,36 @@
// TODO: replace this with a discovery-based method that looks into /system/usr/hyphen-data
String[] availableLanguages = {
+ "as",
+ "bn",
+ "cy",
+ "da",
"de-1901", "de-1996", "de-CH-1901",
- "en-US",
+ "en-GB", "en-US",
"es",
+ "et",
+ "eu",
+ "fr",
+ "ga",
+ "gu",
+ "hi",
+ "hr",
"hu",
"hy",
+ "kn",
+ "ml",
+ "mn-Cyrl",
+ "mr",
"nb",
"nn",
+ "or",
+ "pa",
"pt",
- "und-Ethi"
+ "sl",
+ "ta",
+ "te",
+ "tk",
+ "und-Ethi",
};
for (int i = 0; i < availableLanguages.length; i++) {
String languageTag = availableLanguages[i];
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 692d848..0bd5071 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -799,6 +799,31 @@
return false;
}
+ /**
+ * Returns the range of the run that the character at offset belongs to.
+ * @param offset the offset
+ * @return The range of the run
+ * @hide
+ */
+ public long getRunRange(int offset) {
+ int line = getLineForOffset(offset);
+ Directions dirs = getLineDirections(line);
+ if (dirs == DIRS_ALL_LEFT_TO_RIGHT || dirs == DIRS_ALL_RIGHT_TO_LEFT) {
+ return TextUtils.packRangeInLong(0, getLineEnd(line));
+ }
+ int[] runs = dirs.mDirections;
+ int lineStart = getLineStart(line);
+ for (int i = 0; i < runs.length; i += 2) {
+ int start = lineStart + runs[i];
+ int limit = start + (runs[i+1] & RUN_LENGTH_MASK);
+ if (offset >= start && offset < limit) {
+ return TextUtils.packRangeInLong(start, limit);
+ }
+ }
+ // Should happen only if the offset is "out of bounds"
+ return TextUtils.packRangeInLong(0, getLineEnd(line));
+ }
+
private boolean primaryIsTrailingPrevious(int offset) {
int line = getLineForOffset(offset);
int lineStart = getLineStart(line);
@@ -886,6 +911,10 @@
return getHorizontal(offset, !trailing, clamped);
}
+ private float getHorizontal(int offset, boolean primary) {
+ return primary ? getPrimaryHorizontal(offset) : getSecondaryHorizontal(offset);
+ }
+
private float getHorizontal(int offset, boolean trailing, boolean clamped) {
int line = getLineForOffset(offset);
@@ -1114,6 +1143,20 @@
* closest to the specified horizontal position.
*/
public int getOffsetForHorizontal(int line, float horiz) {
+ return getOffsetForHorizontal(line, horiz, true);
+ }
+
+ /**
+ * Get the character offset on the specified line whose position is
+ * closest to the specified horizontal position.
+ *
+ * @param line the line used to find the closest offset
+ * @param horiz the horizontal position used to find the closest offset
+ * @param primary whether to use the primary position or secondary position to find the offset
+ *
+ * @hide
+ */
+ public int getOffsetForHorizontal(int line, float horiz, boolean primary) {
// TODO: use Paint.getOffsetForAdvance to avoid binary search
final int lineEndOffset = getLineEnd(line);
final int lineStartOffset = getLineStart(line);
@@ -1133,7 +1176,7 @@
!isRtlCharAt(lineEndOffset - 1)) + lineStartOffset;
}
int best = lineStartOffset;
- float bestdist = Math.abs(getPrimaryHorizontal(best) - horiz);
+ float bestdist = Math.abs(getHorizontal(best, primary) - horiz);
for (int i = 0; i < dirs.mDirections.length; i += 2) {
int here = lineStartOffset + dirs.mDirections[i];
@@ -1149,7 +1192,7 @@
guess = (high + low) / 2;
int adguess = getOffsetAtStartOf(guess);
- if (getPrimaryHorizontal(adguess) * swap >= horiz * swap)
+ if (getHorizontal(adguess, primary) * swap >= horiz * swap)
high = guess;
else
low = guess;
@@ -1162,9 +1205,9 @@
int aft = tl.getOffsetToLeftRightOf(low - lineStartOffset, isRtl) + lineStartOffset;
low = tl.getOffsetToLeftRightOf(aft - lineStartOffset, !isRtl) + lineStartOffset;
if (low >= here && low < there) {
- float dist = Math.abs(getPrimaryHorizontal(low) - horiz);
+ float dist = Math.abs(getHorizontal(low, primary) - horiz);
if (aft < there) {
- float other = Math.abs(getPrimaryHorizontal(aft) - horiz);
+ float other = Math.abs(getHorizontal(aft, primary) - horiz);
if (other < dist) {
dist = other;
@@ -1179,7 +1222,7 @@
}
}
- float dist = Math.abs(getPrimaryHorizontal(here) - horiz);
+ float dist = Math.abs(getHorizontal(here, primary) - horiz);
if (dist < bestdist) {
bestdist = dist;
@@ -1187,7 +1230,7 @@
}
}
- float dist = Math.abs(getPrimaryHorizontal(max) - horiz);
+ float dist = Math.abs(getHorizontal(max, primary) - horiz);
if (dist <= bestdist) {
bestdist = dist;
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 6a33579..94ce57a 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -1132,22 +1132,12 @@
@Override
public int getLineTop(int line) {
- int top = mLines[mColumns * line + TOP];
- if (mMaximumVisibleLineCount > 0 && line >= mMaximumVisibleLineCount &&
- line != mLineCount) {
- top += getBottomPadding();
- }
- return top;
+ return mLines[mColumns * line + TOP];
}
@Override
public int getLineDescent(int line) {
- int descent = mLines[mColumns * line + DESCENT];
- if (mMaximumVisibleLineCount > 0 && line >= mMaximumVisibleLineCount - 1 && // -1 intended
- line != mLineCount) {
- descent += getBottomPadding();
- }
- return descent;
+ return mLines[mColumns * line + DESCENT];
}
@Override
diff --git a/core/java/android/transition/Visibility.java b/core/java/android/transition/Visibility.java
index 4eaab37..6579212 100644
--- a/core/java/android/transition/Visibility.java
+++ b/core/java/android/transition/Visibility.java
@@ -84,6 +84,7 @@
}
private int mMode = MODE_IN | MODE_OUT;
+ private boolean mSuppressLayout = true;
public Visibility() {}
@@ -98,6 +99,15 @@
}
/**
+ * This tells the Visibility transition to suppress layout during the transition and release
+ * the suppression after the transition.
+ * @hide
+ */
+ public void setSuppressLayout(boolean suppress) {
+ this.mSuppressLayout = suppress;
+ }
+
+ /**
* Changes the transition to support appearing and/or disappearing Views, depending
* on <code>mode</code>.
*
@@ -428,7 +438,7 @@
Animator animator = onDisappear(sceneRoot, viewToKeep, startValues, endValues);
if (animator != null) {
DisappearListener disappearListener = new DisappearListener(viewToKeep,
- finalVisibility);
+ finalVisibility, mSuppressLayout);
animator.addListener(disappearListener);
animator.addPauseListener(disappearListener);
addListener(disappearListener);
@@ -483,14 +493,16 @@
private final View mView;
private final int mFinalVisibility;
private final ViewGroup mParent;
+ private final boolean mSuppressLayout;
private boolean mLayoutSuppressed;
boolean mCanceled = false;
- public DisappearListener(View view, int finalVisibility) {
+ public DisappearListener(View view, int finalVisibility, boolean suppressLayout) {
this.mView = view;
this.mFinalVisibility = finalVisibility;
this.mParent = (ViewGroup) view.getParent();
+ this.mSuppressLayout = suppressLayout;
// Prevent a layout from including mView in its calculation.
suppressLayout(true);
}
@@ -555,7 +567,7 @@
}
private void suppressLayout(boolean suppress) {
- if (mLayoutSuppressed != suppress && mParent != null) {
+ if (mSuppressLayout && mLayoutSuppressed != suppress && mParent != null) {
mLayoutSuppressed = suppress;
mParent.suppressLayout(suppress);
}
diff --git a/core/java/android/util/Pair.java b/core/java/android/util/Pair.java
index 6027d08..f96da72 100644
--- a/core/java/android/util/Pair.java
+++ b/core/java/android/util/Pair.java
@@ -16,7 +16,7 @@
package android.util;
-import libcore.util.Objects;
+import java.util.Objects;
/**
* Container to ease passing around a tuple of two objects. This object provides a sensible
@@ -52,7 +52,7 @@
return false;
}
Pair<?, ?> p = (Pair<?, ?>) o;
- return Objects.equal(p.first, first) && Objects.equal(p.second, second);
+ return Objects.equals(p.first, first) && Objects.equals(p.second, second);
}
/**
@@ -65,6 +65,11 @@
return (first == null ? 0 : first.hashCode()) ^ (second == null ? 0 : second.hashCode());
}
+ @Override
+ public String toString() {
+ return "Pair{" + String.valueOf(first) + " " + String.valueOf(second) + "}";
+ }
+
/**
* Convenience method for creating an appropriately typed pair.
* @param a the first object in the Pair
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
index d9227ce..78d3b7b 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -606,7 +606,6 @@
private static final int SIGNATURE_ECDSA_WITH_SHA256 = 0x0201;
private static final int SIGNATURE_ECDSA_WITH_SHA512 = 0x0202;
private static final int SIGNATURE_DSA_WITH_SHA256 = 0x0301;
- private static final int SIGNATURE_DSA_WITH_SHA512 = 0x0302;
private static final int CONTENT_DIGEST_CHUNKED_SHA256 = 1;
private static final int CONTENT_DIGEST_CHUNKED_SHA512 = 2;
@@ -620,7 +619,6 @@
case SIGNATURE_ECDSA_WITH_SHA256:
case SIGNATURE_ECDSA_WITH_SHA512:
case SIGNATURE_DSA_WITH_SHA256:
- case SIGNATURE_DSA_WITH_SHA512:
return true;
default:
return false;
@@ -670,7 +668,6 @@
case SIGNATURE_RSA_PSS_WITH_SHA512:
case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512:
case SIGNATURE_ECDSA_WITH_SHA512:
- case SIGNATURE_DSA_WITH_SHA512:
return CONTENT_DIGEST_CHUNKED_SHA512;
default:
throw new IllegalArgumentException(
@@ -714,7 +711,6 @@
case SIGNATURE_ECDSA_WITH_SHA512:
return "EC";
case SIGNATURE_DSA_WITH_SHA256:
- case SIGNATURE_DSA_WITH_SHA512:
return "DSA";
default:
throw new IllegalArgumentException(
@@ -746,8 +742,6 @@
return Pair.create("SHA512withECDSA", null);
case SIGNATURE_DSA_WITH_SHA256:
return Pair.create("SHA256withDSA", null);
- case SIGNATURE_DSA_WITH_SHA512:
- return Pair.create("SHA512withDSA", null);
default:
throw new IllegalArgumentException(
"Unknown signature algorithm: 0x"
diff --git a/core/java/android/util/jar/StrictJarFile.java b/core/java/android/util/jar/StrictJarFile.java
index 302a08d..5d94b06 100644
--- a/core/java/android/util/jar/StrictJarFile.java
+++ b/core/java/android/util/jar/StrictJarFile.java
@@ -58,11 +58,22 @@
public StrictJarFile(String fileName)
throws IOException, SecurityException {
- this(fileName, true);
+ this(fileName, true, true);
}
- public StrictJarFile(String fileName, boolean verify)
- throws IOException, SecurityException {
+ /**
+ *
+ * @param verify whether to verify the file's JAR signatures and collect the corresponding
+ * signer certificates.
+ * @param signatureSchemeRollbackProtectionsEnforced {@code true} to enforce protections against
+ * stripping newer signature schemes (e.g., APK Signature Scheme v2) from the file, or
+ * {@code false} to ignore any such protections. This parameter is ignored when
+ * {@code verify} is {@code false}.
+ */
+ public StrictJarFile(String fileName,
+ boolean verify,
+ boolean signatureSchemeRollbackProtectionsEnforced)
+ throws IOException, SecurityException {
this.nativeHandle = nativeOpenJarFile(fileName);
this.raf = new RandomAccessFile(fileName, "r");
@@ -73,7 +84,12 @@
if (verify) {
HashMap<String, byte[]> metaEntries = getMetaEntries();
this.manifest = new StrictJarManifest(metaEntries.get(JarFile.MANIFEST_NAME), true);
- this.verifier = new StrictJarVerifier(fileName, manifest, metaEntries);
+ this.verifier =
+ new StrictJarVerifier(
+ fileName,
+ manifest,
+ metaEntries,
+ signatureSchemeRollbackProtectionsEnforced);
Set<String> files = manifest.getEntries().keySet();
for (String file : files) {
if (findEntry(file) == null) {
diff --git a/core/java/android/util/jar/StrictJarVerifier.java b/core/java/android/util/jar/StrictJarVerifier.java
index 0546a5f..6da50ba 100644
--- a/core/java/android/util/jar/StrictJarVerifier.java
+++ b/core/java/android/util/jar/StrictJarVerifier.java
@@ -72,6 +72,7 @@
private final StrictJarManifest manifest;
private final HashMap<String, byte[]> metaEntries;
private final int mainAttributesEnd;
+ private final boolean signatureSchemeRollbackProtectionsEnforced;
private final Hashtable<String, HashMap<String, Attributes>> signatures =
new Hashtable<String, HashMap<String, Attributes>>(5);
@@ -164,13 +165,19 @@
*
* @param name
* the name of the JAR file being verified.
+ *
+ * @param signatureSchemeRollbackProtectionsEnforced {@code true} to enforce protections against
+ * stripping newer signature schemes (e.g., APK Signature Scheme v2) from the file, or
+ * {@code false} to ignore any such protections.
*/
StrictJarVerifier(String name, StrictJarManifest manifest,
- HashMap<String, byte[]> metaEntries) {
+ HashMap<String, byte[]> metaEntries, boolean signatureSchemeRollbackProtectionsEnforced) {
jarName = name;
this.manifest = manifest;
this.metaEntries = metaEntries;
this.mainAttributesEnd = manifest.getMainAttributesEnd();
+ this.signatureSchemeRollbackProtectionsEnforced =
+ signatureSchemeRollbackProtectionsEnforced;
}
/**
@@ -357,40 +364,42 @@
return;
}
- // Check whether APK Signature Scheme v2 signature was stripped.
- String apkSignatureSchemeIdList =
- attributes.getValue(
- ApkSignatureSchemeV2Verifier.SF_ATTRIBUTE_ANDROID_APK_SIGNED_NAME);
- if (apkSignatureSchemeIdList != null) {
- // This field contains a comma-separated list of APK signature scheme IDs which were
- // used to sign this APK. If an ID is known to us, it means signatures of that scheme
- // were stripped from the APK because otherwise we wouldn't have fallen back to
- // verifying the APK using the JAR signature scheme.
- boolean v2SignatureGenerated = false;
- StringTokenizer tokenizer = new StringTokenizer(apkSignatureSchemeIdList, ",");
- while (tokenizer.hasMoreTokens()) {
- String idText = tokenizer.nextToken().trim();
- if (idText.isEmpty()) {
- continue;
+ // If requested, check whether APK Signature Scheme v2 signature was stripped.
+ if (signatureSchemeRollbackProtectionsEnforced) {
+ String apkSignatureSchemeIdList =
+ attributes.getValue(
+ ApkSignatureSchemeV2Verifier.SF_ATTRIBUTE_ANDROID_APK_SIGNED_NAME);
+ if (apkSignatureSchemeIdList != null) {
+ // This field contains a comma-separated list of APK signature scheme IDs which
+ // were used to sign this APK. If an ID is known to us, it means signatures of that
+ // scheme were stripped from the APK because otherwise we wouldn't have fallen back
+ // to verifying the APK using the JAR signature scheme.
+ boolean v2SignatureGenerated = false;
+ StringTokenizer tokenizer = new StringTokenizer(apkSignatureSchemeIdList, ",");
+ while (tokenizer.hasMoreTokens()) {
+ String idText = tokenizer.nextToken().trim();
+ if (idText.isEmpty()) {
+ continue;
+ }
+ int id;
+ try {
+ id = Integer.parseInt(idText);
+ } catch (Exception ignored) {
+ continue;
+ }
+ if (id == ApkSignatureSchemeV2Verifier.SF_ATTRIBUTE_ANDROID_APK_SIGNED_ID) {
+ // This APK was supposed to be signed with APK Signature Scheme v2 but no
+ // such signature was found.
+ v2SignatureGenerated = true;
+ break;
+ }
}
- int id;
- try {
- id = Integer.parseInt(idText);
- } catch (Exception ignored) {
- continue;
- }
- if (id == ApkSignatureSchemeV2Verifier.SF_ATTRIBUTE_ANDROID_APK_SIGNED_ID) {
- // This APK was supposed to be signed with APK Signature Scheme v2 but no such
- // signature was found.
- v2SignatureGenerated = true;
- break;
- }
- }
- if (v2SignatureGenerated) {
- throw new SecurityException(signatureFile + " indicates " + jarName + " is signed"
- + " using APK Signature Scheme v2, but no such signature was found."
- + " Signature stripped?");
+ if (v2SignatureGenerated) {
+ throw new SecurityException(signatureFile + " indicates " + jarName
+ + " is signed using APK Signature Scheme v2, but no such signature was"
+ + " found. Signature stripped?");
+ }
}
}
diff --git a/core/java/android/view/ContextThemeWrapper.java b/core/java/android/view/ContextThemeWrapper.java
index 4888877..86318e9 100644
--- a/core/java/android/view/ContextThemeWrapper.java
+++ b/core/java/android/view/ContextThemeWrapper.java
@@ -101,6 +101,15 @@
mOverrideConfiguration = new Configuration(overrideConfiguration);
}
+ /**
+ * Used by ActivityThread to apply the overridden configuration to onConfigurationChange
+ * callbacks.
+ * @hide
+ */
+ public Configuration getOverrideConfiguration() {
+ return mOverrideConfiguration;
+ }
+
@Override
public AssetManager getAssets() {
// Ensure we're returning assets with the correct configuration.
diff --git a/core/java/android/view/HardwareLayer.java b/core/java/android/view/HardwareLayer.java
index a12434c..41c44f1 100644
--- a/core/java/android/view/HardwareLayer.java
+++ b/core/java/android/view/HardwareLayer.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.Nullable;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.Paint;
@@ -51,8 +52,8 @@
* @param paint The paint used when the layer is drawn into the destination canvas.
* @see View#setLayerPaint(android.graphics.Paint)
*/
- public void setLayerPaint(Paint paint) {
- nSetLayerPaint(mFinalizer.get(), paint.getNativeInstance());
+ public void setLayerPaint(@Nullable Paint paint) {
+ nSetLayerPaint(mFinalizer.get(), paint != null ? paint.getNativeInstance() : 0);
mRenderer.pushLayerUpdate(this);
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 7af4a1f..4ba97d5 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -180,7 +180,12 @@
// caller must call setNewConfiguration() sometime later.
Configuration updateOrientationFromAppTokens(in Configuration currentConfig,
IBinder freezeThisOneIfNeeded);
- void setNewConfiguration(in Configuration config);
+ // Notify window manager of the new configuration. Returns an array of stack ids that's
+ // affected by the update, ActivityManager should resize these stacks.
+ int[] setNewConfiguration(in Configuration config);
+
+ // Retrieves the new bounds after the configuration update evaluated by window manager.
+ Rect getBoundsForNewConfiguration(int stackId);
void startFreezingScreen(int exitAnim, int enterAnim);
void stopFreezingScreen();
@@ -400,4 +405,14 @@
* @hide
*/
void registerShortcutKey(in long shortcutCode, IShortcutService keySubscriber);
+
+ /**
+ * Create the input consumer for wallpaper events.
+ */
+ void createWallpaperInputConsumer(out InputChannel inputChannel);
+
+ /**
+ * Remove the input consumer for wallpaper events.
+ */
+ void removeWallpaperInputConsumer();
}
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index a1e2e94..8e1609c 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -116,14 +116,11 @@
* @param top The new top position
* @param right The new right position
* @param bottom The new bottom position
- * @param requestedWidth The new requested width
- * @param requestedHeight The new requested height
* @param deferTransactionUntilFrame Frame number from our parent (attached) to
* defer this action until.
* @param outFrame Rect in which is placed the new position/size on screen.
*/
void repositionChild(IWindow childWindow, int left, int top, int right, int bottom,
- int requestedWidth, int requestedHeight,
long deferTransactionUntilFrame, out Rect outFrame);
/*
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index e0c6770..636384c 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -1870,6 +1870,11 @@
return keyCode == KeyEvent.KEYCODE_META_LEFT || keyCode == KeyEvent.KEYCODE_META_RIGHT;
}
+ /** @hide */
+ public static final boolean isAltKey(int keyCode) {
+ return keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT;
+ }
+
/** {@inheritDoc} */
@Override
public final int getDeviceId() {
diff --git a/core/java/android/view/KeyboardShortcutInfo.java b/core/java/android/view/KeyboardShortcutInfo.java
index c2bd347..eee925d 100644
--- a/core/java/android/view/KeyboardShortcutInfo.java
+++ b/core/java/android/view/KeyboardShortcutInfo.java
@@ -51,7 +51,7 @@
mLabel = label;
mIcon = icon;
mBaseCharacter = MIN_VALUE;
- checkArgument(keycode > KeyEvent.KEYCODE_UNKNOWN && keycode <= KeyEvent.getMaxKeyCode());
+ checkArgument(keycode >= KeyEvent.KEYCODE_UNKNOWN && keycode <= KeyEvent.getMaxKeyCode());
mKeycode = keycode;
mModifiers = modifiers;
}
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index cff9d8e..37da869 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -22,7 +22,6 @@
import android.util.AttributeSet;
import android.widget.ImageView;
import android.widget.RemoteViews;
-import android.widget.TextView;
import java.util.ArrayList;
@@ -37,7 +36,7 @@
private final int mChildMinWidth;
private final int mContentEndMargin;
private View mAppName;
- private View mSubTextView;
+ private View mHeaderText;
private OnClickListener mExpandClickListener;
private HeaderTouchListener mTouchListener = new HeaderTouchListener();
private ImageView mExpandButton;
@@ -73,11 +72,10 @@
protected void onFinishInflate() {
super.onFinishInflate();
mAppName = findViewById(com.android.internal.R.id.app_name_text);
- mSubTextView = findViewById(com.android.internal.R.id.header_sub_text);
+ mHeaderText = findViewById(com.android.internal.R.id.header_text);
mExpandButton = (ImageView) 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);
- mInfo = findViewById(com.android.internal.R.id.header_content_info);
}
@Override
@@ -105,15 +103,7 @@
}
if (totalWidth > givenWidth) {
int overFlow = totalWidth - givenWidth;
- // We are overflowing, lets shrink the info first
- final int infoWidth = mInfo.getMeasuredWidth();
- if (mInfo.getVisibility() != GONE && infoWidth > mChildMinWidth) {
- int newSize = infoWidth - Math.min(infoWidth - mChildMinWidth, overFlow);
- int childWidthSpec = MeasureSpec.makeMeasureSpec(newSize, MeasureSpec.AT_MOST);
- mInfo.measure(childWidthSpec, wrapContentHeightSpec);
- overFlow -= infoWidth - newSize;
- }
- // still overflowing, lets shrink the app name now
+ // We are overflowing, lets shrink the app name first
final int appWidth = mAppName.getMeasuredWidth();
if (overFlow > 0 && mAppName.getVisibility() != GONE && appWidth > mChildMinWidth) {
int newSize = appWidth - Math.min(appWidth - mChildMinWidth, overFlow);
@@ -121,13 +111,13 @@
mAppName.measure(childWidthSpec, wrapContentHeightSpec);
overFlow -= appWidth - newSize;
}
- // still overflowing, finaly we shrink the subtext
- if (overFlow > 0 && mSubTextView.getVisibility() != GONE) {
+ // still overflowing, finaly we shrink the header text
+ if (overFlow > 0 && mHeaderText.getVisibility() != GONE) {
// we're still too big
- final int subTextWidth = mSubTextView.getMeasuredWidth();
- int newSize = Math.max(0, subTextWidth - overFlow);
+ final int textWidth = mHeaderText.getMeasuredWidth();
+ int newSize = Math.max(0, textWidth - overFlow);
int childWidthSpec = MeasureSpec.makeMeasureSpec(newSize, MeasureSpec.AT_MOST);
- mSubTextView.measure(childWidthSpec, wrapContentHeightSpec);
+ mHeaderText.measure(childWidthSpec, wrapContentHeightSpec);
}
}
setMeasuredDimension(givenWidth, givenHeight);
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index a45c18d..ab4cbcf 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -259,7 +259,7 @@
return nSetLayerType(mNativeRenderNode, layerType);
}
- public boolean setLayerPaint(Paint paint) {
+ public boolean setLayerPaint(@Nullable Paint paint) {
return nSetLayerPaint(mNativeRenderNode, paint != null ? paint.getNativeInstance() : 0);
}
@@ -307,18 +307,22 @@
*
* Deep copies the data into native to simplify reference ownership.
*/
- public boolean setOutline(Outline outline) {
+ public boolean setOutline(@Nullable Outline outline) {
if (outline == null) {
return nSetOutlineNone(mNativeRenderNode);
- } else if (outline.isEmpty()) {
- return nSetOutlineEmpty(mNativeRenderNode);
- } else if (outline.mRect != null) {
- return nSetOutlineRoundRect(mNativeRenderNode, outline.mRect.left, outline.mRect.top,
- outline.mRect.right, outline.mRect.bottom, outline.mRadius, outline.mAlpha);
- } else if (outline.mPath != null) {
- return nSetOutlineConvexPath(mNativeRenderNode, outline.mPath.mNativePath,
- outline.mAlpha);
}
+
+ switch(outline.mMode) {
+ case Outline.MODE_EMPTY:
+ return nSetOutlineEmpty(mNativeRenderNode);
+ case Outline.MODE_ROUND_RECT:
+ return nSetOutlineRoundRect(mNativeRenderNode, outline.mRect.left, outline.mRect.top,
+ outline.mRect.right, outline.mRect.bottom, outline.mRadius, outline.mAlpha);
+ case Outline.MODE_CONVEX_PATH:
+ return nSetOutlineConvexPath(mNativeRenderNode, outline.mPath.mNativePath,
+ outline.mAlpha);
+ }
+
throw new IllegalArgumentException("Unrecognized outline?");
}
@@ -763,6 +767,16 @@
return nGetDebugSize(mNativeRenderNode);
}
+ /**
+ * Called by native when the passed displaylist is removed from the draw tree
+ */
+ void onRenderNodeDetached() {
+ discardDisplayList();
+ if (mOwningView != null) {
+ mOwningView.onRenderNodeDetached(this);
+ }
+ }
+
///////////////////////////////////////////////////////////////////////////
// Animations
///////////////////////////////////////////////////////////////////////////
@@ -795,7 +809,9 @@
// Native methods
///////////////////////////////////////////////////////////////////////////
- private static native long nCreate(String name);
+ // Intentionally not static because it acquires a reference to 'this'
+ private native long nCreate(String name);
+
private static native void nDestroyRenderNode(long renderNode);
private static native void nSetDisplayList(long renderNode, long newData);
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index 37e4000..7cd161c 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -145,13 +145,6 @@
private int mSpanSlop;
private int mMinSpan;
- // Bounds for recently seen values
- private float mTouchUpper;
- private float mTouchLower;
- private float mTouchHistoryLastAccepted;
- private int mTouchHistoryDirection;
- private long mTouchHistoryLastAcceptedTime;
- private int mTouchMinMajor;
private final Handler mHandler;
private float mAnchoredScaleStartX;
@@ -207,8 +200,6 @@
mSpanSlop = ViewConfiguration.get(context).getScaledTouchSlop() * 2;
final Resources res = context.getResources();
- mTouchMinMajor = res.getDimensionPixelSize(
- com.android.internal.R.dimen.config_minScalingTouchMajor);
mMinSpan = res.getDimensionPixelSize(com.android.internal.R.dimen.config_minScalingSpan);
mHandler = handler;
// Quick scale is enabled by default after JB_MR2
@@ -223,77 +214,6 @@
}
/**
- * The touchMajor/touchMinor elements of a MotionEvent can flutter/jitter on
- * some hardware/driver combos. Smooth it out to get kinder, gentler behavior.
- * @param ev MotionEvent to add to the ongoing history
- */
- private void addTouchHistory(MotionEvent ev) {
- final long currentTime = SystemClock.uptimeMillis();
- final int count = ev.getPointerCount();
- boolean accept = currentTime - mTouchHistoryLastAcceptedTime >= TOUCH_STABILIZE_TIME;
- float total = 0;
- int sampleCount = 0;
- for (int i = 0; i < count; i++) {
- final boolean hasLastAccepted = !Float.isNaN(mTouchHistoryLastAccepted);
- final int historySize = ev.getHistorySize();
- final int pointerSampleCount = historySize + 1;
- for (int h = 0; h < pointerSampleCount; h++) {
- float major;
- if (h < historySize) {
- major = ev.getHistoricalTouchMajor(i, h);
- } else {
- major = ev.getTouchMajor(i);
- }
- if (major < mTouchMinMajor) major = mTouchMinMajor;
- total += major;
-
- if (Float.isNaN(mTouchUpper) || major > mTouchUpper) {
- mTouchUpper = major;
- }
- if (Float.isNaN(mTouchLower) || major < mTouchLower) {
- mTouchLower = major;
- }
-
- if (hasLastAccepted) {
- final int directionSig = (int) Math.signum(major - mTouchHistoryLastAccepted);
- if (directionSig != mTouchHistoryDirection ||
- (directionSig == 0 && mTouchHistoryDirection == 0)) {
- mTouchHistoryDirection = directionSig;
- final long time = h < historySize ? ev.getHistoricalEventTime(h)
- : ev.getEventTime();
- mTouchHistoryLastAcceptedTime = time;
- accept = false;
- }
- }
- }
- sampleCount += pointerSampleCount;
- }
-
- final float avg = total / sampleCount;
-
- if (accept) {
- float newAccepted = (mTouchUpper + mTouchLower + avg) / 3;
- mTouchUpper = (mTouchUpper + newAccepted) / 2;
- mTouchLower = (mTouchLower + newAccepted) / 2;
- mTouchHistoryLastAccepted = newAccepted;
- mTouchHistoryDirection = 0;
- mTouchHistoryLastAcceptedTime = ev.getEventTime();
- }
- }
-
- /**
- * Clear all touch history tracking. Useful in ACTION_CANCEL or ACTION_UP.
- * @see #addTouchHistory(MotionEvent)
- */
- private void clearTouchHistory() {
- mTouchUpper = Float.NaN;
- mTouchLower = Float.NaN;
- mTouchHistoryLastAccepted = Float.NaN;
- mTouchHistoryDirection = 0;
- mTouchHistoryLastAcceptedTime = 0;
- }
-
- /**
* Accepts MotionEvents and dispatches events to a {@link OnScaleGestureListener}
* when appropriate.
*
@@ -344,7 +264,6 @@
}
if (streamComplete) {
- clearTouchHistory();
return true;
}
}
@@ -391,17 +310,14 @@
focusY = sumY / div;
}
- addTouchHistory(event);
-
// Determine average deviation from focal point
float devSumX = 0, devSumY = 0;
for (int i = 0; i < count; i++) {
if (skipIndex == i) continue;
// Convert the resulting diameter into a radius.
- final float touchSize = mTouchHistoryLastAccepted / 2;
- devSumX += Math.abs(event.getX(i) - focusX) + touchSize;
- devSumY += Math.abs(event.getY(i) - focusY) + touchSize;
+ devSumX += Math.abs(event.getX(i) - focusX);
+ devSumY += Math.abs(event.getY(i) - focusY);
}
final float devX = devSumX / div;
final float devY = devSumY / div;
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 477ffd9..8a8fb43 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -490,7 +490,7 @@
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
;
- if (!creating && !force && !mUpdateWindowNeeded) {
+ if (!creating && !force && !mUpdateWindowNeeded && !sizeChanged) {
mLayout.privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY;
} else {
@@ -584,18 +584,6 @@
mSurface.transferFrom(mNewSurface);
if (visible && mSurface.isValid()) {
- // We set SCALING_MODE_NO_SCALE_CROP to allow the WindowManager
- // to update our Surface crop without requiring a new buffer from
- // us. In the default mode of SCALING_MODE_FREEZE, surface geometry
- // state (which includes crop) is only applied when a buffer
- // with appropriate geometry is available. During drag resize
- // it is quite frequent that a matching buffer will not be available
- // (because we are constantly being resized and have fallen behind).
- // However in such situations the WindowManager still needs to be able
- // to update our crop to ensure we stay within the bounds of the containing
- // window.
- mSurface.setScalingMode(Surface.SCALING_MODE_NO_SCALE_CROP);
-
if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
mSurfaceCreated = true;
mIsCreating = true;
@@ -666,7 +654,6 @@
mLocation[0], mLocation[1]));
mSession.repositionChild(mWindow, mWindowSpaceLeft, mWindowSpaceTop,
mLocation[0], mLocation[1],
- mWindowSpaceWidth, mWindowSpaceHeight,
-1, mWinFrame);
} catch (RemoteException ex) {
Log.e(TAG, "Exception from relayout", ex);
@@ -703,7 +690,6 @@
}
// Just using mRTLastReportedPosition as a dummy rect here
session.repositionChild(window, left, top, right, bottom,
- mWindowSpaceWidth, mWindowSpaceHeight,
frameNumber,
mRTLastReportedPosition);
// Now overwrite mRTLastReportedPosition with our values
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 1be4810..1a712c3 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -133,7 +134,6 @@
*/
public TextureView(Context context) {
super(context);
- init();
}
/**
@@ -144,7 +144,6 @@
*/
public TextureView(Context context, AttributeSet attrs) {
super(context, attrs);
- init();
}
/**
@@ -158,7 +157,6 @@
*/
public TextureView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- init();
}
/**
@@ -176,11 +174,6 @@
*/
public TextureView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- init();
- }
-
- private void init() {
- mLayerPaint = new Paint();
}
/**
@@ -260,7 +253,7 @@
* method will however be taken into account when rendering the content of
* this TextureView.
*
- * @param layerType The ype of layer to use with this view, must be one of
+ * @param layerType The type of layer to use with this view, must be one of
* {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or
* {@link #LAYER_TYPE_HARDWARE}
* @param paint The paint used to compose the layer. This argument is optional
@@ -268,16 +261,16 @@
* {@link #LAYER_TYPE_NONE}
*/
@Override
- public void setLayerType(int layerType, Paint paint) {
- if (paint != mLayerPaint) {
- mLayerPaint = paint == null ? new Paint() : paint;
- invalidate();
- }
+ public void setLayerType(int layerType, @Nullable Paint paint) {
+ setLayerPaint(paint);
}
@Override
- public void setLayerPaint(Paint paint) {
- setLayerType(/* ignored */ 0, paint);
+ public void setLayerPaint(@Nullable Paint paint) {
+ if (paint != mLayerPaint) {
+ mLayerPaint = paint;
+ invalidate();
+ }
}
/**
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index c972476..df774b4 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -794,7 +794,8 @@
}
final long[] frameInfo = choreographer.mFrameInfo.mFrameInfo;
- int syncResult = nSyncAndDrawFrame(mNativeProxy, frameInfo, frameInfo.length);
+ int syncResult = nSyncAndDrawFrame(mNativeProxy, frameInfo, frameInfo.length,
+ mRootNode.mNativeRenderNode);
if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) {
setEnabled(false);
attachInfo.mViewRootImpl.mSurface.release();
@@ -993,7 +994,8 @@
private static native void nSetLightCenter(long nativeProxy,
float lightX, float lightY, float lightZ);
private static native void nSetOpaque(long nativeProxy, boolean opaque);
- private static native int nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size);
+ private static native int nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size,
+ long rootRenderNode);
private static native void nDestroy(long nativeProxy, long rootRenderNode);
private static native void nRegisterAnimatingRenderNode(long rootRenderNode, long animatingNode);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 6fa2aad..b95d830 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2429,7 +2429,12 @@
* 1 PFLAG3_SCROLL_INDICATOR_START
* 1 PFLAG3_SCROLL_INDICATOR_END
* 1 PFLAG3_ASSIST_BLOCKED
- * 1111111 PFLAG3_POINTER_ICON_MASK
+ * 1 PFLAG3_POINTER_ICON_NULL
+ * 1 PFLAG3_POINTER_ICON_VALUE_START
+ * 11111111 PFLAG3_POINTER_ICON_MASK
+ * 1 PFLAG3_OVERLAPPING_RENDERING_FORCED_VALUE
+ * 1 PFLAG3_HAS_OVERLAPPING_RENDERING_FORCED
+ * 1 PFLAG3_TEMPORARY_DETACH
* |-------|-------|-------|-------|
*/
@@ -2518,8 +2523,6 @@
*/
static final int PFLAG3_SCROLL_INDICATOR_END = 0x2000;
- /* End of masks for mPrivateFlags3 */
-
static final int DRAG_MASK = PFLAG2_DRAG_CAN_ACCEPT | PFLAG2_DRAG_HOVERED;
static final int SCROLL_INDICATORS_NONE = 0x0000;
@@ -2651,6 +2654,31 @@
private static final int PFLAG3_POINTER_ICON_VALUE_START = 2 << PFLAG3_POINTER_ICON_LSHIFT;
/**
+ * Whether this view has rendered elements that overlap (see {@link
+ * #hasOverlappingRendering()}, {@link #forceHasOverlappingRendering(boolean)}, and
+ * {@link #getHasOverlappingRendering()} ). The value in this bit is only valid when
+ * PFLAG3_HAS_OVERLAPPING_RENDERING_FORCED has been set. Otherwise, the value is
+ * determined by whatever {@link #hasOverlappingRendering()} returns.
+ */
+ private static final int PFLAG3_OVERLAPPING_RENDERING_FORCED_VALUE = 0x800000;
+
+ /**
+ * Whether {@link #forceHasOverlappingRendering(boolean)} has been called. When true, value
+ * in PFLAG3_OVERLAPPING_RENDERING_FORCED_VALUE is valid.
+ */
+ private static final int PFLAG3_HAS_OVERLAPPING_RENDERING_FORCED = 0x1000000;
+
+ /**
+ * Flag indicating that the view is temporarily detached from the parent view.
+ *
+ * @see #onStartTemporaryDetach()
+ * @see #onFinishTemporaryDetach()
+ */
+ static final int PFLAG3_TEMPORARY_DETACH = 0x2000000;
+
+ /* End of masks for mPrivateFlags3 */
+
+ /**
* Always allow a user to over-scroll this view, provided it is a
* view that can scroll.
*
@@ -3871,6 +3899,7 @@
* cleanup.
*/
final RenderNode mRenderNode;
+ private Runnable mRenderNodeDetachedCallback;
/**
* Set to true when the view is sending hover accessibility events because it
@@ -4516,6 +4545,12 @@
}
}
break;
+ case R.styleable.View_forceHasOverlappingRendering:
+ if (a.peekValue(attr) != null) {
+ forceHasOverlappingRendering(a.getBoolean(attr, true));
+ }
+ break;
+
}
}
@@ -9710,9 +9745,20 @@
}
/**
- * @hide
+ * @return {@code true} when the View is in the state between {@link #onStartTemporaryDetach()}
+ * and {@link #onFinishTemporaryDetach()}.
*/
+ public final boolean isTemporarilyDetached() {
+ return (mPrivateFlags3 & PFLAG3_TEMPORARY_DETACH) != 0;
+ }
+
+ /**
+ * Dispatch {@link #onStartTemporaryDetach()} to this View and its direct children if this is
+ * a container View.
+ */
+ @CallSuper
public void dispatchStartTemporaryDetach() {
+ mPrivateFlags3 |= PFLAG3_TEMPORARY_DETACH;
onStartTemporaryDetach();
}
@@ -9728,10 +9774,13 @@
}
/**
- * @hide
+ * Dispatch {@link #onFinishTemporaryDetach()} to this View and its direct children if this is
+ * a container View.
*/
+ @CallSuper
public void dispatchFinishTemporaryDetach() {
onFinishTemporaryDetach();
+ mPrivateFlags3 &= ~PFLAG3_TEMPORARY_DETACH;
}
/**
@@ -11741,7 +11790,7 @@
}
}
- /**
+ /**
* Utility method to retrieve the inverse of the current mMatrix property.
* We cache the matrix to avoid recalculating it when transform properties
* have not changed.
@@ -12116,6 +12165,42 @@
}
/**
+ * Sets the behavior for overlapping rendering for this view (see {@link
+ * #hasOverlappingRendering()} for more details on this behavior). Calling this method
+ * is an alternative to overriding {@link #hasOverlappingRendering()} in a subclass,
+ * providing the value which is then used internally. That is, when {@link
+ * #forceHasOverlappingRendering(boolean)} is called, the value of {@link
+ * #hasOverlappingRendering()} is ignored and the value passed into this method is used
+ * instead.
+ *
+ * @param hasOverlappingRendering The value for overlapping rendering to be used internally
+ * instead of that returned by {@link #hasOverlappingRendering()}.
+ *
+ * @attr ref android.R.styleable#View_forceHasOverlappingRendering
+ */
+ public void forceHasOverlappingRendering(boolean hasOverlappingRendering) {
+ mPrivateFlags3 |= PFLAG3_HAS_OVERLAPPING_RENDERING_FORCED;
+ if (hasOverlappingRendering) {
+ mPrivateFlags3 |= PFLAG3_OVERLAPPING_RENDERING_FORCED_VALUE;
+ } else {
+ mPrivateFlags3 &= ~PFLAG3_OVERLAPPING_RENDERING_FORCED_VALUE;
+ }
+ }
+
+ /**
+ * Returns the value for overlapping rendering that is used internally. This is either
+ * the value passed into {@link #forceHasOverlappingRendering(boolean)}, if called, or
+ * the return value of {@link #hasOverlappingRendering()}, otherwise.
+ *
+ * @return The value for overlapping rendering being used internally.
+ */
+ public final boolean getHasOverlappingRendering() {
+ return (mPrivateFlags3 & PFLAG3_HAS_OVERLAPPING_RENDERING_FORCED) != 0 ?
+ (mPrivateFlags3 & PFLAG3_OVERLAPPING_RENDERING_FORCED_VALUE) != 0 :
+ hasOverlappingRendering();
+ }
+
+ /**
* Returns whether this View has content which overlaps.
*
* <p>This function, intended to be overridden by specific View types, is an optimization when
@@ -12131,6 +12216,9 @@
* necessitates that a View return true if it uses the methods internally without passing the
* {@link Canvas#CLIP_TO_LAYER_SAVE_FLAG}.</p>
*
+ * <p><strong>Note:</strong> The return value of this method is ignored if {@link
+ * #forceHasOverlappingRendering(boolean)} has been called on this view.</p>
+ *
* @return true if the content in this view might overlap, false otherwise.
*/
@ViewDebug.ExportedProperty(category = "drawing")
@@ -15123,6 +15211,7 @@
protected void onDetachedFromWindowInternal() {
mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT;
mPrivateFlags3 &= ~PFLAG3_IS_LAID_OUT;
+ mPrivateFlags3 &= ~PFLAG3_TEMPORARY_DETACH;
removeUnsetPressCallback();
removeLongPressCallback();
@@ -15626,7 +15715,7 @@
*
* @attr ref android.R.styleable#View_layerType
*/
- public void setLayerType(int layerType, Paint paint) {
+ public void setLayerType(int layerType, @Nullable Paint paint) {
if (layerType < LAYER_TYPE_NONE || layerType > LAYER_TYPE_HARDWARE) {
throw new IllegalArgumentException("Layer type can only be one of: LAYER_TYPE_NONE, "
+ "LAYER_TYPE_SOFTWARE or LAYER_TYPE_HARDWARE");
@@ -15645,8 +15734,7 @@
}
mLayerType = layerType;
- final boolean layerDisabled = (mLayerType == LAYER_TYPE_NONE);
- mLayerPaint = layerDisabled ? null : (paint == null ? new Paint() : paint);
+ mLayerPaint = mLayerType == LAYER_TYPE_NONE ? null : paint;
mRenderNode.setLayerPaint(mLayerPaint);
// draw() behaves differently if we are on a layer, so we need to
@@ -15680,12 +15768,12 @@
*
* @see #setLayerType(int, android.graphics.Paint)
*/
- public void setLayerPaint(Paint paint) {
+ public void setLayerPaint(@Nullable Paint paint) {
int layerType = getLayerType();
if (layerType != LAYER_TYPE_NONE) {
- mLayerPaint = paint == null ? new Paint() : paint;
+ mLayerPaint = paint;
if (layerType == LAYER_TYPE_HARDWARE) {
- if (mRenderNode.setLayerPaint(mLayerPaint)) {
+ if (mRenderNode.setLayerPaint(paint)) {
invalidateViewProperty(false, false);
}
} else {
@@ -15946,6 +16034,27 @@
}
/**
+ * Called when the passed RenderNode is removed from the draw tree
+ * @hide
+ */
+ public void onRenderNodeDetached(RenderNode renderNode) {
+ if (renderNode == mRenderNode && mRenderNodeDetachedCallback != null) {
+ mRenderNodeDetachedCallback.run();
+ }
+ }
+
+ /**
+ * Set callback for functor detach. Exposed to WebView through WebViewDelegate.
+ * Should not be used otherwise.
+ * @hide
+ */
+ public final Runnable setRenderNodeDetachedCallback(@Nullable Runnable callback) {
+ Runnable oldCallback = mRenderNodeDetachedCallback;
+ mRenderNodeDetachedCallback = callback;
+ return oldCallback;
+ }
+
+ /**
* <p>Calling this method is equivalent to calling <code>getDrawingCache(false)</code>.</p>
*
* @return A non-scaled bitmap representing this view or null if cache is disabled.
@@ -16236,8 +16345,10 @@
/**
* Create a snapshot of the view into a bitmap. We should probably make
* some form of this public, but should think about the API.
+ *
+ * @hide
*/
- Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren) {
+ public Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren) {
int width = mRight - mLeft;
int height = mBottom - mTop;
@@ -16567,7 +16678,7 @@
*/
void setDisplayListProperties(RenderNode renderNode) {
if (renderNode != null) {
- renderNode.setHasOverlappingRendering(hasOverlappingRendering());
+ renderNode.setHasOverlappingRendering(getHasOverlappingRendering());
renderNode.setClipToBounds(mParent instanceof ViewGroup
&& ((ViewGroup) mParent).getClipChildren());
@@ -16843,7 +16954,7 @@
}
} else if (cache != null) {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
- if (layerType == LAYER_TYPE_NONE) {
+ if (layerType == LAYER_TYPE_NONE || mLayerPaint == null) {
// no layer paint, use temporary paint to draw bitmap
Paint cachePaint = parent.mCachePaint;
if (cachePaint == null) {
@@ -16856,9 +16967,13 @@
} else {
// use layer paint to draw the bitmap, merging the two alphas, but also restore
int layerPaintAlpha = mLayerPaint.getAlpha();
- mLayerPaint.setAlpha((int) (alpha * layerPaintAlpha));
+ if (alpha < 1) {
+ mLayerPaint.setAlpha((int) (alpha * layerPaintAlpha));
+ }
canvas.drawBitmap(cache, 0.0f, 0.0f, mLayerPaint);
- mLayerPaint.setAlpha(layerPaintAlpha);
+ if (alpha < 1) {
+ mLayerPaint.setAlpha(layerPaintAlpha);
+ }
}
}
@@ -18002,13 +18117,7 @@
* to clear the previous drawable. setVisible first while we still have the callback set.
*/
if (mBackground != null) {
- // It's possible for this method to be invoked from the View constructor before
- // subclass constructors have run. Drawables can and should trigger invalidations
- // and other activity with their callback on visibility changes, which shouldn't
- // happen before subclass constructors finish. However, we won't have set the
- // drawable as visible until the view becomes attached. This guard below keeps
- // multiple calls to this method from constructors from causing issues.
- if (mBackground.isVisible()) {
+ if (isAttachedToWindow()) {
mBackground.setVisible(false, false);
}
mBackground.setCallback(null);
@@ -18243,13 +18352,7 @@
}
if (mForegroundInfo.mDrawable != null) {
- // It's possible for this method to be invoked from the View constructor before
- // subclass constructors have run. Drawables can and should trigger invalidations
- // and other activity with their callback on visibility changes, which shouldn't
- // happen before subclass constructors finish. However, we won't have set the
- // drawable as visible until the view becomes attached. This guard below keeps
- // multiple calls to this method from constructors from causing issues.
- if (mForegroundInfo.mDrawable.isVisible()) {
+ if (isAttachedToWindow()) {
mForegroundInfo.mDrawable.setVisible(false, false);
}
mForegroundInfo.mDrawable.setCallback(null);
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 816d9c4..3f7bbdf 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3251,8 +3251,11 @@
}
}
+ /**
+ * @hide
+ */
@Override
- Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren) {
+ public Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren) {
int count = mChildrenCount;
int[] visibilities = null;
@@ -3262,7 +3265,8 @@
View child = getChildAt(i);
visibilities[i] = child.getVisibility();
if (visibilities[i] == View.VISIBLE) {
- child.setVisibility(INVISIBLE);
+ child.mViewFlags = (child.mViewFlags & ~View.VISIBILITY_MASK)
+ | (View.INVISIBLE & View.VISIBILITY_MASK);
}
}
}
@@ -3271,7 +3275,9 @@
if (skipChildren) {
for (int i = 0; i < count; i++) {
- getChildAt(i).setVisibility(visibilities[i]);
+ View child = getChildAt(i);
+ child.mViewFlags = (child.mViewFlags & ~View.VISIBILITY_MASK)
+ | (visibilities[i] & View.VISIBILITY_MASK);
}
}
@@ -6749,7 +6755,8 @@
@Override
public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
int dxUnconsumed, int dyUnconsumed) {
- // Do nothing
+ // Re-dispatch up the tree by default
+ dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, null);
}
/**
@@ -6757,7 +6764,8 @@
*/
@Override
public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
- // Do nothing
+ // Re-dispatch up the tree by default
+ dispatchNestedPreScroll(dx, dy, consumed, null);
}
/**
@@ -6765,7 +6773,8 @@
*/
@Override
public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
- return false;
+ // Re-dispatch up the tree by default
+ return dispatchNestedFling(velocityX, velocityY, consumed);
}
/**
@@ -6773,7 +6782,8 @@
*/
@Override
public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
- return false;
+ // Re-dispatch up the tree by default
+ return dispatchNestedPreFling(velocityX, velocityY);
}
/**
diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index f18b7ac..c604234 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -1110,6 +1110,13 @@
@Override
public void onAnimationEnd(Animator animation) {
mView.setHasTransientState(false);
+ if (mAnimatorCleanupMap != null) {
+ Runnable r = mAnimatorCleanupMap.get(animation);
+ if (r != null) {
+ r.run();
+ }
+ mAnimatorCleanupMap.remove(animation);
+ }
if (mListener != null) {
mListener.onAnimationEnd(animation);
}
@@ -1120,13 +1127,6 @@
}
mAnimatorOnEndMap.remove(animation);
}
- if (mAnimatorCleanupMap != null) {
- Runnable r = mAnimatorCleanupMap.get(animation);
- if (r != null) {
- r.run();
- }
- mAnimatorCleanupMap.remove(animation);
- }
mAnimatorMap.remove(animation);
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index ffebf71..5b2877f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -16,7 +16,8 @@
package android.view;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.WindowCallbacks.RESIZE_MODE_DOCKED_DIVIDER;
+import static android.view.WindowCallbacks.RESIZE_MODE_FREEFORM;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
@@ -84,6 +85,7 @@
import android.widget.Scroller;
import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.IResultReceiver;
import com.android.internal.os.SomeArgs;
import com.android.internal.policy.PhoneFallbackEventHandler;
@@ -96,8 +98,8 @@
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
-import java.util.concurrent.CountDownLatch;
import java.util.HashSet;
+import java.util.concurrent.CountDownLatch;
/**
* The top of a view hierarchy, implementing the needed protocol between View
@@ -146,9 +148,6 @@
*/
static final int MAX_TRACKBALL_DELAY = 250;
- private static final int RESIZE_MODE_FREEFORM = 0;
- private static final int RESIZE_MODE_DOCKED_DIVIDER = 1;
-
static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>();
static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList();
@@ -156,7 +155,12 @@
static final ArrayList<ComponentCallbacks> sConfigCallbacks = new ArrayList();
- final ArrayList<WindowCallbacks> mWindowCallbacks = new ArrayList();
+ /**
+ * This list must only be modified by the main thread, so a lock is only needed when changing
+ * the list or when accessing the list from a non-main thread.
+ */
+ @GuardedBy("mWindowCallbacks")
+ final ArrayList<WindowCallbacks> mWindowCallbacks = new ArrayList<>();
final Context mContext;
final IWindowSession mWindowSession;
final Display mDisplay;
@@ -233,6 +237,7 @@
boolean mIsAnimating;
private boolean mDragResizing;
+ private boolean mInvalidateRootRequested;
private int mResizeMode;
private int mCanvasOffsetX;
private int mCanvasOffsetY;
@@ -769,7 +774,7 @@
* has invoked. If false, the functor may be invoked
* asynchronously.
*/
- public void invokeFunctor(long functor, boolean waitForCompletion) {
+ public static void invokeFunctor(long functor, boolean waitForCompletion) {
ThreadedRenderer.invokeFunctor(functor, waitForCompletion);
}
@@ -1828,12 +1833,12 @@
final boolean dragResizing = freeformResizing || dockedResizing;
if (mDragResizing != dragResizing) {
if (dragResizing) {
- startDragResizing(mPendingBackDropFrame,
- mWinFrame.equals(mPendingBackDropFrame), mPendingVisibleInsets,
- mPendingStableInsets);
mResizeMode = freeformResizing
? RESIZE_MODE_FREEFORM
: RESIZE_MODE_DOCKED_DIVIDER;
+ startDragResizing(mPendingBackDropFrame,
+ mWinFrame.equals(mPendingBackDropFrame), mPendingVisibleInsets,
+ mPendingStableInsets, mResizeMode);
} else {
// We shouldn't come here, but if we come we should end the resize.
endDragResizing();
@@ -2121,7 +2126,7 @@
boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;
- if (!cancelDraw) {
+ if (!cancelDraw && !newSurface) {
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).startChangingAnimations();
@@ -2170,7 +2175,6 @@
}
}
}
-
private void handleOutOfResourcesException(Surface.OutOfResourcesException e) {
Log.e(mTag, "OutOfResourcesException initializing HW surface", e);
try {
@@ -2438,6 +2442,9 @@
@Override
public void onHardwarePostDraw(DisplayListCanvas canvas) {
drawAccessibilityFocusedDrawableIfNeeded(canvas);
+ for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
+ mWindowCallbacks.get(i).onPostDraw(canvas);
+ }
}
/**
@@ -2675,7 +2682,8 @@
if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
// If accessibility focus moved, always invalidate the root.
- boolean invalidateRoot = accessibilityFocusDirty;
+ boolean invalidateRoot = accessibilityFocusDirty || mInvalidateRootRequested;
+ mInvalidateRootRequested = false;
// Draw with hardware renderer.
mIsAnimating = false;
@@ -2908,6 +2916,14 @@
return mAttachInfo.mAccessibilityFocusDrawable;
}
+ /**
+ * Requests that the root render node is invalidated next time we perform a draw, such that
+ * {@link WindowCallbacks#onPostDraw} gets called.
+ */
+ public void requestInvalidateRootRenderNode() {
+ mInvalidateRootRequested = true;
+ }
+
boolean scrollToRectOrFocus(Rect rectangle, boolean immediate) {
final Rect ci = mAttachInfo.mContentInsets;
final Rect vi = mAttachInfo.mVisibleInsets;
@@ -3399,6 +3415,13 @@
updateConfiguration(config, false);
}
+ final boolean framesChanged = !mWinFrame.equals(args.arg1)
+ || !mPendingOverscanInsets.equals(args.arg5)
+ || !mPendingContentInsets.equals(args.arg2)
+ || !mPendingStableInsets.equals(args.arg6)
+ || !mPendingVisibleInsets.equals(args.arg3)
+ || !mPendingOutsets.equals(args.arg7);
+
mWinFrame.set((Rect) args.arg1);
mPendingOverscanInsets.set((Rect) args.arg5);
mPendingContentInsets.set((Rect) args.arg2);
@@ -3415,7 +3438,7 @@
mReportNextDraw = true;
}
- if (mView != null) {
+ if (mView != null && framesChanged) {
forceLayout(mView);
}
@@ -7076,14 +7099,12 @@
* Start a drag resizing which will inform all listeners that a window resize is taking place.
*/
private void startDragResizing(Rect initialBounds, boolean fullscreen, Rect systemInsets,
- Rect stableInsets) {
+ Rect stableInsets, int resizeMode) {
if (!mDragResizing) {
mDragResizing = true;
- synchronized (mWindowCallbacks) {
- for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
- mWindowCallbacks.get(i).onWindowDragResizeStart(initialBounds, fullscreen,
- systemInsets, stableInsets);
- }
+ for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
+ mWindowCallbacks.get(i).onWindowDragResizeStart(initialBounds, fullscreen,
+ systemInsets, stableInsets, resizeMode);
}
mFullRedrawNeeded = true;
}
@@ -7095,10 +7116,8 @@
private void endDragResizing() {
if (mDragResizing) {
mDragResizing = false;
- synchronized (mWindowCallbacks) {
- for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
- mWindowCallbacks.get(i).onWindowDragResizeEnd();
- }
+ for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
+ mWindowCallbacks.get(i).onWindowDragResizeEnd();
}
mFullRedrawNeeded = true;
}
@@ -7106,13 +7125,11 @@
private boolean updateContentDrawBounds() {
boolean updated = false;
- synchronized (mWindowCallbacks) {
- for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
- updated |= mWindowCallbacks.get(i).onContentDrawn(
- mWindowAttributes.surfaceInsets.left,
- mWindowAttributes.surfaceInsets.top,
- mWidth, mHeight);
- }
+ for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
+ updated |= mWindowCallbacks.get(i).onContentDrawn(
+ mWindowAttributes.surfaceInsets.left,
+ mWindowAttributes.surfaceInsets.top,
+ mWidth, mHeight);
}
return updated | (mDragResizing && mReportNextDraw);
}
@@ -7121,10 +7138,8 @@
if (mReportNextDraw) {
mWindowDrawCountDown = new CountDownLatch(mWindowCallbacks.size());
}
- synchronized (mWindowCallbacks) {
- for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
- mWindowCallbacks.get(i).onRequestDraw(mReportNextDraw);
- }
+ for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
+ mWindowCallbacks.get(i).onRequestDraw(mReportNextDraw);
}
}
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 36ee3e6..2f3f0bf 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -298,7 +298,7 @@
private boolean mDestroyed;
- private boolean mOverlayWithDecorCaption = false;
+ private boolean mOverlayWithDecorCaptionEnabled = false;
// The current window attributes.
private final WindowManager.LayoutParams mWindowAttributes =
@@ -2139,13 +2139,13 @@
* down. This affects only freeform windows since they display the caption.
* @hide
*/
- public void setOverlayDecorCaption(boolean overlayCaption) {
- mOverlayWithDecorCaption = overlayCaption;
+ public void setOverlayWithDecorCaptionEnabled(boolean enabled) {
+ mOverlayWithDecorCaptionEnabled = enabled;
}
/** @hide */
- public boolean getOverlayDecorCaption() {
- return mOverlayWithDecorCaption;
+ public boolean isOverlayWithDecorCaptionEnabled() {
+ return mOverlayWithDecorCaptionEnabled;
}
/** @hide */
@@ -2181,7 +2181,7 @@
* Called when the activity changes from fullscreen mode to multi-window mode and visa-versa.
* @hide
*/
- public abstract void onMultiWindowChanged();
+ public abstract void onMultiWindowModeChanged();
/**
* Called when the activity just relaunched.
diff --git a/core/java/android/view/WindowCallbacks.java b/core/java/android/view/WindowCallbacks.java
index d2bfca7..b2dc1e9 100644
--- a/core/java/android/view/WindowCallbacks.java
+++ b/core/java/android/view/WindowCallbacks.java
@@ -26,6 +26,11 @@
* @hide
*/
public interface WindowCallbacks {
+
+ public static final int RESIZE_MODE_INVALID = -1;
+ public static final int RESIZE_MODE_FREEFORM = 0;
+ public static final int RESIZE_MODE_DOCKED_DIVIDER = 1;
+
/**
* Called by the system when the window got changed by the user, before the layouter got called.
* It also gets called when the insets changed, or when the window switched between a fullscreen
@@ -51,7 +56,7 @@
* @param stableInsets The stable insets for the window.
*/
void onWindowDragResizeStart(Rect initialBounds, boolean fullscreen, Rect systemInsets,
- Rect stableInsets);
+ Rect stableInsets, int resizeMode);
/**
* Called when a drag resize ends.
@@ -69,4 +74,13 @@
* @param reportNextDraw Whether it should report when the requested draw finishes.
*/
void onRequestDraw(boolean reportNextDraw);
+
+ /**
+ * Called after all the content has drawn and the callback now has the ability to draw something
+ * on top of everything. Call {@link ViewRootImpl#requestInvalidateRootRenderNode} when this
+ * content needs to be redrawn.
+ *
+ * @param canvas The canvas to draw on.
+ */
+ void onPostDraw(DisplayListCanvas canvas);
}
diff --git a/core/java/android/view/WindowInfo.java b/core/java/android/view/WindowInfo.java
index b721074..737e4607 100644
--- a/core/java/android/view/WindowInfo.java
+++ b/core/java/android/view/WindowInfo.java
@@ -44,6 +44,8 @@
public boolean focused;
public final Rect boundsInScreen = new Rect();
public List<IBinder> childTokens;
+ public CharSequence title;
+ public int accessibilityIdOfAnchor = View.NO_ID;
private WindowInfo() {
/* do nothing - hide constructor */
@@ -65,6 +67,8 @@
window.parentToken = other.parentToken;
window.focused = other.focused;
window.boundsInScreen.set(other.boundsInScreen);
+ window.title = other.title;
+ window.accessibilityIdOfAnchor = other.accessibilityIdOfAnchor;
if (other.childTokens != null && !other.childTokens.isEmpty()) {
if (window.childTokens == null) {
@@ -95,6 +99,8 @@
parcel.writeStrongBinder(parentToken);
parcel.writeInt(focused ? 1 : 0);
boundsInScreen.writeToParcel(parcel, flags);
+ parcel.writeCharSequence(title);
+ parcel.writeInt(accessibilityIdOfAnchor);
if (childTokens != null && !childTokens.isEmpty()) {
parcel.writeInt(1);
@@ -108,13 +114,15 @@
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("WindowInfo[");
- builder.append("type=").append(type);
+ builder.append("title=").append(title);
+ builder.append(", type=").append(type);
builder.append(", layer=").append(layer);
builder.append(", token=").append(token);
builder.append(", bounds=").append(boundsInScreen);
builder.append(", parent=").append(parentToken);
builder.append(", focused=").append(focused);
builder.append(", children=").append(childTokens);
+ builder.append(", accessibility anchor=").append(accessibilityIdOfAnchor);
builder.append(']');
return builder.toString();
}
@@ -126,6 +134,8 @@
parentToken = parcel.readStrongBinder();
focused = (parcel.readInt() == 1);
boundsInScreen.readFromParcel(parcel);
+ title = parcel.readCharSequence();
+ accessibilityIdOfAnchor = parcel.readInt();
final boolean hasChildren = (parcel.readInt() == 1);
if (hasChildren) {
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 929fdac..750931a 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -91,6 +91,7 @@
mWindowDecorInsetsConsumed = src.mWindowDecorInsetsConsumed;
mStableInsetsConsumed = src.mStableInsetsConsumed;
mIsRound = src.mIsRound;
+ mAlwaysConsumeNavBar = src.mAlwaysConsumeNavBar;
}
/** @hide */
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 03dcf99..c372c30 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1693,6 +1693,14 @@
*/
public long userActivityTimeout = -1;
+ /**
+ * For windows with an anchor (e.g. PopupWindow), keeps track of the View that anchors the
+ * window.
+ *
+ * @hide
+ */
+ public int accessibilityIdOfAnchor = -1;
+
public LayoutParams() {
super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
type = TYPE_APPLICATION;
@@ -1799,6 +1807,7 @@
out.writeInt(surfaceInsets.bottom);
out.writeInt(hasManualSurfaceInsets ? 1 : 0);
out.writeInt(needsMenuKey);
+ out.writeInt(accessibilityIdOfAnchor);
}
public static final Parcelable.Creator<LayoutParams> CREATOR
@@ -1849,6 +1858,7 @@
surfaceInsets.bottom = in.readInt();
hasManualSurfaceInsets = in.readInt() != 0;
needsMenuKey = in.readInt();
+ accessibilityIdOfAnchor = in.readInt();
}
@SuppressWarnings({"PointlessBitwiseExpression"})
@@ -1888,6 +1898,8 @@
/** {@hide} */
public static final int PREFERRED_DISPLAY_MODE_ID = 1 << 23;
/** {@hide} */
+ public static final int ACCESSIBILITY_ANCHOR_CHANGED = 1 << 24;
+ /** {@hide} */
public static final int EVERYTHING_CHANGED = 0xffffffff;
// internal buffer to backup/restore parameters under compatibility mode.
@@ -2048,6 +2060,11 @@
changes |= NEEDS_MENU_KEY_CHANGED;
}
+ if (accessibilityIdOfAnchor != o.accessibilityIdOfAnchor) {
+ accessibilityIdOfAnchor = o.accessibilityIdOfAnchor;
+ changes |= ACCESSIBILITY_ANCHOR_CHANGED;
+ }
+
return changes;
}
diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java
index c22d60d..3ad730b 100644
--- a/core/java/android/view/WindowManagerInternal.java
+++ b/core/java/android/view/WindowManagerInternal.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Rect;
import android.graphics.Region;
@@ -157,6 +158,15 @@
public abstract void setMagnificationSpec(MagnificationSpec spec);
/**
+ * Obtains the magnified and available regions.
+ *
+ * @param outMagnified the currently magnified region
+ * @param outAvailable the region available for magnification
+ */
+ public abstract void getMagnificationRegions(@NonNull Region outMagnified,
+ @NonNull Region outAvailable);
+
+ /**
* Gets the magnification and translation applied to a window given its token.
* Not all windows are magnified and the window manager policy determines which
* windows are magnified. The returned result also takes into account the compat
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index c1392fe..96f179b 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -415,7 +415,7 @@
* Returns true if the window is current in multi-windowing mode. i.e. it shares the
* screen with other application windows.
*/
- public boolean inMultiWindowMode();
+ public boolean isInMultiWindowMode();
}
/**
@@ -1397,4 +1397,9 @@
* is allowed, so for example, if DOCKED_RIGHT is not allowed, DOCKED_LEFT is allowed.
*/
public boolean isDockSideAllowed(int dockSide);
+
+ /**
+ * Called when the configuration has changed, and it's safe to load new values from resources.
+ */
+ public void onConfigurationChanged();
}
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 2cde03d..f8a13a3 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -240,10 +240,12 @@
* <li>{@link #getMovementGranularity()} - Sets the granularity at which a view's text
* was traversed.</li>
* <li>{@link #getText()} - The text of the source's sub-tree.</li>
- * <li>{@link #getFromIndex()} - The start of the next/previous text at the specified granularity
- * - inclusive.</li>
- * <li>{@link #getToIndex()} - The end of the next/previous text at the specified granularity
- * - exclusive.</li>
+ * <li>{@link #getFromIndex()} - The start the text that was skipped over in this movement.
+ * This is the starting point when moving forward through the text, but not when moving
+ * back.</li>
+ * <li>{@link #getToIndex()} - The end of the text that was skipped over in this movement.
+ * This is the ending point when moving forward through the text, but not when moving
+ * back.</li>
* <li>{@link #isPassword()} - Whether the source is password.</li>
* <li>{@link #isEnabled()} - Whether the source is enabled.</li>
* <li>{@link #getContentDescription()} - The content description of the source.</li>
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index bdaf291..02d2a8b 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -328,36 +328,6 @@
// Action arguments
/**
- * Argument for specifying index of {@link android.text.style.ClickableSpan} the click action is
- * related to.
- * <p>
- * <strong>Type:</strong> int<br>
- * <strong>Actions:</strong>
- * {@link AccessibilityAction#ACTION_CLICK}
- * </p>
- *
- * @see AccessibilityAction#ACTION_CLICK
- */
- public static final String ACTION_ARGUMENT_CLICK_SPAN_INDEX_INT =
- "android.view.accessibility.action.ARGUMENT_CLICK_SPAN_INDEX_INT";
-
- /**
- * Argument for specifying index of character in the text which contains
- * {@link android.text.style.ClickableSpan} the click action is
- * related to. If there is more than one {@link android.text.style.ClickableSpan} assigned for
- * the range the character is in only the first span would be clicked.
- * <p>
- * <strong>Type:</strong> int<br>
- * <strong>Actions:</strong>
- * {@link AccessibilityAction#ACTION_CLICK}
- * </p>
- *
- * @see AccessibilityAction#ACTION_CLICK
- */
- public static final String ACTION_ARGUMENT_CLICK_CHARACTER_INDEX_INT =
- "android.view.accessibility.action.ARGUMENT_CLICK_CHARACTER_INDEX_INT";
-
- /**
* Argument for which movement granularity to be used when traversing the node text.
* <p>
* <strong>Type:</strong> int<br>
@@ -2926,8 +2896,10 @@
mInputType = other.mInputType;
mLiveRegion = other.mLiveRegion;
mDrawingOrderInParent = other.mDrawingOrderInParent;
- if (other.mExtras != null && !other.mExtras.isEmpty()) {
- getExtras().putAll(other.mExtras);
+ if (other.mExtras != null) {
+ mExtras = new Bundle(other.mExtras);
+ } else {
+ mExtras = null;
}
mRangeInfo = (other.mRangeInfo != null)
? RangeInfo.obtain(other.mRangeInfo) : null;
@@ -3006,7 +2978,9 @@
mDrawingOrderInParent = parcel.readInt();
if (parcel.readInt() == 1) {
- getExtras().putAll(parcel.readBundle());
+ mExtras = parcel.readBundle();
+ } else {
+ mExtras = null;
}
if (parcel.readInt() == 1) {
@@ -3073,9 +3047,7 @@
mTextSelectionEnd = UNDEFINED_SELECTION_INDEX;
mInputType = InputType.TYPE_NULL;
mLiveRegion = View.ACCESSIBILITY_LIVE_REGION_NONE;
- if (mExtras != null) {
- mExtras.clear();
- }
+ mExtras = null;
if (mRangeInfo != null) {
mRangeInfo.recycle();
mRangeInfo = null;
@@ -3389,33 +3361,6 @@
/**
* Action that clicks on the node info.
- *
- * <p>
- * If a specific {@link android.text.style.ClickableSpan} within node's text content is
- * supposed to be clicked, then one of {@link #ACTION_ARGUMENT_CLICK_SPAN_INDEX_INT} or
- * {@link #ACTION_ARGUMENT_CLICK_CHARACTER_INDEX_INT} arguments should be specified.
- * If both arguments are set {@link #ACTION_ARGUMENT_CLICK_CHARACTER_INDEX_INT} would
- * be ignored.<br>
- *
- * {@link #ACTION_ARGUMENT_CLICK_SPAN_INDEX_INT} specifies index of corresponding
- * {@link android.text.style.ClickableSpan} in {@link android.text.SpannableString}.<br>
- *
- * {@link #ACTION_ARGUMENT_CLICK_CHARACTER_INDEX_INT} specifies index of character
- * that could contain one or more spans.
- * </p>
- *
- * <p>
- * <strong>Optional arguments:</strong>
- * {@link #ACTION_ARGUMENT_CLICK_SPAN_INDEX_INT},
- * {@link #ACTION_ARGUMENT_CLICK_CHARACTER_INDEX_INT}<br>
- * <strong>Example:</strong> Perform click on 3rd {@link android.text.style.ClickableSpan}
- * inside {@link android.text.SpannableString} in node's text.
- * <code><pre><p>
- * Bundle arguments = new Bundle();
- * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_CLICK_SPAN_INDEX_INT, 3);
- * info.performAction(AccessibilityAction.ACTION_CLICK.getId(), arguments);
- * </code></pre></p>
- * </p>
*/
public static final AccessibilityAction ACTION_CLICK =
new AccessibilityAction(
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
index ad78b68..d0d4507 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -89,6 +89,8 @@
private int mParentId = UNDEFINED;
private final Rect mBoundsInScreen = new Rect();
private LongArray mChildIds;
+ private CharSequence mTitle;
+ private int mAnchorId = UNDEFINED;
private int mConnectionId = UNDEFINED;
@@ -97,6 +99,26 @@
}
/**
+ * Gets the title of the window.
+ *
+ * @return The title.
+ */
+ public CharSequence getTitle() {
+ return mTitle;
+ }
+
+ /**
+ * Sets the title of the window.
+ *
+ * @param title The title.
+ *
+ * @hide
+ */
+ public void setTitle(CharSequence title) {
+ mTitle = title;
+ }
+
+ /**
* Gets the type of the window.
*
* @return The type.
@@ -159,9 +181,35 @@
}
/**
- * Gets the parent window if such.
+ * Sets the anchor node's ID.
*
- * @return The parent window.
+ * @param anchorId The anchor's accessibility id in its window.
+ *
+ * @hide
+ */
+ public void setAnchorId(int anchorId) {
+ mAnchorId = anchorId;
+ }
+
+ /**
+ * Gets the node that anchors this window to another.
+ *
+ * @return The anchor node, or {@code null} if none exists.
+ */
+ public AccessibilityNodeInfo getAnchor() {
+ if ((mConnectionId == UNDEFINED) || (mAnchorId == UNDEFINED) || (mParentId == UNDEFINED)) {
+ return null;
+ }
+
+ AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
+ return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
+ mParentId, mAnchorId, true, 0);
+ }
+
+ /**
+ * Gets the parent window.
+ *
+ * @return The parent window, or {@code null} if none exists.
*/
public AccessibilityWindowInfo getParent() {
if (mConnectionId == UNDEFINED || mParentId == UNDEFINED) {
@@ -370,6 +418,8 @@
infoClone.mId = info.mId;
infoClone.mParentId = info.mParentId;
infoClone.mBoundsInScreen.set(info.mBoundsInScreen);
+ infoClone.mTitle = info.mTitle;
+ infoClone.mAnchorId = info.mAnchorId;
if (info.mChildIds != null && info.mChildIds.size() > 0) {
if (infoClone.mChildIds == null) {
@@ -410,6 +460,8 @@
parcel.writeInt(mId);
parcel.writeInt(mParentId);
mBoundsInScreen.writeToParcel(parcel, flags);
+ parcel.writeCharSequence(mTitle);
+ parcel.writeInt(mAnchorId);
final LongArray childIds = mChildIds;
if (childIds == null) {
@@ -432,6 +484,8 @@
mId = parcel.readInt();
mParentId = parcel.readInt();
mBoundsInScreen.readFromParcel(parcel);
+ mTitle = parcel.readCharSequence();
+ mAnchorId = parcel.readInt();
final int childCount = parcel.readInt();
if (childCount > 0) {
@@ -471,6 +525,7 @@
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("AccessibilityWindowInfo[");
+ builder.append("title=").append(mTitle);
builder.append("id=").append(mId);
builder.append(", type=").append(typeToString(mType));
builder.append(", layer=").append(mLayer);
@@ -494,6 +549,7 @@
builder.append(']');
} else {
builder.append(", hasParent=").append(mParentId != UNDEFINED);
+ builder.append(", isAnchored=").append(mAnchorId != UNDEFINED);
builder.append(", hasChildren=").append(mChildIds != null
&& mChildIds.size() > 0);
}
@@ -515,6 +571,8 @@
mChildIds.clear();
}
mConnectionId = UNDEFINED;
+ mAnchorId = UNDEFINED;
+ mTitle = null;
}
/**
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 655c9b3..7f44bac 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -59,4 +59,8 @@
boolean touchExplorationEnabled);
IBinder getWindowToken(int windowId, int userId);
+
+ void enableAccessibilityService(in ComponentName service, int userId);
+
+ void disableAccessibilityService(in ComponentName service, int userId);
}
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index 1536c29..d89c172 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -93,8 +93,12 @@
*/
public static final int ZORDER_BOTTOM = -1;
- private static final boolean USE_CLOSEGUARD
- = SystemProperties.getBoolean("log.closeguard.Animation", false);
+ // Use a preload holder to isolate static initialization into inner class, which allows
+ // Animation and its subclasses to be compile-time initialized.
+ private static class NoImagePreloadHolder {
+ public static final boolean USE_CLOSEGUARD
+ = SystemProperties.getBoolean("log.closeguard.Animation", false);
+ }
/**
* Set by {@link #getTransformation(long, Transformation)} when the animation ends.
@@ -859,7 +863,7 @@
if (!mStarted) {
fireAnimationStart();
mStarted = true;
- if (USE_CLOSEGUARD) {
+ if (NoImagePreloadHolder.USE_CLOSEGUARD) {
guard.open("cancel or detach or getTransformation");
}
}
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index a3c49c5..89dec2d 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -16,6 +16,7 @@
package android.view.inputmethod;
+import android.annotation.CallSuper;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Bundle;
@@ -154,12 +155,11 @@
}
/**
- * Called when this InputConnection is no longer used by the InputMethodManager.
- *
- * @hide
+ * Default implementation calls {@link #finishComposingText()}.
*/
- public void reportFinish() {
- // Intentionally empty
+ @CallSuper
+ public void closeConnection() {
+ finishComposingText();
}
/**
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index 8002a8e..9f66429 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -45,6 +45,8 @@
* was introduced in {@link android.os.Build.VERSION_CODES#N}.</li>
* <li>{@link #getHandler()}}, which was introduced in
* {@link android.os.Build.VERSION_CODES#N}.</li>
+ * <li>{@link #closeConnection()}}, which was introduced in
+ * {@link android.os.Build.VERSION_CODES#N}.</li>
* </ul>
*
* <h3>Implementing an IME or an editor</h3>
@@ -820,4 +822,18 @@
* @return {@code null} to use the default {@link Handler}.
*/
public Handler getHandler();
+
+ /**
+ * Called by the system up to only once to notify that the system is about to invalidate
+ * connection between the input method and the application.
+ *
+ * <p><strong>Editor authors</strong>: You can clear all the nested batch edit right now and
+ * you no longer need to handle subsequent callbacks on this connection, including
+ * {@link #beginBatchEdit()}}. Note that although the system tries to call this method whenever
+ * possible, there may be a chance that this method is not called in some exceptional
+ * situations.</p>
+ *
+ * <p>Note: This does nothing when called from input methods.</p>
+ */
+ public void closeConnection();
}
diff --git a/core/java/android/view/inputmethod/InputConnectionInspector.java b/core/java/android/view/inputmethod/InputConnectionInspector.java
index 46b2c3e..118a61f 100644
--- a/core/java/android/view/inputmethod/InputConnectionInspector.java
+++ b/core/java/android/view/inputmethod/InputConnectionInspector.java
@@ -73,6 +73,11 @@
* {@link android.os.Build.VERSION_CODES#N} and later.
*/
int GET_HANDLER = 1 << 5;
+ /**
+ * {@link InputConnection#closeConnection()}} is available in
+ * {@link android.os.Build.VERSION_CODES#N} and later.
+ */
+ int CLOSE_CONNECTION = 1 << 6;
}
private static final Map<Class, Integer> sMissingMethodsMap = Collections.synchronizedMap(
@@ -119,6 +124,9 @@
if (!hasGetHandler(clazz)) {
flags |= MissingMethodFlags.GET_HANDLER;
}
+ if (!hasCloseConnection(clazz)) {
+ flags |= MissingMethodFlags.CLOSE_CONNECTION;
+ }
sMissingMethodsMap.put(clazz, flags);
return flags;
}
@@ -178,6 +186,15 @@
}
}
+ private static boolean hasCloseConnection(@NonNull final Class clazz) {
+ try {
+ final Method method = clazz.getMethod("closeConnection");
+ return !Modifier.isAbstract(method.getModifiers());
+ } catch (NoSuchMethodException e) {
+ return false;
+ }
+ }
+
public static String getMissingMethodFlagsAsString(@MissingMethodFlags final int flags) {
final StringBuilder sb = new StringBuilder();
boolean isEmpty = true;
@@ -219,6 +236,12 @@
}
sb.append("getHandler()");
}
+ if ((flags & MissingMethodFlags.CLOSE_CONNECTION) != 0) {
+ if (!isEmpty) {
+ sb.append(",");
+ }
+ sb.append("closeConnection()");
+ }
return sb.toString();
}
}
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
index 381df49..e743f62 100644
--- a/core/java/android/view/inputmethod/InputConnectionWrapper.java
+++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java
@@ -261,4 +261,12 @@
public Handler getHandler() {
return mTarget.getHandler();
}
+
+ /**
+ * {@inheritDoc}
+ * @throws NullPointerException if the target is {@code null}.
+ */
+ public void closeConnection() {
+ mTarget.closeConnection();
+ }
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 4bb0c3c..1ce80434 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -493,12 +493,7 @@
// Check focus again in case that "onWindowFocus" is called before
// handling this message.
if (mServedView != null && mServedView.hasWindowFocus()) {
- // Please note that this handler thread could be different
- // from a thread that created mServedView. That could happen
- // the current activity is running in the system process.
- // In that case, we really should not call
- // mServedInputConnectionWrapper.finishComposingText().
- if (checkFocusNoStartInput(mHasBeenInactive, false)) {
+ if (checkFocusNoStartInput(mHasBeenInactive)) {
final int reason = active ?
InputMethodClient.START_INPUT_REASON_ACTIVATED_BY_IMMS :
InputMethodClient.START_INPUT_REASON_DEACTIVATED_BY_IMMS;
@@ -549,7 +544,7 @@
// reportFinish() will take effect.
return;
}
- reportFinish();
+ closeConnection();
}
@Override
@@ -832,7 +827,7 @@
void finishInputLocked() {
mNextServedView = null;
if (mServedView != null) {
- if (DEBUG) Log.v(TAG, "FINISH INPUT: " + mServedView);
+ if (DEBUG) Log.v(TAG, "FINISH INPUT: mServedView=" + dumpViewInfo(mServedView));
if (mCurrentTextBoxAttribute != null) {
try {
mService.finishInput(mClient);
@@ -1141,10 +1136,10 @@
final View view;
synchronized (mH) {
view = mServedView;
-
+
// Make sure we have a window token for the served view.
if (DEBUG) {
- Log.v(TAG, "Starting input: view=" + view +
+ Log.v(TAG, "Starting input: view=" + dumpViewInfo(view) +
" reason=" + InputMethodClient.getStartInputReason(startInputReason));
}
if (view == null) {
@@ -1152,7 +1147,7 @@
return false;
}
}
-
+
// Now we need to get an input connection from the served view.
// This is complicated in a couple ways: we can't be holding our lock
// when calling out to the view, and we need to make sure we call into
@@ -1197,9 +1192,10 @@
// changed.
if (mServedView != view || !mServedConnecting) {
// Something else happened, so abort.
- if (DEBUG) Log.v(TAG,
- "Starting input: finished by someone else (view="
- + mServedView + " conn=" + mServedConnecting + ")");
+ if (DEBUG) Log.v(TAG,
+ "Starting input: finished by someone else. view=" + dumpViewInfo(view)
+ + " mServedView=" + dumpViewInfo(mServedView)
+ + " mServedConnecting=" + mServedConnecting);
return false;
}
@@ -1243,7 +1239,7 @@
mServedInputConnectionWrapper = servedContext;
try {
- if (DEBUG) Log.v(TAG, "START INPUT: " + view + " ic="
+ if (DEBUG) Log.v(TAG, "START INPUT: view=" + dumpViewInfo(view) + " ic="
+ ic + " tba=" + tba + " controlFlags=#"
+ Integer.toHexString(controlFlags));
final InputBindResult res = mService.startInputOrWindowGainedFocus(
@@ -1309,7 +1305,13 @@
}
void focusInLocked(View view) {
- if (DEBUG) Log.v(TAG, "focusIn: " + view);
+ if (DEBUG) Log.v(TAG, "focusIn: " + dumpViewInfo(view));
+
+ if (view != null && view.isTemporarilyDetached()) {
+ // This is a request from a view that is temporarily detached from a window.
+ if (DEBUG) Log.v(TAG, "Temporarily detached view, ignoring");
+ return;
+ }
if (mCurRootView != view.getRootView()) {
// This is a request from a window that isn't in the window with
@@ -1328,15 +1330,15 @@
*/
public void focusOut(View view) {
synchronized (mH) {
- if (DEBUG) Log.v(TAG, "focusOut: " + view
- + " mServedView=" + mServedView
- + " winFocus=" + view.hasWindowFocus());
+ if (DEBUG) Log.v(TAG, "focusOut: view=" + dumpViewInfo(view)
+ + " mServedView=" + dumpViewInfo(mServedView));
if (mServedView != view) {
// The following code would auto-hide the IME if we end up
// with no more views with focus. This can happen, however,
// whenever we go into touch mode, so it ends up hiding
// at times when we don't really want it to. For now it
// seems better to just turn it all off.
+ // TODO: Check view.isTemporarilyDetached() when re-enable the following code.
if (false && view.hasWindowFocus()) {
mNextServedView = null;
scheduleCheckFocusLocked(view);
@@ -1351,10 +1353,9 @@
*/
public void onViewDetachedFromWindow(View view) {
synchronized (mH) {
- if (DEBUG) Log.v(TAG, "onViewDetachedFromWindow: " + view
- + " mServedView=" + mServedView
- + " hasWindowFocus=" + view.hasWindowFocus());
- if (mServedView == view && view.hasWindowFocus()) {
+ if (DEBUG) Log.v(TAG, "onViewDetachedFromWindow: view=" + dumpViewInfo(view)
+ + " mServedView=" + dumpViewInfo(mServedView));
+ if (mServedView == view) {
mNextServedView = null;
scheduleCheckFocusLocked(view);
}
@@ -1372,12 +1373,12 @@
* @hide
*/
public void checkFocus() {
- if (checkFocusNoStartInput(false, true)) {
+ if (checkFocusNoStartInput(false)) {
startInputInner(InputMethodClient.START_INPUT_REASON_CHECK_FOCUS, null, 0, 0, 0);
}
}
- private boolean checkFocusNoStartInput(boolean forceNewFocus, boolean finishComposingText) {
+ private boolean checkFocusNoStartInput(boolean forceNewFocus) {
// This is called a lot, so short-circuit before locking.
if (mServedView == mNextServedView && !forceNewFocus) {
return false;
@@ -1411,7 +1412,7 @@
mServedConnecting = true;
}
- if (finishComposingText && ic != null) {
+ if (ic != null) {
ic.finishComposingText();
}
@@ -1457,7 +1458,7 @@
controlFlags |= CONTROL_WINDOW_FIRST;
}
- if (checkFocusNoStartInput(forceNewFocus, true)) {
+ if (checkFocusNoStartInput(forceNewFocus)) {
// We need to restart input on the current focus view. This
// should be done in conjunction with telling the system service
// about the window gaining focus, to help make the transition
@@ -2311,4 +2312,17 @@
}
}
}
+
+ private static String dumpViewInfo(@Nullable final View view) {
+ if (view == null) {
+ return "null";
+ }
+ final StringBuilder sb = new StringBuilder();
+ sb.append(view);
+ sb.append(",focus=" + view.hasFocus());
+ sb.append(",windowFocus=" + view.hasWindowFocus());
+ sb.append(",window=" + view.getWindowToken());
+ sb.append(",temporaryDetach=" + view.isTemporarilyDetached());
+ return sb.toString();
+ }
}
diff --git a/core/java/android/webkit/WebViewDelegate.java b/core/java/android/webkit/WebViewDelegate.java
index 94dc03c..b6516c8 100644
--- a/core/java/android/webkit/WebViewDelegate.java
+++ b/core/java/android/webkit/WebViewDelegate.java
@@ -16,6 +16,8 @@
package android.webkit;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.app.ActivityThread;
import android.app.Application;
@@ -86,8 +88,7 @@
*/
public void invokeDrawGlFunctor(View containerView, long nativeDrawGLFunctor,
boolean waitForCompletion) {
- ViewRootImpl viewRootImpl = containerView.getViewRootImpl();
- viewRootImpl.invokeFunctor(nativeDrawGLFunctor, waitForCompletion);
+ ViewRootImpl.invokeFunctor(nativeDrawGLFunctor, waitForCompletion);
}
/**
@@ -110,6 +111,24 @@
}
/**
+ * Set the Runnable callback the DrawGlFunction functor is detached and free to be destroyed.
+ * This will replace the previous callback, if any.
+ *
+ * @param view The view to set the callback. Should be the view where onDraw inserted
+ * DrawGLFunctor.
+ * @param callback The new callback to set on the view.
+ * @throws IllegalArgumentException if view is null.
+ * @return The previous callback on this view.
+ */
+ public Runnable setDrawGlFunctionDetachedCallback(
+ @NonNull View view, @Nullable Runnable callback) {
+ if (view == null) {
+ throw new IllegalArgumentException("View cannot be null");
+ }
+ return view.setRenderNodeDetachedCallback(callback);
+ }
+
+ /**
* Detaches the draw GL functor.
*
* @param nativeDrawGLFunctor the pointer to the native functor that implements
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index f1bf890..0ac5731 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -21,7 +21,6 @@
import android.app.AppGlobals;
import android.app.Application;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -128,11 +127,6 @@
public MissingWebViewPackageException(Exception e) { super(e); }
}
- // TODO (gsennton) remove when committing webview xts test change
- public static String getWebViewPackageName() {
- return null;
- }
-
/**
* @hide
*/
@@ -566,20 +560,12 @@
return result;
}
- /**
- * Returns whether the entire package from an ACTION_PACKAGE_CHANGED intent was changed (rather
- * than just one of its components).
- * @hide
- */
- public static boolean entirePackageChanged(Intent intent) {
- String[] componentList =
- intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
- return Arrays.asList(componentList).contains(
- intent.getDataString().substring("package:".length()));
- }
+ private static String WEBVIEW_UPDATE_SERVICE_NAME = "webviewupdate";
- private static IWebViewUpdateService getUpdateService() {
- return IWebViewUpdateService.Stub.asInterface(ServiceManager.getService("webviewupdate"));
+ /** @hide */
+ public static IWebViewUpdateService getUpdateService() {
+ return IWebViewUpdateService.Stub.asInterface(
+ ServiceManager.getService(WEBVIEW_UPDATE_SERVICE_NAME));
}
private static native boolean nativeReserveAddressSpace(long addressSpaceToReserve);
diff --git a/core/java/android/webkit/WebViewProviderInfo.java b/core/java/android/webkit/WebViewProviderInfo.java
index 75ccf35..5d091c9 100644
--- a/core/java/android/webkit/WebViewProviderInfo.java
+++ b/core/java/android/webkit/WebViewProviderInfo.java
@@ -16,32 +16,20 @@
package android.webkit;
-import android.app.AppGlobals;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.Signature;
-import android.os.Build;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
-import android.util.AndroidRuntimeException;
-import android.util.Base64;
import java.util.Arrays;
-/** @hide */
-public class WebViewProviderInfo implements Parcelable {
+/**
+ * @hide
+ */
+@SystemApi
+public final class WebViewProviderInfo implements Parcelable {
- /**
- * @hide
- */
- public static class WebViewPackageNotFoundException extends AndroidRuntimeException {
- public WebViewPackageNotFoundException(String message) { super(message); }
- public WebViewPackageNotFoundException(Exception e) { super(e); }
- }
-
- public WebViewProviderInfo(String packageName, String description, boolean availableByDefault,
- boolean isFallback, String[] signatures) {
+ public WebViewProviderInfo(String packageName, String description,
+ boolean availableByDefault, boolean isFallback, String[] signatures) {
this.packageName = packageName;
this.description = description;
this.availableByDefault = availableByDefault;
@@ -49,92 +37,6 @@
this.signatures = signatures;
}
- private boolean hasValidSignature() {
- if (Build.IS_DEBUGGABLE)
- return true;
- Signature[] packageSignatures;
- try {
- // If no signature is declared, instead check whether the package is included in the
- // system.
- if (signatures == null || signatures.length == 0)
- return getPackageInfo().applicationInfo.isSystemApp();
-
- packageSignatures = getPackageInfo().signatures;
- } catch (WebViewPackageNotFoundException e) {
- return false;
- }
- if (packageSignatures.length != 1)
- return false;
-
- final byte[] packageSignature = packageSignatures[0].toByteArray();
- // Return whether the package signature matches any of the valid signatures
- for (String signature : signatures) {
- final byte[] validSignature = Base64.decode(signature, Base64.DEFAULT);
- if (Arrays.equals(packageSignature, validSignature))
- return true;
- }
- return false;
- }
-
- /**
- * Returns whether this provider is valid for use as a WebView provider.
- */
- public boolean isValidProvider() {
- ApplicationInfo applicationInfo;
- try {
- applicationInfo = getPackageInfo().applicationInfo;
- } catch (WebViewPackageNotFoundException e) {
- return false;
- }
- if (hasValidSignature() && WebViewFactory.getWebViewLibrary(applicationInfo) != null) {
- return true;
- }
- return false;
- }
-
- /**
- * Returns whether this package is enabled.
- * This state can be changed by the user from Settings->Apps
- */
- public boolean isEnabled() {
- try {
- // Explicitly fetch up-to-date package info here since the enabled-state of the package
- // might have changed since we last fetched its package info.
- updatePackageInfo();
- return getPackageInfo().applicationInfo.enabled;
- } catch (WebViewPackageNotFoundException e) {
- return false;
- }
- }
-
- /**
- * Returns whether the provider is always available as long as it is valid.
- * If this returns false, the provider will only be used if the user chose this provider.
- */
- public boolean isAvailableByDefault() {
- return availableByDefault;
- }
-
- public boolean isFallbackPackage() {
- return isFallback;
- }
-
- private void updatePackageInfo() {
- try {
- PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
- packageInfo = pm.getPackageInfo(packageName, PACKAGE_FLAGS);
- } catch (PackageManager.NameNotFoundException e) {
- throw new WebViewPackageNotFoundException(e);
- }
- }
-
- public PackageInfo getPackageInfo() {
- if (packageInfo == null) {
- updatePackageInfo();
- }
- return packageInfo;
- }
-
// aidl stuff
public static final Parcelable.Creator<WebViewProviderInfo> CREATOR =
new Parcelable.Creator<WebViewProviderInfo>() {
@@ -153,7 +55,6 @@
availableByDefault = (in.readInt() > 0);
isFallback = (in.readInt() > 0);
signatures = in.createStringArray();
- packageInfo = null;
}
@Override
@@ -171,16 +72,9 @@
}
// fields read from framework resource
- public String packageName;
- public String description;
- private boolean availableByDefault;
- private boolean isFallback;
-
- private String[] signatures;
-
- private PackageInfo packageInfo;
-
- // flags declaring we want extra info from the package manager
- private final static int PACKAGE_FLAGS = PackageManager.GET_META_DATA
- | PackageManager.GET_SIGNATURES | PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
+ public final String packageName;
+ public final String description;
+ public final boolean availableByDefault;
+ public final boolean isFallback;
+ public final String[] signatures;
}
diff --git a/core/java/android/webkit/WebViewUpdateService.java b/core/java/android/webkit/WebViewUpdateService.java
new file mode 100644
index 0000000..4e83d88
--- /dev/null
+++ b/core/java/android/webkit/WebViewUpdateService.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.annotation.SystemApi;
+import android.os.RemoteException;
+
+/**
+ * @hide
+ */
+@SystemApi
+public final class WebViewUpdateService {
+
+ private WebViewUpdateService () {}
+
+ /**
+ * Fetch all packages that could potentially implement WebView.
+ */
+ public static WebViewProviderInfo[] getAllWebViewPackages() {
+ try {
+ return getUpdateService().getAllWebViewPackages();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Fetch all packages that could potentially implement WebView and are currently valid.
+ */
+ public static WebViewProviderInfo[] getValidWebViewPackages() {
+ try {
+ return getUpdateService().getValidWebViewPackages();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Used by DevelopmentSetting to get the name of the WebView provider currently in use.
+ */
+ public static String getCurrentWebViewPackageName() {
+ try {
+ return getUpdateService().getCurrentWebViewPackageName();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static IWebViewUpdateService getUpdateService() {
+ return WebViewFactory.getUpdateService();
+ }
+}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 7cbe8de..6e1dff9 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -5937,6 +5937,11 @@
public Handler getHandler() {
return getTarget().getHandler();
}
+
+ @Override
+ public void closeConnection() {
+ getTarget().closeConnection();
+ }
}
/**
diff --git a/core/java/android/widget/AccessibilityIterators.java b/core/java/android/widget/AccessibilityIterators.java
index a3d58a4..442ffa1 100644
--- a/core/java/android/widget/AccessibilityIterators.java
+++ b/core/java/android/widget/AccessibilityIterators.java
@@ -134,8 +134,8 @@
@Override
public int[] following(int offset) {
- final int textLegth = mText.length();
- if (textLegth <= 0) {
+ final int textLength = mText.length();
+ if (textLength <= 0) {
return null;
}
if (offset >= mText.length()) {
@@ -163,8 +163,8 @@
@Override
public int[] preceding(int offset) {
- final int textLegth = mText.length();
- if (textLegth <= 0) {
+ final int textLength = mText.length();
+ if (textLength <= 0) {
return null;
}
if (offset <= 0) {
@@ -181,8 +181,13 @@
final int pageHeight = mTempRect.height() - mView.getTotalPaddingTop()
- mView.getTotalPaddingBottom();
final int previousPageEndY = currentLineTop - pageHeight;
- final int currentPageStartLine = (previousPageEndY > 0) ?
- mLayout.getLineForVertical(previousPageEndY) + 1 : 0;
+ int currentPageStartLine = (previousPageEndY > 0) ?
+ mLayout.getLineForVertical(previousPageEndY) : 0;
+ // If we're at the end of text, we're at the end of the current line rather than the
+ // start of the next line, so we should move up one fewer lines than we would otherwise.
+ if (end == mText.length() && (currentPageStartLine < currentLine)) {
+ currentPageStartLine += 1;
+ }
final int start = getLineEdgeIndex(currentPageStartLine, DIRECTION_START);
diff --git a/core/java/android/widget/ArrayAdapter.java b/core/java/android/widget/ArrayAdapter.java
index 027f6d6..9d228cf 100644
--- a/core/java/android/widget/ArrayAdapter.java
+++ b/core/java/android/widget/ArrayAdapter.java
@@ -20,6 +20,7 @@
import android.annotation.IdRes;
import android.annotation.LayoutRes;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Resources;
import android.util.Log;
@@ -61,17 +62,13 @@
private final LayoutInflater mInflater;
- /**
- * Contains the list of objects that represent the data of this ArrayAdapter.
- * The content of this list is referred to as "the array" in the documentation.
- */
- private List<T> mObjects;
+ private final Context mContext;
/**
* The resource indicating what views to inflate to display the content of this
* array adapter.
*/
- private int mResource;
+ private final int mResource;
/**
* The resource indicating what views to inflate to display the content of this
@@ -80,7 +77,13 @@
private int mDropDownResource;
/**
- * If the inflated resource is not a TextView, {@link #mFieldId} is used to find
+ * Contains the list of objects that represent the data of this ArrayAdapter.
+ * The content of this list is referred to as "the array" in the documentation.
+ */
+ private List<T> mObjects;
+
+ /**
+ * If the inflated resource is not a TextView, {@code mFieldId} is used to find
* a TextView inside the inflated views hierarchy. This field must contain the
* identifier that matches the one defined in the resource file.
*/
@@ -92,8 +95,6 @@
*/
private boolean mNotifyOnChange = true;
- private Context mContext;
-
// A copy of the original mObjects array, initialized from and then used instead as soon as
// the mFilter ArrayFilter is used. mObjects will then only contain the filtered values.
private ArrayList<T> mOriginalValues;
@@ -109,8 +110,8 @@
* @param resource The resource ID for a layout file containing a TextView to use when
* instantiating views.
*/
- public ArrayAdapter(Context context, @LayoutRes int resource) {
- this(context, resource, 0, new ArrayList<T>());
+ public ArrayAdapter(@NonNull Context context, @LayoutRes int resource) {
+ this(context, resource, 0, new ArrayList<>());
}
/**
@@ -121,8 +122,9 @@
* instantiating views.
* @param textViewResourceId The id of the TextView within the layout resource to be populated
*/
- public ArrayAdapter(Context context, @LayoutRes int resource, @IdRes int textViewResourceId) {
- this(context, resource, textViewResourceId, new ArrayList<T>());
+ public ArrayAdapter(@NonNull Context context, @LayoutRes int resource,
+ @IdRes int textViewResourceId) {
+ this(context, resource, textViewResourceId, new ArrayList<>());
}
/**
@@ -133,7 +135,7 @@
* instantiating views.
* @param objects The objects to represent in the ListView.
*/
- public ArrayAdapter(Context context, @LayoutRes int resource, @NonNull T[] objects) {
+ public ArrayAdapter(@NonNull Context context, @LayoutRes int resource, @NonNull T[] objects) {
this(context, resource, 0, Arrays.asList(objects));
}
@@ -146,8 +148,8 @@
* @param textViewResourceId The id of the TextView within the layout resource to be populated
* @param objects The objects to represent in the ListView.
*/
- public ArrayAdapter(Context context, @LayoutRes int resource, @IdRes int textViewResourceId,
- @NonNull T[] objects) {
+ public ArrayAdapter(@NonNull Context context, @LayoutRes int resource,
+ @IdRes int textViewResourceId, @NonNull T[] objects) {
this(context, resource, textViewResourceId, Arrays.asList(objects));
}
@@ -159,7 +161,8 @@
* instantiating views.
* @param objects The objects to represent in the ListView.
*/
- public ArrayAdapter(Context context, @LayoutRes int resource, @NonNull List<T> objects) {
+ public ArrayAdapter(@NonNull Context context, @LayoutRes int resource,
+ @NonNull List<T> objects) {
this(context, resource, 0, objects);
}
@@ -172,8 +175,8 @@
* @param textViewResourceId The id of the TextView within the layout resource to be populated
* @param objects The objects to represent in the ListView.
*/
- public ArrayAdapter(Context context, @LayoutRes int resource, @IdRes int textViewResourceId,
- @NonNull List<T> objects) {
+ public ArrayAdapter(@NonNull Context context, @LayoutRes int resource,
+ @IdRes int textViewResourceId, @NonNull List<T> objects) {
mContext = context;
mInflater = LayoutInflater.from(context);
mResource = mDropDownResource = resource;
@@ -186,7 +189,7 @@
*
* @param object The object to add at the end of the array.
*/
- public void add(T object) {
+ public void add(@Nullable T object) {
synchronized (mLock) {
if (mOriginalValues != null) {
mOriginalValues.add(object);
@@ -201,8 +204,17 @@
* Adds the specified Collection at the end of the array.
*
* @param collection The Collection to add at the end of the array.
+ * @throws UnsupportedOperationException if the <tt>addAll</tt> operation
+ * is not supported by this list
+ * @throws ClassCastException if the class of an element of the specified
+ * collection prevents it from being added to this list
+ * @throws NullPointerException if the specified collection contains one
+ * or more null elements and this list does not permit null
+ * elements, or if the specified collection is null
+ * @throws IllegalArgumentException if some property of an element of the
+ * specified collection prevents it from being added to this list
*/
- public void addAll(Collection<? extends T> collection) {
+ public void addAll(@NonNull Collection<? extends T> collection) {
synchronized (mLock) {
if (mOriginalValues != null) {
mOriginalValues.addAll(collection);
@@ -235,7 +247,7 @@
* @param object The object to insert into the array.
* @param index The index at which the object must be inserted.
*/
- public void insert(T object, int index) {
+ public void insert(@Nullable T object, int index) {
synchronized (mLock) {
if (mOriginalValues != null) {
mOriginalValues.add(index, object);
@@ -251,7 +263,7 @@
*
* @param object The object to remove.
*/
- public void remove(T object) {
+ public void remove(@Nullable T object) {
synchronized (mLock) {
if (mOriginalValues != null) {
mOriginalValues.remove(object);
@@ -282,7 +294,7 @@
* @param comparator The comparator used to sort the objects contained
* in this adapter.
*/
- public void sort(Comparator<? super T> comparator) {
+ public void sort(@NonNull Comparator<? super T> comparator) {
synchronized (mLock) {
if (mOriginalValues != null) {
Collections.sort(mOriginalValues, comparator);
@@ -293,9 +305,6 @@
if (mNotifyOnChange) notifyDataSetChanged();
}
- /**
- * {@inheritDoc}
- */
@Override
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
@@ -326,21 +335,17 @@
*
* @return The Context associated with this adapter.
*/
- public Context getContext() {
+ public @NonNull Context getContext() {
return mContext;
}
- /**
- * {@inheritDoc}
- */
+ @Override
public int getCount() {
return mObjects.size();
}
- /**
- * {@inheritDoc}
- */
- public T getItem(int position) {
+ @Override
+ public @Nullable T getItem(int position) {
return mObjects.get(position);
}
@@ -351,28 +356,25 @@
*
* @return The position of the specified item.
*/
- public int getPosition(T item) {
+ public int getPosition(@Nullable T item) {
return mObjects.indexOf(item);
}
- /**
- * {@inheritDoc}
- */
+ @Override
public long getItemId(int position) {
return position;
}
- /**
- * {@inheritDoc}
- */
- public View getView(int position, View convertView, ViewGroup parent) {
+ @Override
+ public @NonNull View getView(int position, @Nullable View convertView,
+ @NonNull ViewGroup parent) {
return createViewFromResource(mInflater, position, convertView, parent, mResource);
}
- private View createViewFromResource(LayoutInflater inflater, int position, View convertView,
- ViewGroup parent, int resource) {
- View view;
- TextView text;
+ private @NonNull View createViewFromResource(@NonNull LayoutInflater inflater, int position,
+ @Nullable View convertView, @NonNull ViewGroup parent, int resource) {
+ final View view;
+ final TextView text;
if (convertView == null) {
view = inflater.inflate(resource, parent, false);
@@ -387,6 +389,12 @@
} else {
// Otherwise, find the TextView field within the layout
text = (TextView) view.findViewById(mFieldId);
+
+ if (text == null) {
+ throw new RuntimeException("Failed to find view with ID "
+ + mContext.getResources().getResourceName(mFieldId)
+ + " in item layout");
+ }
}
} catch (ClassCastException e) {
Log.e("ArrayAdapter", "You must supply a resource ID for a TextView");
@@ -394,9 +402,9 @@
"ArrayAdapter requires the resource ID to be a TextView", e);
}
- T item = getItem(position);
+ final T item = getItem(position);
if (item instanceof CharSequence) {
- text.setText((CharSequence)item);
+ text.setText((CharSequence) item);
} else {
text.setText(item.toString());
}
@@ -426,7 +434,7 @@
* @see #getDropDownView(int, View, ViewGroup)
*/
@Override
- public void setDropDownViewTheme(Resources.Theme theme) {
+ public void setDropDownViewTheme(@Nullable Resources.Theme theme) {
if (theme == null) {
mDropDownInflater = null;
} else if (theme == mInflater.getContext().getTheme()) {
@@ -438,12 +446,13 @@
}
@Override
- public Resources.Theme getDropDownViewTheme() {
+ public @Nullable Resources.Theme getDropDownViewTheme() {
return mDropDownInflater == null ? null : mDropDownInflater.getContext().getTheme();
}
@Override
- public View getDropDownView(int position, View convertView, ViewGroup parent) {
+ public View getDropDownView(int position, @Nullable View convertView,
+ @NonNull ViewGroup parent) {
final LayoutInflater inflater = mDropDownInflater == null ? mInflater : mDropDownInflater;
return createViewFromResource(inflater, position, convertView, parent, mDropDownResource);
}
@@ -458,16 +467,14 @@
*
* @return An ArrayAdapter<CharSequence>.
*/
- public static ArrayAdapter<CharSequence> createFromResource(Context context,
+ public static @NonNull ArrayAdapter<CharSequence> createFromResource(@NonNull Context context,
@ArrayRes int textArrayResId, @LayoutRes int textViewResId) {
- CharSequence[] strings = context.getResources().getTextArray(textArrayResId);
- return new ArrayAdapter<CharSequence>(context, textViewResId, strings);
+ final CharSequence[] strings = context.getResources().getTextArray(textArrayResId);
+ return new ArrayAdapter<>(context, textViewResId, strings);
}
- /**
- * {@inheritDoc}
- */
- public Filter getFilter() {
+ @Override
+ public @NonNull Filter getFilter() {
if (mFilter == null) {
mFilter = new ArrayFilter();
}
@@ -482,31 +489,31 @@
private class ArrayFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence prefix) {
- FilterResults results = new FilterResults();
+ final FilterResults results = new FilterResults();
if (mOriginalValues == null) {
synchronized (mLock) {
- mOriginalValues = new ArrayList<T>(mObjects);
+ mOriginalValues = new ArrayList<>(mObjects);
}
}
if (prefix == null || prefix.length() == 0) {
- ArrayList<T> list;
+ final ArrayList<T> list;
synchronized (mLock) {
- list = new ArrayList<T>(mOriginalValues);
+ list = new ArrayList<>(mOriginalValues);
}
results.values = list;
results.count = list.size();
} else {
- String prefixString = prefix.toString().toLowerCase();
+ final String prefixString = prefix.toString().toLowerCase();
- ArrayList<T> values;
+ final ArrayList<T> values;
synchronized (mLock) {
- values = new ArrayList<T>(mOriginalValues);
+ values = new ArrayList<>(mOriginalValues);
}
final int count = values.size();
- final ArrayList<T> newValues = new ArrayList<T>();
+ final ArrayList<T> newValues = new ArrayList<>();
for (int i = 0; i < count; i++) {
final T value = values.get(i);
@@ -517,11 +524,8 @@
newValues.add(value);
} else {
final String[] words = valueText.split(" ");
- final int wordCount = words.length;
-
- // Start at index 0, in case valueText starts with space(s)
- for (int k = 0; k < wordCount; k++) {
- if (words[k].startsWith(prefixString)) {
+ for (String word : words) {
+ if (word.startsWith(prefixString)) {
newValues.add(value);
break;
}
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index 7d57cb8..6a4e36a 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -1116,7 +1116,7 @@
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
super.onFocusChanged(focused, direction, previouslyFocusedRect);
- if (mTemporaryDetach) {
+ if (isTemporarilyDetached()) {
// If we are temporarily in the detach state, then do nothing.
return;
}
diff --git a/core/java/android/widget/CalendarView.java b/core/java/android/widget/CalendarView.java
index cde7604..66896ab 100644
--- a/core/java/android/widget/CalendarView.java
+++ b/core/java/android/widget/CalendarView.java
@@ -430,9 +430,11 @@
* Sets whether to show the week number.
*
* @param showWeekNumber True to show the week number.
+ * @deprecated No longer used by Material-style CalendarView.
*
* @attr ref android.R.styleable#CalendarView_showWeekNumber
*/
+ @Deprecated
public void setShowWeekNumber(boolean showWeekNumber) {
mDelegate.setShowWeekNumber(showWeekNumber);
}
@@ -441,9 +443,11 @@
* Gets whether to show the week number.
*
* @return True if showing the week number.
+ * @deprecated No longer used by Material-style CalendarView.
*
* @attr ref android.R.styleable#CalendarView_showWeekNumber
*/
+ @Deprecated
public boolean getShowWeekNumber() {
return mDelegate.getShowWeekNumber();
}
diff --git a/core/java/android/widget/CalendarViewLegacyDelegate.java b/core/java/android/widget/CalendarViewLegacyDelegate.java
index 442fb33..f540479 100644
--- a/core/java/android/widget/CalendarViewLegacyDelegate.java
+++ b/core/java/android/widget/CalendarViewLegacyDelegate.java
@@ -404,7 +404,7 @@
@Override
public int getUnfocusedMonthDateColor() {
- return mFocusedMonthDateColor;
+ return mUnfocusedMonthDateColor;
}
@Override
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 6e3dbd8..0c5edc5 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -75,8 +75,6 @@
*/
@Widget
public class DatePicker extends FrameLayout {
- private static final String LOG_TAG = DatePicker.class.getSimpleName();
-
private static final int MODE_SPINNER = 1;
private static final int MODE_CALENDAR = 2;
@@ -341,7 +339,9 @@
*
* @return {@code true} if the calendar view is shown
* @see #getCalendarView()
+ * @deprecated Not supported by Material-style {@code calendar} mode
*/
+ @Deprecated
public boolean getCalendarViewShown() {
return mDelegate.getCalendarViewShown();
}
@@ -349,13 +349,18 @@
/**
* Returns the {@link CalendarView} used by this picker.
* <p>
- * <strong>Note:</strong> This method returns {@code null} when the
+ * <strong>Note:</strong> This method throws an
+ * {@link UnsupportedOperationException} when the
* {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
* to {@code calendar}.
*
* @return the calendar view
* @see #getCalendarViewShown()
+ * @deprecated Not supported by Material-style {@code calendar} mode
+ * @throws UnsupportedOperationException if called when the picker is
+ * displayed in {@code calendar} mode
*/
+ @Deprecated
public CalendarView getCalendarView() {
return mDelegate.getCalendarView();
}
@@ -369,7 +374,9 @@
*
* @param shown {@code true} to show the calendar view, {@code false} to
* hide it
+ * @deprecated Not supported by Material-style {@code calendar} mode
*/
+ @Deprecated
public void setCalendarViewShown(boolean shown) {
mDelegate.setCalendarViewShown(shown);
}
@@ -382,7 +389,9 @@
* to {@code calendar}.
*
* @return {@code true} if the spinners are shown
+ * @deprecated Not supported by Material-style {@code calendar} mode
*/
+ @Deprecated
public boolean getSpinnersShown() {
return mDelegate.getSpinnersShown();
}
@@ -396,7 +405,9 @@
*
* @param shown {@code true} to show the spinners, {@code false} to hide
* them
+ * @deprecated Not supported by Material-style {@code calendar} mode
*/
+ @Deprecated
public void setSpinnersShown(boolean shown) {
mDelegate.setSpinnersShown(shown);
}
diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java
index 5adac01..332e89c 100755
--- a/core/java/android/widget/DatePickerCalendarDelegate.java
+++ b/core/java/android/widget/DatePickerCalendarDelegate.java
@@ -378,9 +378,9 @@
mCurrentDate.set(Calendar.MONTH, monthOfYear);
mCurrentDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
- mDateChangedListener = callBack;
-
onDateChanged(false, false);
+
+ mDateChangedListener = callBack;
}
@Override
diff --git a/core/java/android/widget/DatePickerSpinnerDelegate.java b/core/java/android/widget/DatePickerSpinnerDelegate.java
index 255de79..d8a3c56 100644
--- a/core/java/android/widget/DatePickerSpinnerDelegate.java
+++ b/core/java/android/widget/DatePickerSpinnerDelegate.java
@@ -244,6 +244,7 @@
setDate(year, monthOfYear, dayOfMonth);
updateSpinners();
updateCalendarView();
+
mOnDateChangedListener = onDateChangedListener;
}
diff --git a/core/java/android/widget/DayPickerPagerAdapter.java b/core/java/android/widget/DayPickerPagerAdapter.java
index 8ce2f9c..97936e7 100644
--- a/core/java/android/widget/DayPickerPagerAdapter.java
+++ b/core/java/android/widget/DayPickerPagerAdapter.java
@@ -148,18 +148,22 @@
void setCalendarTextColor(ColorStateList calendarTextColor) {
mCalendarTextColor = calendarTextColor;
+ notifyDataSetChanged();
}
void setDaySelectorColor(ColorStateList selectorColor) {
mDaySelectorColor = selectorColor;
+ notifyDataSetChanged();
}
void setMonthTextAppearance(int resId) {
mMonthTextAppearance = resId;
+ notifyDataSetChanged();
}
void setDayOfWeekTextAppearance(int resId) {
mDayOfWeekTextAppearance = resId;
+ notifyDataSetChanged();
}
int getDayOfWeekTextAppearance() {
@@ -168,6 +172,7 @@
void setDayTextAppearance(int resId) {
mDayTextAppearance = resId;
+ notifyDataSetChanged();
}
int getDayTextAppearance() {
diff --git a/core/java/android/widget/EditText.java b/core/java/android/widget/EditText.java
index 434e3eb..ad35550 100644
--- a/core/java/android/widget/EditText.java
+++ b/core/java/android/widget/EditText.java
@@ -155,6 +155,9 @@
public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
switch (action) {
case AccessibilityNodeInfo.ACTION_SET_TEXT: {
+ if (!isEnabled()) {
+ return false;
+ }
CharSequence text = (arguments != null) ? arguments.getCharSequence(
AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE) : null;
setText(text);
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 942cbcb..440ef21 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -218,7 +218,6 @@
boolean mShowSoftInputOnFocus = true;
private boolean mPreserveSelection;
private boolean mRestartActionModeOnNextRefresh;
- boolean mTemporaryDetach;
boolean mIsBeingLongClicked;
@@ -367,7 +366,6 @@
showError();
mShowErrorAfterAttach = false;
}
- mTemporaryDetach = false;
final ViewTreeObserver observer = mTextView.getViewTreeObserver();
// No need to create the controller.
@@ -429,7 +427,6 @@
hideCursorAndSpanControllers();
stopTextActionModeWithPreservingSelection();
- mTemporaryDetach = false;
}
private void discardTextDisplayLists() {
@@ -1212,7 +1209,7 @@
stopTextActionModeWithPreservingSelection();
} else {
hideCursorAndSpanControllers();
- if (mTemporaryDetach) {
+ if (mTextView.isTemporarilyDetached()) {
stopTextActionModeWithPreservingSelection();
} else {
stopTextActionMode();
@@ -1781,6 +1778,18 @@
if (translate) canvas.translate(0, -cursorOffsetVertical);
}
+ void invalidateHandlesAndActionMode() {
+ if (mSelectionModifierCursorController != null) {
+ mSelectionModifierCursorController.invalidateHandles();
+ }
+ if (mInsertionPointCursorController != null) {
+ mInsertionPointCursorController.invalidateHandle();
+ }
+ if (mTextActionMode != null) {
+ mTextActionMode.invalidate();
+ }
+ }
+
/**
* Invalidates all the sub-display lists that overlap the specified character range
*/
@@ -1866,9 +1875,9 @@
if (hasSelection) {
hideInsertionPointCursorController();
if (mTextActionMode == null) {
- if (mRestartActionModeOnNextRefresh || mTextView.isInExtractedMode()) {
+ if (mRestartActionModeOnNextRefresh) {
// To avoid distraction, newly start action mode only when selection action
- // mode is being restarted or in full screen extracted mode.
+ // mode is being restarted.
startSelectionActionMode();
}
} else if (selectionController == null || !selectionController.isActive()) {
@@ -3465,7 +3474,6 @@
popupBackground.getPadding(mTempRect);
width += mTempRect.left + mTempRect.right;
}
- mSuggestionListView.getLayoutParams().width = width;
mPopupWindow.setWidth(width);
}
@@ -3640,6 +3648,9 @@
}
if (menu.hasVisibleItems() || mode.getCustomView() != null) {
+ if (mHasSelection && !mTextView.hasTransientState()) {
+ mTextView.setHasTransientState(true);
+ }
return true;
} else {
return false;
@@ -4034,14 +4045,17 @@
// Don't update drawable during dragging.
return;
}
+ final Layout layout = mTextView.getLayout();
+ if (layout == null) {
+ return;
+ }
final int offset = getCurrentCursorOffset();
- final boolean isRtlCharAtOffset = mTextView.getLayout().isRtlCharAt(offset);
+ final boolean isRtlCharAtOffset = isAtRtlRun(layout, offset);
final Drawable oldDrawable = mDrawable;
mDrawable = isRtlCharAtOffset ? mDrawableRtl : mDrawableLtr;
mHotspotX = getHotspotX(mDrawable, isRtlCharAtOffset);
mHorizontalGravity = getHorizontalGravity(isRtlCharAtOffset);
- final Layout layout = mTextView.getLayout();
- if (layout != null && oldDrawable != mDrawable && isShowing()) {
+ if (oldDrawable != mDrawable && isShowing()) {
// Update popup window position.
mPositionX = getCursorHorizontalPosition(layout, offset) - mHotspotX -
getHorizontalOffset() + getCursorOffset();
@@ -4101,6 +4115,14 @@
setMeasuredDimension(getPreferredWidth(), getPreferredHeight());
}
+ @Override
+ public void invalidate() {
+ super.invalidate();
+ if (isShowing()) {
+ positionAtCursorOffset(getCurrentCursorOffset(), true);
+ }
+ };
+
private int getPreferredWidth() {
return Math.max(mDrawable.getIntrinsicWidth(), mMinSize);
}
@@ -4154,7 +4176,25 @@
public abstract void updatePosition(float x, float y);
- protected void positionAtCursorOffset(int offset, boolean parentScrolled) {
+ protected boolean isAtRtlRun(@NonNull Layout layout, int offset) {
+ return layout.isRtlCharAt(offset);
+ }
+
+ @VisibleForTesting
+ public float getHorizontal(@NonNull Layout layout, int offset) {
+ return layout.getPrimaryHorizontal(offset);
+ }
+
+ protected int getOffsetAtCoordinate(@NonNull Layout layout, int line, float x) {
+ return mTextView.getOffsetAtCoordinate(line, x);
+ }
+
+ /**
+ * @param offset Cursor offset. Must be in [-1, length].
+ * @param forceUpdatePosition whether to force update the position. This should be true
+ * when If the parent has been scrolled, for example.
+ */
+ protected void positionAtCursorOffset(int offset, boolean forceUpdatePosition) {
// A HandleView relies on the layout, which may be nulled by external methods
Layout layout = mTextView.getLayout();
if (layout == null) {
@@ -4165,7 +4205,7 @@
layout = mTextView.getLayout();
boolean offsetChanged = offset != mPreviousOffset;
- if (offsetChanged || parentScrolled) {
+ if (offsetChanged || forceUpdatePosition) {
if (offsetChanged) {
updateSelection(offset);
addPositionToTouchUpFilter(offset);
@@ -4194,7 +4234,7 @@
* @return The clamped horizontal position for the cursor.
*/
int getCursorHorizontalPosition(Layout layout, int offset) {
- return (int) (layout.getPrimaryHorizontal(offset) - 0.5f);
+ return (int) (getHorizontal(layout, offset) - 0.5f);
}
public void updatePosition(int parentPositionX, int parentPositionY,
@@ -4427,7 +4467,7 @@
int getCursorHorizontalPosition(Layout layout, int offset) {
final Drawable drawable = mCursorCount > 0 ? mCursorDrawable[0] : null;
if (drawable != null) {
- final float horizontal = layout.getPrimaryHorizontal(offset);
+ final float horizontal = getHorizontal(layout, offset);
return clampHorizontalPosition(drawable, horizontal) + mTempRect.left;
}
return super.getCursorHorizontalPosition(layout, offset);
@@ -4499,10 +4539,10 @@
mPreviousLineTouched = mTextView.getLineAtCoordinate(y);
}
int currLine = getCurrentLineAdjustedForSlop(layout, mPreviousLineTouched, y);
- offset = mTextView.getOffsetAtCoordinate(currLine, x);
+ offset = getOffsetAtCoordinate(layout, currLine, x);
mPreviousLineTouched = currLine;
} else {
- offset = mTextView.getOffsetForPosition(x, y);
+ offset = -1;
}
positionAtCursorOffset(offset, false);
if (mTextActionMode != null) {
@@ -4612,14 +4652,14 @@
final int anotherHandleOffset =
isStartHandle() ? mTextView.getSelectionEnd() : mTextView.getSelectionStart();
int currLine = getCurrentLineAdjustedForSlop(layout, mPreviousLineTouched, y);
- int initialOffset = mTextView.getOffsetAtCoordinate(currLine, x);
+ int initialOffset = getOffsetAtCoordinate(layout, currLine, x);
if (isStartHandle() && initialOffset >= anotherHandleOffset
|| !isStartHandle() && initialOffset <= anotherHandleOffset) {
// Handles have crossed, bound it to the first selected line and
// adjust by word / char as normal.
currLine = layout.getLineForOffset(anotherHandleOffset);
- initialOffset = mTextView.getOffsetAtCoordinate(currLine, x);
+ initialOffset = getOffsetAtCoordinate(layout, currLine, x);
}
int offset = initialOffset;
@@ -4631,8 +4671,8 @@
}
final int currentOffset = getCurrentCursorOffset();
- final boolean rtlAtCurrentOffset = layout.isRtlCharAt(currentOffset);
- final boolean atRtl = layout.isRtlCharAt(offset);
+ final boolean rtlAtCurrentOffset = isAtRtlRun(layout, currentOffset);
+ final boolean atRtl = isAtRtlRun(layout, offset);
final boolean isLvlBoundary = layout.isLevelBoundary(offset);
// We can't determine if the user is expanding or shrinking the selection if they're
@@ -4689,14 +4729,15 @@
if (isExpanding) {
// User is increasing the selection.
- final boolean snapToWord = !mInWord
- || (isStartHandle() ? currLine < mPrevLine : currLine > mPrevLine);
+ int wordBoundary = isStartHandle() ? wordStart : wordEnd;
+ final boolean snapToWord = (!mInWord
+ || (isStartHandle() ? currLine < mPrevLine : currLine > mPrevLine))
+ && atRtl == isAtRtlRun(layout, wordBoundary);
if (snapToWord) {
// Sometimes words can be broken across lines (Chinese, hyphenation).
// We still snap to the word boundary but we only use the letters on the
// current line to determine if the user is far enough into the word to snap.
- int wordBoundary = isStartHandle() ? wordStart : wordEnd;
- if (layout != null && layout.getLineForOffset(wordBoundary) != currLine) {
+ if (layout.getLineForOffset(wordBoundary) != currLine) {
wordBoundary = isStartHandle() ?
layout.getLineStart(currLine) : layout.getLineEnd(currLine);
}
@@ -4717,9 +4758,9 @@
offset = mPreviousOffset;
}
}
- if (layout != null && (isStartHandle() && offset < initialOffset)
+ if ((isStartHandle() && offset < initialOffset)
|| (!isStartHandle() && offset > initialOffset)) {
- final float adjustedX = layout.getPrimaryHorizontal(offset);
+ final float adjustedX = getHorizontal(layout, offset);
mTouchWordDelta =
mTextView.convertToLocalHorizontalCoordinate(x) - adjustedX;
} else {
@@ -4728,7 +4769,7 @@
positionCursor = true;
} else {
final int adjustedOffset =
- mTextView.getOffsetAtCoordinate(currLine, x - mTouchWordDelta);
+ getOffsetAtCoordinate(layout, currLine, x - mTouchWordDelta);
final boolean shrinking = isStartHandle()
? adjustedOffset > mPreviousOffset || currLine > mPrevLine
: adjustedOffset < mPreviousOffset || currLine < mPrevLine;
@@ -4737,9 +4778,9 @@
if (currLine != mPrevLine) {
// We're on a different line, so we'll snap to word boundaries.
offset = isStartHandle() ? wordStart : wordEnd;
- if (layout != null && (isStartHandle() && offset < initialOffset)
+ if ((isStartHandle() && offset < initialOffset)
|| (!isStartHandle() && offset > initialOffset)) {
- final float adjustedX = layout.getPrimaryHorizontal(offset);
+ final float adjustedX = getHorizontal(layout, offset);
mTouchWordDelta =
mTextView.convertToLocalHorizontalCoordinate(x) - adjustedX;
} else {
@@ -4754,7 +4795,7 @@
// Handle has jumped to the word boundary, and the user is moving
// their finger towards the handle, the delta should be updated.
mTouchWordDelta = mTextView.convertToLocalHorizontalCoordinate(x) -
- layout.getPrimaryHorizontal(mPreviousOffset);
+ getHorizontal(layout, mPreviousOffset);
}
}
@@ -4765,13 +4806,9 @@
mPrevX = x;
}
- /**
- * @param offset Cursor offset. Must be in [-1, length].
- * @param parentScrolled If the parent has been scrolled or not.
- */
@Override
- protected void positionAtCursorOffset(int offset, boolean parentScrolled) {
- super.positionAtCursorOffset(offset, parentScrolled);
+ protected void positionAtCursorOffset(int offset, boolean forceUpdatePosition) {
+ super.positionAtCursorOffset(offset, forceUpdatePosition);
mInWord = (offset != -1) && !getWordIteratorWithText().isBoundary(offset);
}
@@ -4792,9 +4829,32 @@
isStartHandle() ? mTextView.getSelectionEnd() : mTextView.getSelectionStart();
if ((isStartHandle() && offset >= anotherHandleOffset)
|| (!isStartHandle() && offset <= anotherHandleOffset)) {
+ mTouchWordDelta = 0.0f;
+ final Layout layout = mTextView.getLayout();
+ if (layout != null && offset != anotherHandleOffset) {
+ final float horiz = getHorizontal(layout, offset);
+ final float anotherHandleHoriz = getHorizontal(layout, anotherHandleOffset,
+ !isStartHandle());
+ final float currentHoriz = getHorizontal(layout, mPreviousOffset);
+ if (currentHoriz < anotherHandleHoriz && horiz < anotherHandleHoriz
+ || currentHoriz > anotherHandleHoriz && horiz > anotherHandleHoriz) {
+ // This handle passes another one as it crossed a direction boundary.
+ // Don't minimize the selection, but keep the handle at the run boundary.
+ final int currentOffset = getCurrentCursorOffset();
+ final int offsetToGetRunRange = isStartHandle() ?
+ currentOffset : Math.max(currentOffset - 1, 0);
+ final long range = layout.getRunRange(offsetToGetRunRange);
+ if (isStartHandle()) {
+ offset = TextUtils.unpackRangeStartFromLong(range);
+ } else {
+ offset = TextUtils.unpackRangeEndFromLong(range);
+ }
+ positionAtCursorOffset(offset, false);
+ return;
+ }
+ }
// Handles can not cross and selection is at least one character.
offset = getNextCursorOffset(anotherHandleOffset, !isStartHandle());
- mTouchWordDelta = 0.0f;
}
positionAtCursorOffset(offset, false);
}
@@ -4812,6 +4872,50 @@
}
return nearEdge;
}
+
+ @Override
+ protected boolean isAtRtlRun(@NonNull Layout layout, int offset) {
+ final int offsetToCheck = isStartHandle() ? offset : Math.max(offset - 1, 0);
+ return layout.isRtlCharAt(offsetToCheck);
+ }
+
+ @Override
+ public float getHorizontal(@NonNull Layout layout, int offset) {
+ return getHorizontal(layout, offset, isStartHandle());
+ }
+
+ private float getHorizontal(@NonNull Layout layout, int offset, boolean startHandle) {
+ final int line = layout.getLineForOffset(offset);
+ final int offsetToCheck = startHandle ? offset : Math.max(offset - 1, 0);
+ final boolean isRtlChar = layout.isRtlCharAt(offsetToCheck);
+ final boolean isRtlParagraph = layout.getParagraphDirection(line) == -1;
+ return (isRtlChar == isRtlParagraph) ?
+ layout.getPrimaryHorizontal(offset) : layout.getSecondaryHorizontal(offset);
+ }
+
+ @Override
+ protected int getOffsetAtCoordinate(@NonNull Layout layout, int line, float x) {
+ final float localX = mTextView.convertToLocalHorizontalCoordinate(x);
+ final int primaryOffset = layout.getOffsetForHorizontal(line, localX, true);
+ if (!layout.isLevelBoundary(primaryOffset)) {
+ return primaryOffset;
+ }
+ final int secondaryOffset = layout.getOffsetForHorizontal(line, localX, false);
+ final int currentOffset = getCurrentCursorOffset();
+ final int primaryDiff = Math.abs(primaryOffset - currentOffset);
+ final int secondaryDiff = Math.abs(secondaryOffset - currentOffset);
+ if (primaryDiff < secondaryDiff) {
+ return primaryOffset;
+ } else if (primaryDiff > secondaryDiff) {
+ return secondaryOffset;
+ } else {
+ final int offsetToCheck = isStartHandle() ?
+ currentOffset : Math.max(currentOffset - 1, 0);
+ final boolean isRtlChar = layout.isRtlCharAt(offsetToCheck);
+ final boolean isRtlParagraph = layout.getParagraphDirection(line) == -1;
+ return isRtlChar == isRtlParagraph ? primaryOffset : secondaryOffset;
+ }
+ }
}
private int getCurrentLineAdjustedForSlop(Layout layout, int prevLine, float y) {
@@ -4930,6 +5034,12 @@
public boolean isActive() {
return mHandle != null && mHandle.isShowing();
}
+
+ public void invalidateHandle() {
+ if (mHandle != null) {
+ mHandle.invalidate();
+ }
+ }
}
class SelectionModifierCursorController implements CursorController {
@@ -5334,6 +5444,15 @@
public boolean isActive() {
return mStartHandle != null && mStartHandle.isShowing();
}
+
+ public void invalidateHandles() {
+ if (mStartHandle != null) {
+ mStartHandle.invalidate();
+ }
+ if (mEndHandle != null) {
+ mEndHandle.invalidate();
+ }
+ }
}
private class CorrectionHighlighter {
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index fe8916b..9ac4917 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -16,17 +16,15 @@
package android.widget;
-import java.util.ArrayList;
+import com.android.internal.R;
+import android.annotation.AttrRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.StyleRes;
import android.content.Context;
-import android.content.res.ColorStateList;
import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.PorterDuff;
import android.graphics.Rect;
-import android.graphics.Region;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.Gravity;
@@ -36,8 +34,7 @@
import android.view.ViewHierarchyEncoder;
import android.widget.RemoteViews.RemoteView;
-import com.android.internal.R;
-
+import java.util.ArrayList;
/**
* FrameLayout is designed to block out an area on the screen to display
@@ -75,31 +72,29 @@
@ViewDebug.ExportedProperty(category = "padding")
private int mForegroundPaddingBottom = 0;
- private final Rect mSelfBounds = new Rect();
- private final Rect mOverlayBounds = new Rect();
-
- private final ArrayList<View> mMatchParentChildren = new ArrayList<View>(1);
-
- public FrameLayout(Context context) {
+ private final ArrayList<View> mMatchParentChildren = new ArrayList<>(1);
+
+ public FrameLayout(@NonNull Context context) {
super(context);
}
-
- public FrameLayout(Context context, @Nullable AttributeSet attrs) {
+
+ public FrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
- public FrameLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ public FrameLayout(@NonNull Context context, @Nullable AttributeSet attrs,
+ @AttrRes int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
- public FrameLayout(
- Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ public FrameLayout(@NonNull Context context, @Nullable AttributeSet attrs,
+ @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
final TypedArray a = context.obtainStyledAttributes(
- attrs, com.android.internal.R.styleable.FrameLayout, defStyleAttr, defStyleRes);
-
- if (a.getBoolean(com.android.internal.R.styleable.FrameLayout_measureAllChildren, false)) {
+ attrs, R.styleable.FrameLayout, defStyleAttr, defStyleRes);
+
+ if (a.getBoolean(R.styleable.FrameLayout_measureAllChildren, false)) {
setMeasureAllChildren(true);
}
@@ -171,10 +166,6 @@
mPaddingBottom + mForegroundPaddingBottom;
}
-
- /**
- * {@inheritDoc}
- */
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
@@ -264,17 +255,13 @@
}
}
}
-
- /**
- * {@inheritDoc}
- */
+
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
layoutChildren(left, top, right, bottom, false /* no force left gravity */);
}
- void layoutChildren(int left, int top, int right, int bottom,
- boolean forceLeftGravity) {
+ void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
final int count = getChildCount();
final int parentLeft = getPaddingLeftWithForeground();
@@ -378,12 +365,9 @@
return mMeasureAllChildren;
}
- /**
- * {@inheritDoc}
- */
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
- return new FrameLayout.LayoutParams(getContext(), attrs);
+ return new FrameLayout.LayoutParams(getContext(), attrs);
}
@Override
@@ -391,9 +375,6 @@
return false;
}
- /**
- * {@inheritDoc}
- */
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParams;
@@ -431,34 +412,30 @@
* Per-child layout information for layouts that support margins.
* See {@link android.R.styleable#FrameLayout_Layout FrameLayout Layout Attributes}
* for a list of all child view attributes that this class supports.
- *
+ *
* @attr ref android.R.styleable#FrameLayout_Layout_layout_gravity
*/
public static class LayoutParams extends MarginLayoutParams {
/**
* The gravity to apply with the View to which these layout parameters
* are associated.
+ * <p>
+ * The default value is {@code Gravity.TOP | Gravity.START}
*
* @see android.view.Gravity
- *
* @attr ref android.R.styleable#FrameLayout_Layout_layout_gravity
*/
- public int gravity = -1;
+ public int gravity = DEFAULT_CHILD_GRAVITY;
- /**
- * {@inheritDoc}
- */
- public LayoutParams(Context c, AttributeSet attrs) {
+ public LayoutParams(@NonNull Context c, @Nullable AttributeSet attrs) {
super(c, attrs);
- TypedArray a = c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.FrameLayout_Layout);
- gravity = a.getInt(com.android.internal.R.styleable.FrameLayout_Layout_layout_gravity, -1);
+ final TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.FrameLayout_Layout);
+ gravity = a.getInt(R.styleable.FrameLayout_Layout_layout_gravity,
+ DEFAULT_CHILD_GRAVITY);
a.recycle();
}
- /**
- * {@inheritDoc}
- */
public LayoutParams(int width, int height) {
super(width, height);
}
@@ -468,9 +445,9 @@
* and weight.
*
* @param width the width, either {@link #MATCH_PARENT},
- * {@link #WRAP_CONTENT} or a fixed size in pixels
+ * {@link #WRAP_CONTENT} or a fixed size in pixels
* @param height the height, either {@link #MATCH_PARENT},
- * {@link #WRAP_CONTENT} or a fixed size in pixels
+ * {@link #WRAP_CONTENT} or a fixed size in pixels
* @param gravity the gravity
*
* @see android.view.Gravity
@@ -480,17 +457,11 @@
this.gravity = gravity;
}
- /**
- * {@inheritDoc}
- */
- public LayoutParams(ViewGroup.LayoutParams source) {
+ public LayoutParams(@NonNull ViewGroup.LayoutParams source) {
super(source);
}
- /**
- * {@inheritDoc}
- */
- public LayoutParams(ViewGroup.MarginLayoutParams source) {
+ public LayoutParams(@NonNull ViewGroup.MarginLayoutParams source) {
super(source);
}
@@ -500,7 +471,7 @@
*
* @param source The layout params to copy from.
*/
- public LayoutParams(LayoutParams source) {
+ public LayoutParams(@NonNull LayoutParams source) {
super(source);
this.gravity = source.gravity;
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index f16fdd6..00f368e 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -325,16 +325,24 @@
if (getChildCount() > 0) {
final View child = getChildAt(0);
- int width = getMeasuredWidth();
- if (child.getMeasuredWidth() < width) {
- final FrameLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ final int widthPadding;
+ final int heightPadding;
+ final FrameLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
+ if (targetSdkVersion >= Build.VERSION_CODES.M) {
+ widthPadding = mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin;
+ heightPadding = mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin;
+ } else {
+ widthPadding = mPaddingLeft + mPaddingRight;
+ heightPadding = mPaddingTop + mPaddingBottom;
+ }
- int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, mPaddingTop
- + mPaddingBottom, lp.height);
- width -= mPaddingLeft;
- width -= mPaddingRight;
- int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
-
+ int desiredWidth = getMeasuredWidth() - widthPadding;
+ if (child.getMeasuredWidth() < desiredWidth) {
+ final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
+ desiredWidth, MeasureSpec.EXACTLY);
+ final int childHeightMeasureSpec = getChildMeasureSpec(
+ heightMeasureSpec, heightPadding, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
@@ -1235,17 +1243,17 @@
}
@Override
- protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) {
+ protected void measureChild(View child, int parentWidthMeasureSpec,
+ int parentHeightMeasureSpec) {
ViewGroup.LayoutParams lp = child.getLayoutParams();
- int childWidthMeasureSpec;
- int childHeightMeasureSpec;
+ final int horizontalPadding = mPaddingLeft + mPaddingRight;
+ final int childWidthMeasureSpec = MeasureSpec.makeSafeMeasureSpec(
+ Math.max(0, MeasureSpec.getSize(parentWidthMeasureSpec) - horizontalPadding),
+ MeasureSpec.UNSPECIFIED);
- childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop
- + mPaddingBottom, lp.height);
-
- childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
-
+ final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
+ mPaddingTop + mPaddingBottom, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
@@ -1257,8 +1265,11 @@
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
+ heightUsed, lp.height);
- final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
- lp.leftMargin + lp.rightMargin, MeasureSpec.UNSPECIFIED);
+ final int usedTotal = mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin +
+ widthUsed;
+ final int childWidthMeasureSpec = MeasureSpec.makeSafeMeasureSpec(
+ Math.max(0, MeasureSpec.getSize(parentWidthMeasureSpec) - usedTotal),
+ MeasureSpec.UNSPECIFIED);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 0206577..222a040 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -911,17 +911,11 @@
}
if (mDrawable != null) {
- // It's possible for this method to be invoked from the constructor before
- // subclass constructors have run. Drawables can and should trigger invalidations
- // and other activity with their callback on visibility changes, which shouldn't
- // happen before subclass constructors finish. However, we won't have set the
- // drawable as visible until the view becomes attached. This guard below keeps
- // multiple calls to this method from constructors from causing issues.
- if (mDrawable.isVisible()) {
- mDrawable.setVisible(false, false);
- }
mDrawable.setCallback(null);
unscheduleDrawable(mDrawable);
+ if (isAttachedToWindow()) {
+ mDrawable.setVisible(false, false);
+ }
}
mDrawable = d;
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 5b4a368..bfc87f2 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -16,15 +16,14 @@
package android.widget;
-import android.annotation.Nullable;
-import android.os.Bundle;
-import android.os.Trace;
+import com.google.android.collect.Lists;
+
import com.android.internal.R;
import com.android.internal.util.Predicate;
-import com.google.android.collect.Lists;
import android.annotation.IdRes;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
@@ -33,6 +32,8 @@
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.Trace;
import android.util.AttributeSet;
import android.util.MathUtils;
import android.util.SparseBooleanArray;
@@ -1106,20 +1107,63 @@
}
private class FocusSelector implements Runnable {
+ // the selector is waiting to set selection on the list view
+ private static final int STATE_SET_SELECTION = 1;
+ // the selector set the selection on the list view, waiting for a layoutChildren pass
+ private static final int STATE_WAIT_FOR_LAYOUT = 2;
+ // the selector's selection has been honored and it is waiting to request focus on the
+ // target child.
+ private static final int STATE_REQUEST_FOCUS = 3;
+
+ private int mAction;
private int mPosition;
private int mPositionTop;
-
- public FocusSelector setup(int position, int top) {
+
+ FocusSelector setupForSetSelection(int position, int top) {
mPosition = position;
mPositionTop = top;
+ mAction = STATE_SET_SELECTION;
return this;
}
-
+
public void run() {
- setSelectionFromTop(mPosition, mPositionTop);
+ if (mAction == STATE_SET_SELECTION) {
+ setSelectionFromTop(mPosition, mPositionTop);
+ mAction = STATE_WAIT_FOR_LAYOUT;
+ } else if (mAction == STATE_REQUEST_FOCUS) {
+ final int childIndex = mPosition - mFirstPosition;
+ final View child = getChildAt(childIndex);
+ if (child != null) {
+ child.requestFocus();
+ }
+ mAction = -1;
+ }
+ }
+
+ @Nullable Runnable setupFocusIfValid(int position) {
+ if (mAction != STATE_WAIT_FOR_LAYOUT || position != mPosition) {
+ return null;
+ }
+ mAction = STATE_REQUEST_FOCUS;
+ return this;
+ }
+
+ void onLayoutComplete() {
+ if (mAction == STATE_WAIT_FOR_LAYOUT) {
+ mAction = -1;
+ }
}
}
-
+
+ @Override
+ protected void onDetachedFromWindow() {
+ if (mFocusSelector != null) {
+ removeCallbacks(mFocusSelector);
+ mFocusSelector = null;
+ }
+ super.onDetachedFromWindow();
+ }
+
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
if (getChildCount() > 0) {
@@ -1132,7 +1176,7 @@
if (mFocusSelector == null) {
mFocusSelector = new FocusSelector();
}
- post(mFocusSelector.setup(childPosition, top));
+ post(mFocusSelector.setupForSetSelection(childPosition, top));
}
}
super.onSizeChanged(w, h, oldw, oldh);
@@ -1629,7 +1673,7 @@
focusLayoutRestoreView = findFocus();
if (focusLayoutRestoreView != null) {
// Tell it we are going to mess with it.
- focusLayoutRestoreView.onStartTemporaryDetach();
+ focusLayoutRestoreView.dispatchStartTemporaryDetach();
}
}
requestFocus();
@@ -1672,7 +1716,21 @@
adjustViewsUpOrDown();
break;
case LAYOUT_SPECIFIC:
- sel = fillSpecific(reconcileSelectedPosition(), mSpecificTop);
+ final int selectedPosition = reconcileSelectedPosition();
+ sel = fillSpecific(selectedPosition, mSpecificTop);
+ /**
+ * When ListView is resized, FocusSelector requests an async selection for the
+ * previously focused item to make sure it is still visible. If the item is not
+ * selectable, it won't regain focus so instead we call FocusSelector
+ * to directly request focus on the view after it is visible.
+ */
+ if (sel == null && mFocusSelector != null) {
+ final Runnable focusRunnable = mFocusSelector
+ .setupFocusIfValid(selectedPosition);
+ if (focusRunnable != null) {
+ post(focusRunnable);
+ }
+ }
break;
case LAYOUT_MOVE_SELECTION:
sel = moveSelection(oldSel, newSel, delta, childrenTop, childrenBottom);
@@ -1792,7 +1850,7 @@
// our view hierarchy.
if (focusLayoutRestoreView != null
&& focusLayoutRestoreView.getWindowToken() != null) {
- focusLayoutRestoreView.onFinishTemporaryDetach();
+ focusLayoutRestoreView.dispatchFinishTemporaryDetach();
}
mLayoutMode = LAYOUT_NORMAL;
@@ -1812,6 +1870,9 @@
invokeOnItemScrollListener();
} finally {
+ if (mFocusSelector != null) {
+ mFocusSelector.onLayoutComplete();
+ }
if (!blockLayoutRequests) {
mBlockLayoutRequests = false;
}
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index d1b5fc8..0d8d8ed 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -129,8 +129,8 @@
*/
private static final int ANIMATION_STYLE_DEFAULT = -1;
- private final int[] mDrawingLocation = new int[2];
- private final int[] mScreenLocation = new int[2];
+ private final int[] mTmpDrawingLocation = new int[2];
+ private final int[] mTmpScreenLocation = new int[2];
private final Rect mTempRect = new Rect();
private Context mContext;
@@ -173,9 +173,6 @@
private int mHeight = LayoutParams.WRAP_CONTENT;
private int mLastHeight;
- private int mPopupWidth;
- private int mPopupHeight;
-
private float mElevation;
private Drawable mBackground;
@@ -222,7 +219,7 @@
mDecorView.getLayoutParams();
updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
- mAnchoredGravity));
+ p.width, p.height, mAnchoredGravity));
update(p.x, p.y, -1, -1, true);
}
}
@@ -1123,7 +1120,7 @@
TransitionManager.endTransitions(mDecorView);
- unregisterForViewTreeChanges();
+ detachFromAnchor();
mIsShowing = true;
mIsDropdown = false;
@@ -1206,7 +1203,7 @@
TransitionManager.endTransitions(mDecorView);
- registerForViewTreeChanges(anchor, xoff, yoff, gravity);
+ attachToAnchor(anchor, xoff, yoff, gravity);
mIsShowing = true;
mIsDropdown = true;
@@ -1214,8 +1211,10 @@
final WindowManager.LayoutParams p = createPopupLayoutParams(anchor.getWindowToken());
preparePopup(p);
- final boolean aboveAnchor = findDropDownPosition(anchor, p, xoff, yoff, gravity);
+ final boolean aboveAnchor = findDropDownPosition(anchor, p, xoff, yoff,
+ p.width, p.height, gravity);
updateAboveAnchor(aboveAnchor);
+ p.accessibilityIdOfAnchor = (anchor != null) ? anchor.getAccessibilityViewId() : -1;
invokePopup(p);
}
@@ -1297,8 +1296,6 @@
mPopupViewInitialLayoutDirectionInherited =
(mContentView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
- mPopupWidth = p.width;
- mPopupHeight = p.height;
}
/**
@@ -1494,122 +1491,196 @@
* to reclaim space. If scrolling is not possible or not enough, the popup
* window gets moved on top of the anchor.
* <p>
- * The height must have been set on the layout parameters prior to calling
- * this method.
+ * The results of positioning are placed in {@code outParams}.
*
* @param anchor the view on which the popup window must be anchored
- * @param p the layout parameters used to display the drop down
- * @param xoff horizontal offset used to adjust for background padding
- * @param yoff vertical offset used to adjust for background padding
+ * @param outParams the layout parameters used to display the drop down
+ * @param xOffset absolute horizontal offset from the left of the anchor
+ * @param yOffset absolute vertical offset from the top of the anchor
* @param gravity horizontal gravity specifying popup alignment
* @return true if the popup is translated upwards to fit on screen
*/
- private boolean findDropDownPosition(View anchor, WindowManager.LayoutParams p, int xoff,
- int yoff, int gravity) {
+ private boolean findDropDownPosition(View anchor, WindowManager.LayoutParams outParams,
+ int xOffset, int yOffset, int width, int height, int gravity) {
final int anchorHeight = anchor.getHeight();
final int anchorWidth = anchor.getWidth();
if (mOverlapAnchor) {
- yoff -= anchorHeight;
+ yOffset -= anchorHeight;
}
- anchor.getLocationInWindow(mDrawingLocation);
- p.x = mDrawingLocation[0] + xoff;
- p.y = mDrawingLocation[1] + anchorHeight + yoff;
+ // Initially, align to the bottom-left corner of the anchor plus offsets.
+ final int[] drawingLocation = mTmpDrawingLocation;
+ anchor.getLocationInWindow(drawingLocation);
+ outParams.x = drawingLocation[0] + xOffset;
+ outParams.y = drawingLocation[1] + anchorHeight + yOffset;
+ // If we need to adjust for gravity RIGHT, align to the bottom-right
+ // corner of the anchor (still accounting for offsets).
final int hgrav = Gravity.getAbsoluteGravity(gravity, anchor.getLayoutDirection())
& Gravity.HORIZONTAL_GRAVITY_MASK;
if (hgrav == Gravity.RIGHT) {
- // Flip the location to align the right sides of the popup and
- // anchor instead of left.
- p.x -= mPopupWidth - anchorWidth;
+ outParams.x -= width - anchorWidth;
}
- boolean onTop = false;
+ // Let the window manager know to align the top to y.
+ outParams.gravity = Gravity.LEFT | Gravity.TOP;
+ outParams.width = width;
+ outParams.height = height;
- p.gravity = Gravity.LEFT | Gravity.TOP;
+ final int[] screenLocation = mTmpScreenLocation;
+ anchor.getLocationOnScreen(screenLocation);
- anchor.getLocationOnScreen(mScreenLocation);
final Rect displayFrame = new Rect();
anchor.getWindowVisibleDisplayFrame(displayFrame);
- final int screenY = mScreenLocation[1] + anchorHeight + yoff;
- final View root = anchor.getRootView();
- if (screenY + mPopupHeight > displayFrame.bottom
- || p.x + mPopupWidth - root.getWidth() > 0) {
- // If the drop down disappears at the bottom of the screen, we try
- // to scroll a parent scrollview or move the drop down back up on
- // top of the edit box.
- if (mAllowScrollingAnchorParent) {
- final int scrollX = anchor.getScrollX();
- final int scrollY = anchor.getScrollY();
- final Rect r = new Rect(scrollX, scrollY, scrollX + mPopupWidth + xoff,
- scrollY + mPopupHeight + anchorHeight + yoff);
- anchor.requestRectangleOnScreen(r, true);
- }
+ // First, attempt to fit the popup vertically without resizing.
+ final boolean fitsVertical = tryFitVertical(outParams, yOffset, height,
+ anchorHeight, drawingLocation[1], screenLocation[1], displayFrame.top,
+ displayFrame.bottom, false);
- // Now we re-evaluate the space available, and decide from that
- // whether the pop-up will go above or below the anchor.
- anchor.getLocationInWindow(mDrawingLocation);
- p.x = mDrawingLocation[0] + xoff;
- p.y = mDrawingLocation[1] + anchorHeight + yoff;
+ // Next, attempt to fit the popup horizontally without resizing.
+ final boolean fitsHorizontal = tryFitHorizontal(outParams, xOffset, width,
+ anchorWidth, drawingLocation[0], screenLocation[0], displayFrame.left,
+ displayFrame.right, false);
- // Preserve the gravity adjustment.
- if (hgrav == Gravity.RIGHT) {
- p.x -= mPopupWidth - anchorWidth;
- }
+ // If the popup still doesn't fit, attempt to scroll the parent.
+ if (!fitsVertical || !fitsHorizontal) {
+ final int scrollX = anchor.getScrollX();
+ final int scrollY = anchor.getScrollY();
+ final Rect r = new Rect(scrollX, scrollY, scrollX + width + xOffset,
+ scrollY + height + anchorHeight + yOffset);
+ if (mAllowScrollingAnchorParent && anchor.requestRectangleOnScreen(r, true)) {
+ // Reset for the new anchor position.
+ anchor.getLocationInWindow(drawingLocation);
+ outParams.x = drawingLocation[0] + xOffset;
+ outParams.y = drawingLocation[1] + anchorHeight + yOffset;
- // Determine whether there is more space above or below the anchor.
- anchor.getLocationOnScreen(mScreenLocation);
- onTop = (displayFrame.bottom - mScreenLocation[1] - anchorHeight - yoff) <
- (mScreenLocation[1] - yoff - displayFrame.top);
- if (!mOverlapAnchor) {
- if (onTop) {
- p.gravity = Gravity.LEFT | Gravity.BOTTOM;
- p.y = root.getHeight() - mDrawingLocation[1] + yoff;
- } else {
- p.y = mDrawingLocation[1] + anchorHeight + yoff;
+ // Preserve the gravity adjustment.
+ if (hgrav == Gravity.RIGHT) {
+ outParams.x -= width - anchorWidth;
}
}
+
+ // Try to fit the popup again and allowing resizing.
+ tryFitVertical(outParams, yOffset, height, anchorHeight, drawingLocation[1],
+ screenLocation[1], displayFrame.top, displayFrame.bottom, mClipToScreen);
+ tryFitHorizontal(outParams, xOffset, width, anchorWidth, drawingLocation[0],
+ screenLocation[0], displayFrame.left, displayFrame.right, mClipToScreen);
}
- if (mClipToScreen) {
- final int winOffsetX = mScreenLocation[0] - mDrawingLocation[0];
- final int winOffsetY = mScreenLocation[1] - mDrawingLocation[1];
- p.x += winOffsetX;
- p.y += winOffsetY;
- final int displayFrameWidth = displayFrame.right - displayFrame.left;
- final int right = p.x + p.width;
- if (right > displayFrame.right) {
- p.x -= right - displayFrame.right;
- }
+ // Return whether the popup's top edge is above the anchor's top edge.
+ return outParams.y < drawingLocation[1];
+ }
- if (p.x < displayFrame.left) {
- p.x = displayFrame.left;
- p.width = Math.min(p.width, displayFrameWidth);
- }
+ private boolean tryFitVertical(@NonNull LayoutParams outParams, int yOffset, int height,
+ int anchorHeight, int drawingLocationY, int screenLocationY, int displayFrameTop,
+ int displayFrameBottom, boolean allowResize) {
+ final int anchorTopInScreen = screenLocationY + anchorHeight + yOffset;
+ final int spaceBelow = displayFrameBottom - anchorTopInScreen;
+ if (height <= spaceBelow) {
+ return true;
+ }
+ final int spaceAbove = displayFrameTop + anchorTopInScreen - anchorHeight;
+ if (height <= spaceAbove) {
+ // Move everything up.
if (mOverlapAnchor) {
- final int bottom = p.y + p.height;
- if (bottom > displayFrame.bottom) {
- p.y -= bottom - displayFrame.bottom;
- }
- } else {
- if (onTop) {
- final int popupTop = mScreenLocation[1] + yoff - mPopupHeight;
- if (popupTop < 0) {
- p.y += popupTop;
- }
- } else {
- p.y = Math.max(p.y, displayFrame.top);
- }
+ yOffset += anchorHeight;
}
- p.x -= winOffsetX;
- p.y -= winOffsetY;
+ outParams.y = drawingLocationY - height + yOffset;
+
+ return true;
}
- p.gravity |= Gravity.DISPLAY_CLIP_VERTICAL;
+ if (positionInDisplayVertical(outParams, height, drawingLocationY, screenLocationY,
+ displayFrameTop, displayFrameBottom, allowResize)) {
+ return true;
+ }
- return onTop;
+ return false;
+ }
+
+ private boolean positionInDisplayVertical(@NonNull LayoutParams outParams, int height,
+ int drawingLocationY, int screenLocationY, int displayFrameTop, int displayFrameBottom,
+ boolean canResize) {
+ boolean fitsInDisplay = true;
+
+ final int winOffsetY = screenLocationY - drawingLocationY;
+ outParams.y += winOffsetY;
+ outParams.height = height;
+
+ final int bottom = outParams.y + height;
+ if (bottom > displayFrameBottom) {
+ // The popup is too far down, move it back in.
+ outParams.y -= bottom - displayFrameBottom;
+ }
+
+ if (outParams.y < displayFrameTop) {
+ // The popup is too far up, move it back in and clip if
+ // it's still too large.
+ outParams.y = displayFrameTop;
+
+ final int displayFrameHeight = displayFrameBottom - displayFrameTop;
+ if (canResize && height > displayFrameHeight) {
+ outParams.height = displayFrameHeight;
+ } else {
+ fitsInDisplay = false;
+ }
+ }
+
+ outParams.y -= winOffsetY;
+
+ return fitsInDisplay;
+ }
+
+ private boolean tryFitHorizontal(@NonNull LayoutParams outParams, int xOffset, int width,
+ int anchorWidth, int drawingLocationX, int screenLocationX, int displayFrameLeft,
+ int displayFrameRight, boolean allowResize) {
+ final int anchorLeftInScreen = screenLocationX + xOffset;
+ final int spaceRight = displayFrameRight - anchorLeftInScreen;
+ if (width <= spaceRight) {
+ return true;
+ }
+
+ if (positionInDisplayHorizontal(outParams, width, drawingLocationX, screenLocationX,
+ displayFrameLeft, displayFrameRight, allowResize)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private boolean positionInDisplayHorizontal(@NonNull LayoutParams outParams, int width,
+ int drawingLocationX, int screenLocationX, int displayFrameLeft, int displayFrameRight,
+ boolean canResize) {
+ boolean fitsInDisplay = true;
+
+ // Use screen coordinates for comparison against display frame.
+ final int winOffsetX = screenLocationX - drawingLocationX;
+ outParams.x += winOffsetX;
+
+ final int right = outParams.x + width;
+ if (right > displayFrameRight) {
+ // The popup is too far right, move it back in.
+ outParams.x -= right - displayFrameRight;
+ }
+
+ if (outParams.x < displayFrameLeft) {
+ // The popup is too far left, move it back in and clip if it's
+ // still too large.
+ outParams.x = displayFrameLeft;
+
+ final int displayFrameWidth = displayFrameRight - displayFrameLeft;
+ if (canResize && width > displayFrameWidth) {
+ outParams.width = displayFrameWidth;
+ } else {
+ fitsInDisplay = false;
+ }
+ }
+
+ outParams.x -= winOffsetX;
+
+ return fitsInDisplay;
}
/**
@@ -1665,7 +1736,7 @@
anchor.getWindowVisibleDisplayFrame(displayFrame);
}
- final int[] anchorPos = mDrawingLocation;
+ final int[] anchorPos = mTmpDrawingLocation;
anchor.getLocationOnScreen(anchorPos);
final int bottomEdge = displayFrame.bottom;
@@ -1754,7 +1825,7 @@
}
// Clears the anchor view.
- unregisterForViewTreeChanges();
+ detachFromAnchor();
if (mOnDismissListener != null) {
mOnDismissListener.onDismiss();
@@ -1970,6 +2041,13 @@
update = true;
}
+ int newAccessibilityIdOfAnchor =
+ (mAnchor != null) ? mAnchor.get().getAccessibilityViewId() : -1;
+ if (newAccessibilityIdOfAnchor != p.accessibilityIdOfAnchor) {
+ p.accessibilityIdOfAnchor = newAccessibilityIdOfAnchor;
+ update = true;
+ }
+
if (update) {
setLayoutDirectionFromAnchor();
mWindowManager.updateViewLayout(mDecorView, p);
@@ -1987,7 +2065,7 @@
* @param height the new height, must be >= 0 or -1 to ignore
*/
public void update(View anchor, int width, int height) {
- update(anchor, false, 0, 0, true, width, height);
+ update(anchor, false, 0, 0, width, height);
}
/**
@@ -2007,54 +2085,51 @@
* @param height the new height, must be >= 0 or -1 to ignore
*/
public void update(View anchor, int xoff, int yoff, int width, int height) {
- update(anchor, true, xoff, yoff, true, width, height);
+ update(anchor, true, xoff, yoff, width, height);
}
private void update(View anchor, boolean updateLocation, int xoff, int yoff,
- boolean updateDimension, int width, int height) {
+ int width, int height) {
if (!isShowing() || mContentView == null) {
return;
}
final WeakReference<View> oldAnchor = mAnchor;
+ final int gravity = mAnchoredGravity;
+
final boolean needsUpdate = updateLocation && (mAnchorXoff != xoff || mAnchorYoff != yoff);
if (oldAnchor == null || oldAnchor.get() != anchor || (needsUpdate && !mIsDropdown)) {
- registerForViewTreeChanges(anchor, xoff, yoff, mAnchoredGravity);
+ attachToAnchor(anchor, xoff, yoff, gravity);
} else if (needsUpdate) {
// No need to register again if this is a DropDown, showAsDropDown already did.
mAnchorXoff = xoff;
mAnchorYoff = yoff;
}
- final WindowManager.LayoutParams p =
- (WindowManager.LayoutParams) mDecorView.getLayoutParams();
+ final LayoutParams p = (LayoutParams) mDecorView.getLayoutParams();
+ final int oldGravity = p.gravity;
+ final int oldWidth = p.width;
+ final int oldHeight = p.height;
+ final int oldX = p.x;
+ final int oldY = p.y;
- if (updateDimension) {
- if (width == -1) {
- width = mPopupWidth;
- } else {
- mPopupWidth = width;
- p.width = width;
- }
- if (height == -1) {
- height = mPopupHeight;
- } else {
- mPopupHeight = height;
- p.height = height;
- }
+ // If an explicit width/height has not specified, use the most recent
+ // explicitly specified value (either from setWidth/Height or update).
+ if (width == -1) {
+ width = mWidth;
+ }
+ if (height == -1) {
+ height = mHeight;
}
- final int x = p.x;
- final int y = p.y;
- if (updateLocation) {
- updateAboveAnchor(findDropDownPosition(anchor, p, xoff, yoff, mAnchoredGravity));
- } else {
- updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
- mAnchoredGravity));
- }
+ final boolean aboveAnchor = findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
+ width, height, gravity);
+ updateAboveAnchor(aboveAnchor);
- update(p.x, p.y, width, height, x != p.x || y != p.y);
+ final boolean paramsChanged = oldGravity != p.gravity || oldX != p.x || oldY != p.y
+ || oldWidth != p.width || oldHeight != p.height;
+ update(p.x, p.y, p.width, p.height, paramsChanged);
}
/**
@@ -2067,7 +2142,7 @@
public void onDismiss();
}
- private void unregisterForViewTreeChanges() {
+ private void detachFromAnchor() {
final View anchor = mAnchor != null ? mAnchor.get() : null;
if (anchor != null) {
final ViewTreeObserver vto = anchor.getViewTreeObserver();
@@ -2084,8 +2159,8 @@
mIsAnchorRootAttached = false;
}
- private void registerForViewTreeChanges(View anchor, int xoff, int yoff, int gravity) {
- unregisterForViewTreeChanges();
+ private void attachToAnchor(View anchor, int xoff, int yoff, int gravity) {
+ detachFromAnchor();
final ViewTreeObserver vto = anchor.getViewTreeObserver();
if (vto != null) {
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 8bd63df..6d2cea6 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -17,6 +17,7 @@
package android.widget;
import android.annotation.ColorInt;
+import android.app.ActivityManager.StackId;
import android.app.ActivityOptions;
import android.app.ActivityThread;
import android.app.Application;
@@ -228,6 +229,11 @@
public boolean onClickHandler(View view, PendingIntent pendingIntent,
Intent fillInIntent) {
+ return onClickHandler(view, pendingIntent, fillInIntent, StackId.INVALID_STACK_ID);
+ }
+
+ public boolean onClickHandler(View view, PendingIntent pendingIntent,
+ Intent fillInIntent, int launchStackId) {
try {
// TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
Context context = view.getContext();
@@ -239,6 +245,10 @@
0, 0,
view.getMeasuredWidth(), view.getMeasuredHeight());
}
+
+ if (launchStackId != StackId.INVALID_STACK_ID) {
+ opts.setLaunchStackId(launchStackId);
+ }
context.startIntentSender(
pendingIntent.getIntentSender(), fillInIntent,
Intent.FLAG_ACTIVITY_NEW_TASK,
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 3f7a07b..0555cd4 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -350,24 +350,24 @@
if (getChildCount() > 0) {
final View child = getChildAt(0);
- final int height = getMeasuredHeight();
- if (child.getMeasuredHeight() < height) {
- final int widthPadding;
- final int heightPadding;
- final FrameLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams();
- final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
- if (targetSdkVersion >= VERSION_CODES.M) {
- widthPadding = mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin;
- heightPadding = mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin;
- } else {
- widthPadding = mPaddingLeft + mPaddingRight;
- heightPadding = mPaddingTop + mPaddingBottom;
- }
+ final int widthPadding;
+ final int heightPadding;
+ final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
+ final FrameLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ if (targetSdkVersion >= VERSION_CODES.M) {
+ widthPadding = mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin;
+ heightPadding = mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin;
+ } else {
+ widthPadding = mPaddingLeft + mPaddingRight;
+ heightPadding = mPaddingTop + mPaddingBottom;
+ }
+ final int desiredHeight = getMeasuredHeight() - heightPadding;
+ if (child.getMeasuredHeight() < desiredHeight) {
final int childWidthMeasureSpec = getChildMeasureSpec(
widthMeasureSpec, widthPadding, lp.width);
final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
- height - heightPadding, MeasureSpec.EXACTLY);
+ desiredHeight, MeasureSpec.EXACTLY);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
@@ -1268,9 +1268,10 @@
childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft
+ mPaddingRight, lp.width);
-
+ final int verticalPadding = mPaddingTop + mPaddingBottom;
childHeightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(
- MeasureSpec.getSize(parentHeightMeasureSpec), MeasureSpec.UNSPECIFIED);
+ Math.max(0, MeasureSpec.getSize(parentHeightMeasureSpec) - verticalPadding),
+ MeasureSpec.UNSPECIFIED);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
@@ -1283,8 +1284,11 @@
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
+ widthUsed, lp.width);
+ final int usedTotal = mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin +
+ heightUsed;
final int childHeightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(
- MeasureSpec.getSize(parentHeightMeasureSpec), MeasureSpec.UNSPECIFIED);
+ Math.max(0, MeasureSpec.getSize(parentHeightMeasureSpec) - usedTotal),
+ MeasureSpec.UNSPECIFIED);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java
index 43cf5a1..ee716df 100644
--- a/core/java/android/widget/SimpleMonthView.java
+++ b/core/java/android/widget/SimpleMonthView.java
@@ -16,6 +16,9 @@
package android.widget;
+import com.android.internal.R;
+import com.android.internal.widget.ExploreByTouchHelper;
+
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -44,14 +47,13 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-import com.android.internal.R;
-import com.android.internal.widget.ExploreByTouchHelper;
-
import java.text.NumberFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Locale;
+import libcore.icu.LocaleData;
+
/**
* A calendar-like view displaying a specified month and the appropriate selectable day numbers
* within the specified month.
@@ -66,7 +68,6 @@
private static final int DEFAULT_WEEK_START = Calendar.SUNDAY;
private static final String MONTH_YEAR_FORMAT = "MMMMy";
- private static final String DAY_OF_WEEK_FORMAT = "EEEEE";
private static final int SELECTED_HIGHLIGHT_ALPHA = 0xB0;
@@ -80,6 +81,7 @@
private final Paint mDayHighlightPaint = new Paint();
private final Paint mDayHighlightSelectorPaint = new Paint();
+ /** Array of single-character weekday labels ordered by column index. */
private final String[] mDayOfWeekLabels = new String[7];
private final Calendar mCalendar;
@@ -120,7 +122,7 @@
*/
private int mToday = DEFAULT_SELECTED_DAY;
- /** The first day of the week (ex. Calendar.SUNDAY). */
+ /** The first day of the week (ex. Calendar.SUNDAY) indexed from one. */
private int mWeekStart = DEFAULT_WEEK_START;
/** The number of days (ex. 28) in the current month. */
@@ -199,13 +201,11 @@
Log.d(LOG_TAG, "mWeekStart => " + mWeekStart);
}
- final Calendar calendar = Calendar.getInstance(mLocale);
- calendar.setFirstDayOfWeek(mWeekStart);
-
- final SimpleDateFormat formatter = new SimpleDateFormat(DAY_OF_WEEK_FORMAT, mLocale);
- for (int i = 0; i < 7; i++) {
- calendar.set(Calendar.DAY_OF_WEEK, i);
- mDayOfWeekLabels[i] = formatter.format(calendar.getTime());
+ // Use tiny (e.g. single-character) weekday names from ICU. The indices
+ // for this list correspond to Calendar days, e.g. SUNDAY is index 1.
+ final String[] tinyWeekdayNames = LocaleData.get(mLocale).tinyWeekdayNames;
+ for (int i = 0; i < DAYS_IN_WEEK; i++) {
+ mDayOfWeekLabels[i] = tinyWeekdayNames[(mWeekStart + i - 1) % DAYS_IN_WEEK + 1];
}
if (DEBUG_WRONG_DATE) {
@@ -662,8 +662,7 @@
colCenterRtl = colCenter;
}
- final int dayOfWeek = (col + mWeekStart) % DAYS_IN_WEEK;
- final String label = mDayOfWeekLabels[dayOfWeek];
+ final String label = mDayOfWeekLabels[col];
canvas.drawText(label, colCenterRtl, rowCenter - halfLineHeight, p);
}
}
@@ -813,6 +812,11 @@
*/
void setMonthParams(int selectedDay, int month, int year, int weekStart, int enabledDayStart,
int enabledDayEnd) {
+ if (DEBUG_WRONG_DATE) {
+ Log.d(LOG_TAG, "setMonthParams(" + selectedDay + ", " + month + ", " + year + ", "
+ + weekStart + ", " + enabledDayStart + ", " + enabledDayEnd + ")");
+ }
+
mActivatedDay = selectedDay;
if (isValidMonth(month)) {
@@ -849,6 +853,14 @@
mTouchHelper.invalidateRoot();
updateMonthYearLabel();
+
+ if (DEBUG_WRONG_DATE) {
+ Log.d(LOG_TAG, "mMonth = " + mMonth);
+ Log.d(LOG_TAG, "mDayOfWeekStart = " + mDayOfWeekStart);
+ Log.d(LOG_TAG, "mWeekStart = " + mWeekStart);
+ Log.d(LOG_TAG, "mDaysInMonth = " + mDaysInMonth);
+ Log.d(LOG_TAG, "mToday = " + mToday);
+ }
}
private static int getDaysInMonth(int month, int year) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 3195097..48fd58b 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -17,6 +17,7 @@
package android.widget;
import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
+
import android.R;
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
@@ -329,10 +330,6 @@
private int mCurTextColor;
private int mCurHintTextColor;
private boolean mFreezesText;
- private boolean mDispatchTemporaryDetach;
-
- /** Whether this view is temporarily detached from the parent view. */
- boolean mTemporaryDetach;
private Editable.Factory mEditableFactory = Editable.Factory.getInstance();
private Spannable.Factory mSpannableFactory = Spannable.Factory.getInstance();
@@ -2892,6 +2889,11 @@
public void setTextLocale(@NonNull Locale locale) {
mLocalesChanged = true;
mTextPaint.setTextLocale(locale);
+ if (mLayout != null) {
+ nullLayouts();
+ requestLayout();
+ invalidate();
+ }
}
/**
@@ -2908,6 +2910,11 @@
public void setTextLocales(@NonNull @Size(min=1) LocaleList locales) {
mLocalesChanged = true;
mTextPaint.setTextLocales(locales);
+ if (mLayout != null) {
+ nullLayouts();
+ requestLayout();
+ invalidate();
+ }
}
@Override
@@ -2915,6 +2922,11 @@
super.onConfigurationChanged(newConfig);
if (!mLocalesChanged) {
mTextPaint.setTextLocales(LocaleList.getDefault());
+ if (mLayout != null) {
+ nullLayouts();
+ requestLayout();
+ invalidate();
+ }
}
}
@@ -3115,10 +3127,15 @@
}
/**
+ * Returns the font feature settings. The format is the same as the CSS
+ * font-feature-settings attribute:
+ * <a href="http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings">
+ * http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings</a>
+ *
* @return the currently set font feature settings. Default is null.
*
* @see #setFontFeatureSettings(String)
- * @see Paint#setFontFeatureSettings
+ * @see Paint#setFontFeatureSettings(String) Paint.setFontFeatureSettings(String)
*/
@Nullable
public String getFontFeatureSettings() {
@@ -3182,13 +3199,15 @@
}
/**
- * Sets font feature settings. The format is the same as the CSS
+ * Sets font feature settings. The format is the same as the CSS
* font-feature-settings attribute:
- * http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings
+ * <a href="http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings">
+ * http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings</a>
*
* @param fontFeatureSettings font feature settings represented as CSS compatible string
+ *
* @see #getFontFeatureSettings()
- * @see Paint#getFontFeatureSettings
+ * @see Paint#getFontFeatureSettings() Paint.getFontFeatureSettings()
*
* @attr ref android.R.styleable#TextView_fontFeatureSettings
*/
@@ -3331,7 +3350,10 @@
mShadowColor = color;
// Will change text clip region
- if (mEditor != null) mEditor.invalidateTextDisplayList();
+ if (mEditor != null) {
+ mEditor.invalidateTextDisplayList();
+ mEditor.invalidateHandlesAndActionMode();
+ }
invalidate();
}
@@ -3403,17 +3425,10 @@
}
/**
- * Sets whether the movement method will automatically be set to {@link LinkMovementMethod}
- * after {@link #setText} or {@link #append} is called. The movement method is set if one of the
- * following is true:
- * <ul>
- * <li>{@link #setAutoLinkMask} has been set to nonzero and links are detected in
- * {@link #setText} or {@link #append}.
- * <li>The input for {@link #setText} or {@link #append} contains a {@link ClickableSpan}.
- * </ul>
- *
- * <p>This function does not have an immediate effect, movement method will be set only after a
- * call to {@link #setText} or {@link #append}. The default is true.</p>
+ * Sets whether the movement method will automatically be set to
+ * {@link LinkMovementMethod} if {@link #setAutoLinkMask} has been
+ * set to nonzero and links are detected in {@link #setText}.
+ * The default is true.
*
* @attr ref android.R.styleable#TextView_linksClickable
*/
@@ -3423,14 +3438,10 @@
}
/**
- * Returns whether the movement method will automatically be set to {@link LinkMovementMethod}
- * after {@link #setText} or {@link #append} is called.
- *
- * See {@link #setLinksClickable} for details.
- *
- * <p>The default is true.</p>
- *
- * @see #setLinksClickable
+ * Returns whether the movement method will automatically be set to
+ * {@link LinkMovementMethod} if {@link #setAutoLinkMask} has been
+ * set to nonzero and links are detected in {@link #setText}.
+ * The default is true.
*
* @attr ref android.R.styleable#TextView_linksClickable
*/
@@ -4024,19 +4035,13 @@
((Editable) mText).append(text, start, end);
- boolean hasClickableSpans = false;
if (mAutoLinkMask != 0) {
- hasClickableSpans = Linkify.addLinks((Spannable) mText, mAutoLinkMask);
- } else if (mLinksClickable && text instanceof Spanned) {
- ClickableSpan[] clickableSpans =
- ((Spanned) text).getSpans(0, text.length(), ClickableSpan.class);
- hasClickableSpans = clickableSpans != null && clickableSpans.length > 0;
- }
-
- // Do not change the movement method for text that supports text selection as it
- // would prevent an arbitrary cursor displacement.
- if (hasClickableSpans && mLinksClickable && !textCanBeSelected()) {
- setMovementMethod(LinkMovementMethod.getInstance());
+ boolean linksWereAdded = Linkify.addLinks((Spannable) mText, mAutoLinkMask);
+ // Do not change the movement method for text that support text selection as it
+ // would prevent an arbitrary cursor displacement.
+ if (linksWereAdded && mLinksClickable && !textCanBeSelected()) {
+ setMovementMethod(LinkMovementMethod.getInstance());
+ }
}
}
@@ -4389,7 +4394,6 @@
text = TextUtils.stringOrSpannedString(text);
}
- boolean hasClickableSpans = false;
if (mAutoLinkMask != 0) {
Spannable s2;
@@ -4399,32 +4403,22 @@
s2 = mSpannableFactory.newSpannable(text);
}
- hasClickableSpans = Linkify.addLinks(s2, mAutoLinkMask);
- if (hasClickableSpans) {
+ if (Linkify.addLinks(s2, mAutoLinkMask)) {
text = s2;
- }
- } else if (mLinksClickable && text instanceof Spanned) {
- ClickableSpan[] clickableSpans =
- ((Spanned) text).getSpans(0, text.length(), ClickableSpan.class);
- hasClickableSpans = clickableSpans != null && clickableSpans.length > 0;
- if (hasClickableSpans && !(text instanceof Spannable)) {
- text = mSpannableFactory.newSpannable(text);
- }
- }
+ type = (type == BufferType.EDITABLE) ? BufferType.EDITABLE : BufferType.SPANNABLE;
- if (hasClickableSpans) {
- type = (type == BufferType.EDITABLE) ? BufferType.EDITABLE : BufferType.SPANNABLE;
- /*
- * We must go ahead and set the text before changing the
- * movement method, because setMovementMethod() may call
- * setText() again to try to upgrade the buffer type.
- */
- mText = text;
+ /*
+ * We must go ahead and set the text before changing the
+ * movement method, because setMovementMethod() may call
+ * setText() again to try to upgrade the buffer type.
+ */
+ mText = text;
- // Do not change the movement method for text that supports text selection as it
- // would prevent an arbitrary cursor displacement.
- if (mLinksClickable && !textCanBeSelected()) {
- setMovementMethod(LinkMovementMethod.getInstance());
+ // Do not change the movement method for text that support text selection as it
+ // would prevent an arbitrary cursor displacement.
+ if (mLinksClickable && !textCanBeSelected()) {
+ setMovementMethod(LinkMovementMethod.getInstance());
+ }
}
}
@@ -5411,8 +5405,6 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- mTemporaryDetach = false;
-
if (mEditor != null) mEditor.onAttachedToWindow();
if (mPreDrawListenerDetached) {
@@ -8294,14 +8286,12 @@
newSelEnd = Selection.getSelectionEnd(buf);
}
- if (newSelStart == newSelEnd && hasTransientState()) {
- setHasTransientState(false);
- } else if (newSelStart != newSelEnd && !hasTransientState()) {
- setHasTransientState(true);
- }
-
if (mEditor != null) {
mEditor.refreshTextActionMode();
+ if (!hasSelection() && mEditor.mTextActionMode == null && hasTransientState()) {
+ // User generated selection has been removed.
+ setHasTransientState(false);
+ }
}
onSelectionChanged(newSelStart, newSelEnd);
}
@@ -8319,6 +8309,7 @@
if (mEditor != null) {
if (oldStart >= 0) mEditor.invalidateTextDisplayList(mLayout, oldStart, oldEnd);
if (newStart >= 0) mEditor.invalidateTextDisplayList(mLayout, newStart, newEnd);
+ mEditor.invalidateHandlesAndActionMode();
}
}
@@ -8373,40 +8364,9 @@
}
}
- /**
- * @hide
- */
- @Override
- public void dispatchFinishTemporaryDetach() {
- mDispatchTemporaryDetach = true;
- super.dispatchFinishTemporaryDetach();
- mDispatchTemporaryDetach = false;
- }
-
- @Override
- public void onStartTemporaryDetach() {
- super.onStartTemporaryDetach();
- // Only track when onStartTemporaryDetach() is called directly,
- // usually because this instance is an editable field in a list
- if (!mDispatchTemporaryDetach) mTemporaryDetach = true;
-
- // Tell the editor that we are temporarily detached. It can use this to preserve
- // selection state as needed.
- if (mEditor != null) mEditor.mTemporaryDetach = true;
- }
-
- @Override
- public void onFinishTemporaryDetach() {
- super.onFinishTemporaryDetach();
- // Only track when onStartTemporaryDetach() is called directly,
- // usually because this instance is an editable field in a list
- if (!mDispatchTemporaryDetach) mTemporaryDetach = false;
- if (mEditor != null) mEditor.mTemporaryDetach = false;
- }
-
@Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
- if (mTemporaryDetach) {
+ if (isTemporarilyDetached()) {
// If we are temporarily in the detach state, then do nothing.
super.onFocusChanged(focused, direction, previouslyFocusedRect);
return;
@@ -9101,6 +9061,9 @@
if (mBufferType == BufferType.EDITABLE) {
info.setEditable(true);
+ if (isEnabled()) {
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_TEXT);
+ }
}
if (mEditor != null) {
@@ -9232,6 +9195,17 @@
}
}
} return false;
+ case AccessibilityNodeInfo.ACTION_SET_TEXT: {
+ if (!isEnabled() || (mBufferType != BufferType.EDITABLE)) {
+ return false;
+ }
+ CharSequence text = (arguments != null) ? arguments.getCharSequence(
+ AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE) : null;
+ setText(text);
+ if (text != null && text.length() > 0) {
+ Selection.setSelection((Spannable) mText, text.length());
+ }
+ } return true;
default: {
return super.performAccessibilityActionInternal(action, arguments);
}
@@ -9240,52 +9214,12 @@
private boolean performAccessibilityActionClick(Bundle arguments) {
boolean handled = false;
- boolean processed = false;
if (!isEnabled()) {
return false;
}
- if (arguments != null && arguments.containsKey(
- AccessibilityNodeInfo.ACTION_ARGUMENT_CLICK_SPAN_INDEX_INT)) {
- int spanIndex = arguments.getInt(
- AccessibilityNodeInfo.ACTION_ARGUMENT_CLICK_SPAN_INDEX_INT, -1);
- if (spanIndex >= 0 && hasSpannableText()) {
- ClickableSpan[] spans = ((Spannable) mText).getSpans(0,
- mText.length(), ClickableSpan.class);
- if (spans != null && spans.length > spanIndex && spans[spanIndex] != null) {
- // Simulate View.onTouchEvent for an ACTION_UP event
- if (isFocusable() && !isFocused()) {
- requestFocus();
- }
- spans[spanIndex].onClick(this);
- handled = true;
- }
- }
- processed = true;
- }
-
- if (!processed && arguments != null && arguments.containsKey(
- AccessibilityNodeInfo.ACTION_ARGUMENT_CLICK_CHARACTER_INDEX_INT)) {
- int characterIndex = arguments.getInt(
- AccessibilityNodeInfo.ACTION_ARGUMENT_CLICK_CHARACTER_INDEX_INT, -1);
- if (characterIndex >= 0 && hasSpannableText()) {
- ClickableSpan[] spans = ((Spannable) mText).getSpans(characterIndex,
- characterIndex, ClickableSpan.class);
- // click only on the first span to keep parity with onTouch() implementation
- if (spans != null && spans.length > 0 && spans[0] != null) {
- // Simulate View.onTouchEvent for an ACTION_UP event
- if (isFocusable() && !isFocused()) {
- requestFocus();
- }
- spans[0].onClick(this);
- handled = true;
- }
- }
- processed = true;
- }
-
- if (!processed && (isClickable() || isLongClickable())) {
+ if (isClickable() || isLongClickable()) {
// Simulate View.onTouchEvent for an ACTION_UP event
if (isFocusable() && !isFocused()) {
requestFocus();
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
index 06daf61..9cdb73a 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -23,6 +23,7 @@
import android.annotation.Nullable;
import android.annotation.StringRes;
import android.annotation.StyleRes;
+import android.annotation.TestApi;
import android.app.ActionBar;
import android.content.Context;
import android.content.res.TypedArray;
@@ -106,6 +107,8 @@
* @attr ref android.R.styleable#Toolbar_contentInsetLeft
* @attr ref android.R.styleable#Toolbar_contentInsetRight
* @attr ref android.R.styleable#Toolbar_contentInsetStart
+ * @attr ref android.R.styleable#Toolbar_contentInsetStartWithNavigation
+ * @attr ref android.R.styleable#Toolbar_contentInsetEndWithActions
* @attr ref android.R.styleable#Toolbar_gravity
* @attr ref android.R.styleable#Toolbar_logo
* @attr ref android.R.styleable#Toolbar_logoDescription
@@ -159,6 +162,8 @@
private int mTitleMarginBottom;
private final RtlSpacingHelper mContentInsets = new RtlSpacingHelper();
+ private int mContentInsetStartWithNavigation;
+ private int mContentInsetEndWithActions;
private int mGravity = Gravity.START | Gravity.CENTER_VERTICAL;
@@ -272,6 +277,11 @@
mContentInsets.setRelative(contentInsetStart, contentInsetEnd);
}
+ mContentInsetStartWithNavigation = a.getDimensionPixelOffset(
+ R.styleable.Toolbar_contentInsetStartWithNavigation, RtlSpacingHelper.UNDEFINED);
+ mContentInsetEndWithActions = a.getDimensionPixelOffset(
+ R.styleable.Toolbar_contentInsetEndWithActions, RtlSpacingHelper.UNDEFINED);
+
mCollapseIcon = a.getDrawable(R.styleable.Toolbar_collapseIcon);
mCollapseDescription = a.getText(R.styleable.Toolbar_collapseContentDescription);
@@ -967,6 +977,15 @@
}
/**
+ * @hide
+ */
+ @Nullable
+ @TestApi
+ public View getNavigationView() {
+ return mNavButtonView;
+ }
+
+ /**
* Return the Menu shown in the toolbar.
*
* <p>Applications that wish to populate the toolbar's menu can do so from here. To use
@@ -1055,7 +1074,7 @@
}
/**
- * Set the content insets for this toolbar relative to layout direction.
+ * Sets the content insets for this toolbar relative to layout direction.
*
* <p>The content inset affects the valid area for Toolbar content other than
* the navigation button and menu. Insets define the minimum margin for these components
@@ -1069,13 +1088,15 @@
* @see #getContentInsetEnd()
* @see #getContentInsetLeft()
* @see #getContentInsetRight()
+ * @attr ref android.R.styleable#Toolbar_contentInsetEnd
+ * @attr ref android.R.styleable#Toolbar_contentInsetStart
*/
public void setContentInsetsRelative(int contentInsetStart, int contentInsetEnd) {
mContentInsets.setRelative(contentInsetStart, contentInsetEnd);
}
/**
- * Get the starting content inset for this toolbar.
+ * Gets the starting content inset for this toolbar.
*
* <p>The content inset affects the valid area for Toolbar content other than
* the navigation button and menu. Insets define the minimum margin for these components
@@ -1088,13 +1109,14 @@
* @see #getContentInsetEnd()
* @see #getContentInsetLeft()
* @see #getContentInsetRight()
+ * @attr ref android.R.styleable#Toolbar_contentInsetStart
*/
public int getContentInsetStart() {
return mContentInsets.getStart();
}
/**
- * Get the ending content inset for this toolbar.
+ * Gets the ending content inset for this toolbar.
*
* <p>The content inset affects the valid area for Toolbar content other than
* the navigation button and menu. Insets define the minimum margin for these components
@@ -1107,13 +1129,14 @@
* @see #getContentInsetStart()
* @see #getContentInsetLeft()
* @see #getContentInsetRight()
+ * @attr ref android.R.styleable#Toolbar_contentInsetEnd
*/
public int getContentInsetEnd() {
return mContentInsets.getEnd();
}
/**
- * Set the content insets for this toolbar.
+ * Sets the content insets for this toolbar.
*
* <p>The content inset affects the valid area for Toolbar content other than
* the navigation button and menu. Insets define the minimum margin for these components
@@ -1127,13 +1150,15 @@
* @see #getContentInsetEnd()
* @see #getContentInsetLeft()
* @see #getContentInsetRight()
+ * @attr ref android.R.styleable#Toolbar_contentInsetLeft
+ * @attr ref android.R.styleable#Toolbar_contentInsetRight
*/
public void setContentInsetsAbsolute(int contentInsetLeft, int contentInsetRight) {
mContentInsets.setAbsolute(contentInsetLeft, contentInsetRight);
}
/**
- * Get the left content inset for this toolbar.
+ * Gets the left content inset for this toolbar.
*
* <p>The content inset affects the valid area for Toolbar content other than
* the navigation button and menu. Insets define the minimum margin for these components
@@ -1146,13 +1171,14 @@
* @see #getContentInsetStart()
* @see #getContentInsetEnd()
* @see #getContentInsetRight()
+ * @attr ref android.R.styleable#Toolbar_contentInsetLeft
*/
public int getContentInsetLeft() {
return mContentInsets.getLeft();
}
/**
- * Get the right content inset for this toolbar.
+ * Gets the right content inset for this toolbar.
*
* <p>The content inset affects the valid area for Toolbar content other than
* the navigation button and menu. Insets define the minimum margin for these components
@@ -1165,11 +1191,160 @@
* @see #getContentInsetStart()
* @see #getContentInsetEnd()
* @see #getContentInsetLeft()
+ * @attr ref android.R.styleable#Toolbar_contentInsetRight
*/
public int getContentInsetRight() {
return mContentInsets.getRight();
}
+ /**
+ * Gets the start content inset to use when a navigation button is present.
+ *
+ * <p>Different content insets are often called for when additional buttons are present
+ * in the toolbar, as well as at different toolbar sizes. The larger value of
+ * {@link #getContentInsetStart()} and this value will be used during layout.</p>
+ *
+ * @return the start content inset used when a navigation icon has been set in pixels
+ *
+ * @see #setContentInsetStartWithNavigation(int)
+ * @attr ref android.R.styleable#Toolbar_contentInsetStartWithNavigation
+ */
+ public int getContentInsetStartWithNavigation() {
+ return mContentInsetStartWithNavigation != RtlSpacingHelper.UNDEFINED
+ ? mContentInsetStartWithNavigation
+ : getContentInsetStart();
+ }
+
+ /**
+ * Sets the start content inset to use when a navigation button is present.
+ *
+ * <p>Different content insets are often called for when additional buttons are present
+ * in the toolbar, as well as at different toolbar sizes. The larger value of
+ * {@link #getContentInsetStart()} and this value will be used during layout.</p>
+ *
+ * @param insetStartWithNavigation the inset to use when a navigation icon has been set
+ * in pixels
+ *
+ * @see #getContentInsetStartWithNavigation()
+ * @attr ref android.R.styleable#Toolbar_contentInsetStartWithNavigation
+ */
+ public void setContentInsetStartWithNavigation(int insetStartWithNavigation) {
+ if (insetStartWithNavigation < 0) {
+ insetStartWithNavigation = RtlSpacingHelper.UNDEFINED;
+ }
+ if (insetStartWithNavigation != mContentInsetStartWithNavigation) {
+ mContentInsetStartWithNavigation = insetStartWithNavigation;
+ if (getNavigationIcon() != null) {
+ requestLayout();
+ }
+ }
+ }
+
+ /**
+ * Gets the end content inset to use when action buttons are present.
+ *
+ * <p>Different content insets are often called for when additional buttons are present
+ * in the toolbar, as well as at different toolbar sizes. The larger value of
+ * {@link #getContentInsetEnd()} and this value will be used during layout.</p>
+ *
+ * @return the end content inset used when a menu has been set in pixels
+ *
+ * @see #setContentInsetEndWithActions(int)
+ * @attr ref android.R.styleable#Toolbar_contentInsetEndWithActions
+ */
+ public int getContentInsetEndWithActions() {
+ return mContentInsetEndWithActions != RtlSpacingHelper.UNDEFINED
+ ? mContentInsetEndWithActions
+ : getContentInsetEnd();
+ }
+
+ /**
+ * Sets the start content inset to use when action buttons are present.
+ *
+ * <p>Different content insets are often called for when additional buttons are present
+ * in the toolbar, as well as at different toolbar sizes. The larger value of
+ * {@link #getContentInsetEnd()} and this value will be used during layout.</p>
+ *
+ * @param insetEndWithActions the inset to use when a menu has been set in pixels
+ *
+ * @see #setContentInsetEndWithActions(int)
+ * @attr ref android.R.styleable#Toolbar_contentInsetEndWithActions
+ */
+ public void setContentInsetEndWithActions(int insetEndWithActions) {
+ if (insetEndWithActions < 0) {
+ insetEndWithActions = RtlSpacingHelper.UNDEFINED;
+ }
+ if (insetEndWithActions != mContentInsetEndWithActions) {
+ mContentInsetEndWithActions = insetEndWithActions;
+ if (getNavigationIcon() != null) {
+ requestLayout();
+ }
+ }
+ }
+
+ /**
+ * Gets the content inset that will be used on the starting side of the bar in the current
+ * toolbar configuration.
+ *
+ * @return the current content inset start in pixels
+ *
+ * @see #getContentInsetStartWithNavigation()
+ */
+ public int getCurrentContentInsetStart() {
+ return getNavigationIcon() != null
+ ? Math.max(getContentInsetStart(), Math.max(mContentInsetStartWithNavigation, 0))
+ : getContentInsetStart();
+ }
+
+ /**
+ * Gets the content inset that will be used on the ending side of the bar in the current
+ * toolbar configuration.
+ *
+ * @return the current content inset end in pixels
+ *
+ * @see #getContentInsetEndWithActions()
+ */
+ public int getCurrentContentInsetEnd() {
+ boolean hasActions = false;
+ if (mMenuView != null) {
+ final MenuBuilder mb = mMenuView.peekMenu();
+ hasActions = mb != null && mb.hasVisibleItems();
+ }
+ return hasActions
+ ? Math.max(getContentInsetEnd(), Math.max(mContentInsetEndWithActions, 0))
+ : getContentInsetEnd();
+ }
+
+ /**
+ * Gets the content inset that will be used on the left side of the bar in the current
+ * toolbar configuration.
+ *
+ * @return the current content inset left in pixels
+ *
+ * @see #getContentInsetStartWithNavigation()
+ * @see #getContentInsetEndWithActions()
+ */
+ public int getCurrentContentInsetLeft() {
+ return isLayoutRtl()
+ ? getCurrentContentInsetEnd()
+ : getCurrentContentInsetStart();
+ }
+
+ /**
+ * Gets the content inset that will be used on the right side of the bar in the current
+ * toolbar configuration.
+ *
+ * @return the current content inset right in pixels
+ *
+ * @see #getContentInsetStartWithNavigation()
+ * @see #getContentInsetEndWithActions()
+ */
+ public int getCurrentContentInsetRight() {
+ return isLayoutRtl()
+ ? getCurrentContentInsetStart()
+ : getCurrentContentInsetEnd();
+ }
+
private void ensureNavButtonView() {
if (mNavButtonView == null) {
mNavButtonView = new ImageButton(getContext(), null, 0, mNavButtonStyle);
@@ -1406,7 +1581,7 @@
childState = combineMeasuredStates(childState, mCollapseButtonView.getMeasuredState());
}
- final int contentInsetStart = getContentInsetStart();
+ final int contentInsetStart = getCurrentContentInsetStart();
width += Math.max(contentInsetStart, navWidth);
collapsingMargins[marginStartIndex] = Math.max(0, contentInsetStart - navWidth);
@@ -1420,7 +1595,7 @@
childState = combineMeasuredStates(childState, mMenuView.getMeasuredState());
}
- final int contentInsetEnd = getContentInsetEnd();
+ final int contentInsetEnd = getCurrentContentInsetEnd();
width += Math.max(contentInsetEnd, menuWidth);
collapsingMargins[marginEndIndex] = Math.max(0, contentInsetEnd - menuWidth);
@@ -1543,10 +1718,12 @@
}
}
- collapsingMargins[0] = Math.max(0, getContentInsetLeft() - left);
- collapsingMargins[1] = Math.max(0, getContentInsetRight() - (width - paddingRight - right));
- left = Math.max(left, getContentInsetLeft());
- right = Math.min(right, width - paddingRight - getContentInsetRight());
+ final int contentInsetLeft = getCurrentContentInsetLeft();
+ final int contentInsetRight = getCurrentContentInsetRight();
+ collapsingMargins[0] = Math.max(0, contentInsetLeft - left);
+ collapsingMargins[1] = Math.max(0, contentInsetRight - (width - paddingRight - right));
+ left = Math.max(left, contentInsetLeft);
+ right = Math.min(right, width - paddingRight - contentInsetRight);
if (shouldLayout(mExpandedActionView)) {
if (isRtl) {
diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java
index 753c069..b7ac600 100644
--- a/core/java/com/android/internal/app/AlertController.java
+++ b/core/java/com/android/internal/app/AlertController.java
@@ -516,9 +516,15 @@
}
// Only show the divider if we have a title.
- final View divider;
+ View divider = null;
if (mMessage != null || mListView != null || hasCustomPanel) {
- divider = topPanel.findViewById(R.id.titleDivider);
+ if (!hasCustomPanel) {
+ divider = topPanel.findViewById(R.id.titleDividerNoCustom);
+ }
+ if (divider == null) {
+ divider = topPanel.findViewById(R.id.titleDivider);
+ }
+
} else {
divider = topPanel.findViewById(R.id.titleDividerTop);
}
@@ -526,6 +532,17 @@
if (divider != null) {
divider.setVisibility(View.VISIBLE);
}
+ } else {
+ if (contentPanel != null) {
+ final View spacer = contentPanel.findViewById(R.id.textSpacerNoTitle);
+ if (spacer != null) {
+ spacer.setVisibility(View.VISIBLE);
+ }
+ }
+ }
+
+ if (mListView instanceof RecycleListView) {
+ ((RecycleListView) mListView).setHasDecor(hasTopPanel, hasButtonPanel);
}
// Update scroll indicators as needed.
@@ -861,23 +878,34 @@
}
public static class RecycleListView extends ListView {
+ private final int mPaddingTopNoTitle;
+ private final int mPaddingBottomNoButtons;
+
boolean mRecycleOnMeasure = true;
public RecycleListView(Context context) {
- super(context);
+ this(context, null);
}
public RecycleListView(Context context, AttributeSet attrs) {
super(context, attrs);
+
+ final TypedArray ta = context.obtainStyledAttributes(
+ attrs, R.styleable.RecycleListView);
+ mPaddingBottomNoButtons = ta.getDimensionPixelOffset(
+ R.styleable.RecycleListView_paddingBottomNoButtons, -1);
+ mPaddingTopNoTitle = ta.getDimensionPixelOffset(
+ R.styleable.RecycleListView_paddingTopNoTitle, -1);
}
- public RecycleListView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- public RecycleListView(
- Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
+ public void setHasDecor(boolean hasTitle, boolean hasButtons) {
+ if (!hasButtons || !hasTitle) {
+ final int paddingLeft = getPaddingLeft();
+ final int paddingTop = hasTitle ? getPaddingTop() : mPaddingTopNoTitle;
+ final int paddingRight = getPaddingRight();
+ final int paddingBottom = hasButtons ? getPaddingBottom() : mPaddingBottomNoButtons;
+ setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
+ }
}
@Override
diff --git a/core/java/com/android/internal/app/LocaleHelper.java b/core/java/com/android/internal/app/LocaleHelper.java
index d8d6e56..a9d5113 100644
--- a/core/java/com/android/internal/app/LocaleHelper.java
+++ b/core/java/com/android/internal/app/LocaleHelper.java
@@ -181,6 +181,7 @@
public static final class LocaleInfoComparator implements Comparator<LocaleStore.LocaleInfo> {
private final Collator mCollator;
private final boolean mCountryMode;
+ private static final String PREFIX_ARABIC = "\u0627\u0644"; // ALEF-LAM, ال
/**
* Constructor.
@@ -192,6 +193,20 @@
mCountryMode = countryMode;
}
+ /*
+ * The Arabic collation should ignore Alef-Lam at the beginning (b/26277596)
+ *
+ * We look at the label's locale, not the current system locale.
+ * This is because the name of the Arabic language itself is in Arabic,
+ * and starts with Alef-Lam, no matter what the system locale is.
+ */
+ private String removePrefixForCompare(Locale locale, String str) {
+ if ("ar".equals(locale.getLanguage()) && str.startsWith(PREFIX_ARABIC)) {
+ return str.substring(PREFIX_ARABIC.length());
+ }
+ return str;
+ }
+
/**
* Compares its two arguments for order.
*
@@ -204,9 +219,11 @@
public int compare(LocaleStore.LocaleInfo lhs, LocaleStore.LocaleInfo rhs) {
// We don't care about the various suggestion types, just "suggested" (!= 0)
// and "all others" (== 0)
- if (mCountryMode || (lhs.isSuggested() == rhs.isSuggested())) {
+ if (lhs.isSuggested() == rhs.isSuggested()) {
// They are in the same "bucket" (suggested / others), so we compare the text
- return mCollator.compare(lhs.getLabel(mCountryMode), rhs.getLabel(mCountryMode));
+ return mCollator.compare(
+ removePrefixForCompare(lhs.getLocale(), lhs.getLabel(mCountryMode)),
+ removePrefixForCompare(rhs.getLocale(), rhs.getLabel(mCountryMode)));
} else {
// One locale is suggested and one is not, so we put them in different "buckets"
return lhs.isSuggested() ? -1 : 1;
diff --git a/core/java/com/android/internal/app/LocaleStore.java b/core/java/com/android/internal/app/LocaleStore.java
index c4e6675..7803e52 100644
--- a/core/java/com/android/internal/app/LocaleStore.java
+++ b/core/java/com/android/internal/app/LocaleStore.java
@@ -31,8 +31,9 @@
private static boolean sFullyInitialized = false;
public static class LocaleInfo {
- private static final int SUGGESTION_TYPE_NONE = 0x00;
- private static final int SUGGESTION_TYPE_SIM = 0x01;
+ private static final int SUGGESTION_TYPE_NONE = 0;
+ private static final int SUGGESTION_TYPE_SIM = 1 << 0;
+ private static final int SUGGESTION_TYPE_CFG = 1 << 1;
private final Locale mLocale;
private final Locale mParent;
@@ -273,6 +274,22 @@
final HashSet<String> localizedLocales = new HashSet<>();
for (String localeId : LocalePicker.getSystemAssetLocales()) {
LocaleInfo li = new LocaleInfo(localeId);
+ final String country = li.getLocale().getCountry();
+ // All this is to figure out if we should suggest a country
+ if (!country.isEmpty()) {
+ LocaleInfo cachedLocale = null;
+ if (sLocaleCache.containsKey(li.getId())) { // the simple case, e.g. fr-CH
+ cachedLocale = sLocaleCache.get(li.getId());
+ } else { // e.g. zh-TW localized, zh-Hant-TW in cache
+ final String langScriptCtry = li.getLangScriptKey() + "-" + country;
+ if (sLocaleCache.containsKey(langScriptCtry)) {
+ cachedLocale = sLocaleCache.get(langScriptCtry);
+ }
+ }
+ if (cachedLocale != null) {
+ cachedLocale.mSuggestionFlags |= LocaleInfo.SUGGESTION_TYPE_CFG;
+ }
+ }
localizedLocales.add(li.getLangScriptKey());
}
diff --git a/core/java/com/android/internal/app/ProcessMap.java b/core/java/com/android/internal/app/ProcessMap.java
index 23a8bd7..81036f7 100644
--- a/core/java/com/android/internal/app/ProcessMap.java
+++ b/core/java/com/android/internal/app/ProcessMap.java
@@ -54,4 +54,8 @@
public ArrayMap<String, SparseArray<E>> getMap() {
return mMap;
}
+
+ public int size() {
+ return mMap.size();
+ }
}
diff --git a/core/java/com/android/internal/app/ProcessStats.java b/core/java/com/android/internal/app/ProcessStats.java
deleted file mode 100644
index 17ca904d..0000000
--- a/core/java/com/android/internal/app/ProcessStats.java
+++ /dev/null
@@ -1,3794 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.app;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.text.format.DateFormat;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.DebugUtils;
-import android.util.Log;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.util.TimeUtils;
-
-import com.android.internal.util.GrowingArrayUtils;
-
-import dalvik.system.VMRuntime;
-import libcore.util.EmptyArray;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Objects;
-
-public final class ProcessStats implements Parcelable {
- static final String TAG = "ProcessStats";
- static final boolean DEBUG = false;
- static final boolean DEBUG_PARCEL = false;
-
- public static final String SERVICE_NAME = "procstats";
-
- // How often the service commits its data, giving the minimum batching
- // that is done.
- public static long COMMIT_PERIOD = 3*60*60*1000; // Commit current stats every 3 hours
-
- // Minimum uptime period before committing. If the COMMIT_PERIOD has elapsed but
- // the total uptime has not exceeded this amount, then the commit will be held until
- // it is reached.
- public static long COMMIT_UPTIME_PERIOD = 60*60*1000; // Must have at least 1 hour elapsed
-
- public static final int STATE_NOTHING = -1;
- public static final int STATE_PERSISTENT = 0;
- public static final int STATE_TOP = 1;
- public static final int STATE_IMPORTANT_FOREGROUND = 2;
- public static final int STATE_IMPORTANT_BACKGROUND = 3;
- public static final int STATE_BACKUP = 4;
- public static final int STATE_HEAVY_WEIGHT = 5;
- public static final int STATE_SERVICE = 6;
- public static final int STATE_SERVICE_RESTARTING = 7;
- public static final int STATE_RECEIVER = 8;
- public static final int STATE_HOME = 9;
- public static final int STATE_LAST_ACTIVITY = 10;
- public static final int STATE_CACHED_ACTIVITY = 11;
- public static final int STATE_CACHED_ACTIVITY_CLIENT = 12;
- public static final int STATE_CACHED_EMPTY = 13;
- public static final int STATE_COUNT = STATE_CACHED_EMPTY+1;
-
- public static final int PSS_SAMPLE_COUNT = 0;
- public static final int PSS_MINIMUM = 1;
- public static final int PSS_AVERAGE = 2;
- public static final int PSS_MAXIMUM = 3;
- public static final int PSS_USS_MINIMUM = 4;
- public static final int PSS_USS_AVERAGE = 5;
- public static final int PSS_USS_MAXIMUM = 6;
- public static final int PSS_COUNT = PSS_USS_MAXIMUM+1;
-
- public static final int SYS_MEM_USAGE_SAMPLE_COUNT = 0;
- public static final int SYS_MEM_USAGE_CACHED_MINIMUM = 1;
- public static final int SYS_MEM_USAGE_CACHED_AVERAGE = 2;
- public static final int SYS_MEM_USAGE_CACHED_MAXIMUM = 3;
- public static final int SYS_MEM_USAGE_FREE_MINIMUM = 4;
- public static final int SYS_MEM_USAGE_FREE_AVERAGE = 5;
- public static final int SYS_MEM_USAGE_FREE_MAXIMUM = 6;
- public static final int SYS_MEM_USAGE_ZRAM_MINIMUM = 7;
- public static final int SYS_MEM_USAGE_ZRAM_AVERAGE = 8;
- public static final int SYS_MEM_USAGE_ZRAM_MAXIMUM = 9;
- public static final int SYS_MEM_USAGE_KERNEL_MINIMUM = 10;
- public static final int SYS_MEM_USAGE_KERNEL_AVERAGE = 11;
- public static final int SYS_MEM_USAGE_KERNEL_MAXIMUM = 12;
- public static final int SYS_MEM_USAGE_NATIVE_MINIMUM = 13;
- public static final int SYS_MEM_USAGE_NATIVE_AVERAGE = 14;
- public static final int SYS_MEM_USAGE_NATIVE_MAXIMUM = 15;
- public static final int SYS_MEM_USAGE_COUNT = SYS_MEM_USAGE_NATIVE_MAXIMUM+1;
-
- public static final int ADJ_NOTHING = -1;
- public static final int ADJ_MEM_FACTOR_NORMAL = 0;
- public static final int ADJ_MEM_FACTOR_MODERATE = 1;
- public static final int ADJ_MEM_FACTOR_LOW = 2;
- public static final int ADJ_MEM_FACTOR_CRITICAL = 3;
- public static final int ADJ_MEM_FACTOR_COUNT = ADJ_MEM_FACTOR_CRITICAL+1;
- public static final int ADJ_SCREEN_MOD = ADJ_MEM_FACTOR_COUNT;
- public static final int ADJ_SCREEN_OFF = 0;
- public static final int ADJ_SCREEN_ON = ADJ_SCREEN_MOD;
- public static final int ADJ_COUNT = ADJ_SCREEN_ON*2;
-
- public static final int FLAG_COMPLETE = 1<<0;
- public static final int FLAG_SHUTDOWN = 1<<1;
- public static final int FLAG_SYSPROPS = 1<<2;
-
- public static final int[] ALL_MEM_ADJ = new int[] { ADJ_MEM_FACTOR_NORMAL,
- ADJ_MEM_FACTOR_MODERATE, ADJ_MEM_FACTOR_LOW, ADJ_MEM_FACTOR_CRITICAL };
-
- public static final int[] ALL_SCREEN_ADJ = new int[] { ADJ_SCREEN_OFF, ADJ_SCREEN_ON };
-
- public static final int[] NON_CACHED_PROC_STATES = new int[] {
- STATE_PERSISTENT, STATE_TOP, STATE_IMPORTANT_FOREGROUND,
- STATE_IMPORTANT_BACKGROUND, STATE_BACKUP, STATE_HEAVY_WEIGHT,
- STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER
- };
-
- public static final int[] BACKGROUND_PROC_STATES = new int[] {
- STATE_IMPORTANT_FOREGROUND, STATE_IMPORTANT_BACKGROUND, STATE_BACKUP,
- STATE_HEAVY_WEIGHT, STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER
- };
-
- // Map from process states to the states we track.
- static final int[] PROCESS_STATE_TO_STATE = new int[] {
- STATE_PERSISTENT, // ActivityManager.PROCESS_STATE_PERSISTENT
- STATE_PERSISTENT, // ActivityManager.PROCESS_STATE_PERSISTENT_UI
- STATE_TOP, // ActivityManager.PROCESS_STATE_TOP
- STATE_IMPORTANT_FOREGROUND, // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
- STATE_IMPORTANT_FOREGROUND, // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
- STATE_TOP, // ActivityManager.PROCESS_STATE_TOP_SLEEPING
- STATE_IMPORTANT_FOREGROUND, // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
- STATE_IMPORTANT_BACKGROUND, // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
- STATE_BACKUP, // ActivityManager.PROCESS_STATE_BACKUP
- STATE_HEAVY_WEIGHT, // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
- STATE_SERVICE, // ActivityManager.PROCESS_STATE_SERVICE
- STATE_RECEIVER, // ActivityManager.PROCESS_STATE_RECEIVER
- STATE_HOME, // ActivityManager.PROCESS_STATE_HOME
- STATE_LAST_ACTIVITY, // ActivityManager.PROCESS_STATE_LAST_ACTIVITY
- STATE_CACHED_ACTIVITY, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY
- STATE_CACHED_ACTIVITY_CLIENT, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT
- STATE_CACHED_EMPTY, // ActivityManager.PROCESS_STATE_CACHED_EMPTY
- };
-
- public static final int[] ALL_PROC_STATES = new int[] { STATE_PERSISTENT,
- STATE_TOP, STATE_IMPORTANT_FOREGROUND, STATE_IMPORTANT_BACKGROUND, STATE_BACKUP,
- STATE_HEAVY_WEIGHT, STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER,
- STATE_HOME, STATE_LAST_ACTIVITY, STATE_CACHED_ACTIVITY,
- STATE_CACHED_ACTIVITY_CLIENT, STATE_CACHED_EMPTY
- };
-
- static final String[] STATE_NAMES = new String[] {
- "Persist", "Top ", "ImpFg ", "ImpBg ",
- "Backup ", "HeavyWt", "Service", "ServRst",
- "Receivr", "Home ",
- "LastAct", "CchAct ", "CchCAct", "CchEmty"
- };
-
- public static final String[] ADJ_SCREEN_NAMES_CSV = new String[] {
- "off", "on"
- };
-
- public static final String[] ADJ_MEM_NAMES_CSV = new String[] {
- "norm", "mod", "low", "crit"
- };
-
- public static final String[] STATE_NAMES_CSV = new String[] {
- "pers", "top", "impfg", "impbg", "backup", "heavy",
- "service", "service-rs", "receiver", "home", "lastact",
- "cch-activity", "cch-aclient", "cch-empty"
- };
-
- static final String[] ADJ_SCREEN_TAGS = new String[] {
- "0", "1"
- };
-
- static final String[] ADJ_MEM_TAGS = new String[] {
- "n", "m", "l", "c"
- };
-
- static final String[] STATE_TAGS = new String[] {
- "p", "t", "f", "b", "u", "w",
- "s", "x", "r", "h", "l", "a", "c", "e"
- };
-
- static final String CSV_SEP = "\t";
-
- // Current version of the parcel format.
- private static final int PARCEL_VERSION = 18;
- // In-memory Parcel magic number, used to detect attempts to unmarshall bad data
- private static final int MAGIC = 0x50535453;
-
- // Where the "type"/"state" part of the data appears in an offset integer.
- static int OFFSET_TYPE_SHIFT = 0;
- static int OFFSET_TYPE_MASK = 0xff;
- // Where the "which array" part of the data appears in an offset integer.
- static int OFFSET_ARRAY_SHIFT = 8;
- static int OFFSET_ARRAY_MASK = 0xff;
- // Where the "index into array" part of the data appears in an offset integer.
- static int OFFSET_INDEX_SHIFT = 16;
- static int OFFSET_INDEX_MASK = 0xffff;
-
- public String mReadError;
- public String mTimePeriodStartClockStr;
- public int mFlags;
-
- public final ProcessMap<SparseArray<PackageState>> mPackages
- = new ProcessMap<SparseArray<PackageState>>();
- public final ProcessMap<ProcessState> mProcesses = new ProcessMap<ProcessState>();
-
- public final long[] mMemFactorDurations = new long[ADJ_COUNT];
- public int mMemFactor = STATE_NOTHING;
- public long mStartTime;
-
- public int[] mSysMemUsageTable = null;
- public int mSysMemUsageTableSize = 0;
- public final long[] mSysMemUsageArgs = new long[SYS_MEM_USAGE_COUNT];
-
- public long mTimePeriodStartClock;
- public long mTimePeriodStartRealtime;
- public long mTimePeriodEndRealtime;
- public long mTimePeriodStartUptime;
- public long mTimePeriodEndUptime;
- String mRuntime;
- boolean mRunning;
-
- static final int LONGS_SIZE = 4096;
- final ArrayList<long[]> mLongs = new ArrayList<long[]>();
- int mNextLong;
-
- int[] mAddLongTable;
- int mAddLongTableSize;
-
- // For writing parcels.
- ArrayMap<String, Integer> mCommonStringToIndex;
-
- // For reading parcels.
- ArrayList<String> mIndexToCommonString;
-
- public ProcessStats(boolean running) {
- mRunning = running;
- reset();
- }
-
- public ProcessStats(Parcel in) {
- reset();
- readFromParcel(in);
- }
-
- public void add(ProcessStats other) {
- ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = other.mPackages.getMap();
- for (int ip=0; ip<pkgMap.size(); ip++) {
- final String pkgName = pkgMap.keyAt(ip);
- final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip);
- for (int iu=0; iu<uids.size(); iu++) {
- final int uid = uids.keyAt(iu);
- final SparseArray<PackageState> versions = uids.valueAt(iu);
- for (int iv=0; iv<versions.size(); iv++) {
- final int vers = versions.keyAt(iv);
- final PackageState otherState = versions.valueAt(iv);
- final int NPROCS = otherState.mProcesses.size();
- final int NSRVS = otherState.mServices.size();
- for (int iproc=0; iproc<NPROCS; iproc++) {
- ProcessState otherProc = otherState.mProcesses.valueAt(iproc);
- if (otherProc.mCommonProcess != otherProc) {
- if (DEBUG) Slog.d(TAG, "Adding pkg " + pkgName + " uid " + uid
- + " vers " + vers + " proc " + otherProc.mName);
- ProcessState thisProc = getProcessStateLocked(pkgName, uid, vers,
- otherProc.mName);
- if (thisProc.mCommonProcess == thisProc) {
- if (DEBUG) Slog.d(TAG, "Existing process is single-package, splitting");
- thisProc.mMultiPackage = true;
- long now = SystemClock.uptimeMillis();
- final PackageState pkgState = getPackageStateLocked(pkgName, uid,
- vers);
- thisProc = thisProc.clone(thisProc.mPackage, now);
- pkgState.mProcesses.put(thisProc.mName, thisProc);
- }
- thisProc.add(otherProc);
- }
- }
- for (int isvc=0; isvc<NSRVS; isvc++) {
- ServiceState otherSvc = otherState.mServices.valueAt(isvc);
- if (DEBUG) Slog.d(TAG, "Adding pkg " + pkgName + " uid " + uid
- + " service " + otherSvc.mName);
- ServiceState thisSvc = getServiceStateLocked(pkgName, uid, vers,
- otherSvc.mProcessName, otherSvc.mName);
- thisSvc.add(otherSvc);
- }
- }
- }
- }
-
- ArrayMap<String, SparseArray<ProcessState>> procMap = other.mProcesses.getMap();
- for (int ip=0; ip<procMap.size(); ip++) {
- SparseArray<ProcessState> uids = procMap.valueAt(ip);
- for (int iu=0; iu<uids.size(); iu++) {
- int uid = uids.keyAt(iu);
- ProcessState otherProc = uids.valueAt(iu);
- ProcessState thisProc = mProcesses.get(otherProc.mName, uid);
- if (DEBUG) Slog.d(TAG, "Adding uid " + uid + " proc " + otherProc.mName);
- if (thisProc == null) {
- if (DEBUG) Slog.d(TAG, "Creating new process!");
- thisProc = new ProcessState(this, otherProc.mPackage, uid, otherProc.mVersion,
- otherProc.mName);
- mProcesses.put(otherProc.mName, uid, thisProc);
- PackageState thisState = getPackageStateLocked(otherProc.mPackage, uid,
- otherProc.mVersion);
- if (!thisState.mProcesses.containsKey(otherProc.mName)) {
- thisState.mProcesses.put(otherProc.mName, thisProc);
- }
- }
- thisProc.add(otherProc);
- }
- }
-
- for (int i=0; i<ADJ_COUNT; i++) {
- if (DEBUG) Slog.d(TAG, "Total duration #" + i + " inc by "
- + other.mMemFactorDurations[i] + " from "
- + mMemFactorDurations[i]);
- mMemFactorDurations[i] += other.mMemFactorDurations[i];
- }
-
- for (int i=0; i<other.mSysMemUsageTableSize; i++) {
- int ent = other.mSysMemUsageTable[i];
- int state = (ent>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
- long[] longs = other.mLongs.get((ent>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
- addSysMemUsage(state, longs, ((ent >> OFFSET_INDEX_SHIFT) & OFFSET_INDEX_MASK));
- }
-
- if (other.mTimePeriodStartClock < mTimePeriodStartClock) {
- mTimePeriodStartClock = other.mTimePeriodStartClock;
- mTimePeriodStartClockStr = other.mTimePeriodStartClockStr;
- }
- mTimePeriodEndRealtime += other.mTimePeriodEndRealtime - other.mTimePeriodStartRealtime;
- mTimePeriodEndUptime += other.mTimePeriodEndUptime - other.mTimePeriodStartUptime;
- }
-
- public void addSysMemUsage(long cachedMem, long freeMem, long zramMem, long kernelMem,
- long nativeMem) {
- if (mMemFactor != STATE_NOTHING) {
- int state = mMemFactor * STATE_COUNT;
- mSysMemUsageArgs[SYS_MEM_USAGE_SAMPLE_COUNT] = 1;
- for (int i=0; i<3; i++) {
- mSysMemUsageArgs[SYS_MEM_USAGE_CACHED_MINIMUM + i] = cachedMem;
- mSysMemUsageArgs[SYS_MEM_USAGE_FREE_MINIMUM + i] = freeMem;
- mSysMemUsageArgs[SYS_MEM_USAGE_ZRAM_MINIMUM + i] = zramMem;
- mSysMemUsageArgs[SYS_MEM_USAGE_KERNEL_MINIMUM + i] = kernelMem;
- mSysMemUsageArgs[SYS_MEM_USAGE_NATIVE_MINIMUM + i] = nativeMem;
- }
- addSysMemUsage(state, mSysMemUsageArgs, 0);
- }
- }
-
- void addSysMemUsage(int state, long[] data, int dataOff) {
- int idx = binarySearch(mSysMemUsageTable, mSysMemUsageTableSize, state);
- int off;
- if (idx >= 0) {
- off = mSysMemUsageTable[idx];
- } else {
- mAddLongTable = mSysMemUsageTable;
- mAddLongTableSize = mSysMemUsageTableSize;
- off = addLongData(~idx, state, SYS_MEM_USAGE_COUNT);
- mSysMemUsageTable = mAddLongTable;
- mSysMemUsageTableSize = mAddLongTableSize;
- }
- long[] longs = mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
- idx = (off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK;
- addSysMemUsage(longs, idx, data, dataOff);
- }
-
- static void addSysMemUsage(long[] dstData, int dstOff, long[] addData, int addOff) {
- final long dstCount = dstData[dstOff+SYS_MEM_USAGE_SAMPLE_COUNT];
- final long addCount = addData[addOff+SYS_MEM_USAGE_SAMPLE_COUNT];
- if (dstCount == 0) {
- dstData[dstOff+SYS_MEM_USAGE_SAMPLE_COUNT] = addCount;
- for (int i=SYS_MEM_USAGE_CACHED_MINIMUM; i<SYS_MEM_USAGE_COUNT; i++) {
- dstData[dstOff+i] = addData[addOff+i];
- }
- } else if (addCount > 0) {
- dstData[dstOff+SYS_MEM_USAGE_SAMPLE_COUNT] = dstCount + addCount;
- for (int i=SYS_MEM_USAGE_CACHED_MINIMUM; i<SYS_MEM_USAGE_COUNT; i+=3) {
- if (dstData[dstOff+i] > addData[addOff+i]) {
- dstData[dstOff+i] = addData[addOff+i];
- }
- dstData[dstOff+i+1] = (long)(
- ((dstData[dstOff+i+1]*(double)dstCount)
- + (addData[addOff+i+1]*(double)addCount))
- / (dstCount+addCount) );
- if (dstData[dstOff+i+2] < addData[addOff+i+2]) {
- dstData[dstOff+i+2] = addData[addOff+i+2];
- }
- }
- }
- }
-
- public static final Parcelable.Creator<ProcessStats> CREATOR
- = new Parcelable.Creator<ProcessStats>() {
- public ProcessStats createFromParcel(Parcel in) {
- return new ProcessStats(in);
- }
-
- public ProcessStats[] newArray(int size) {
- return new ProcessStats[size];
- }
- };
-
- private static void printScreenLabel(PrintWriter pw, int offset) {
- switch (offset) {
- case ADJ_NOTHING:
- pw.print(" ");
- break;
- case ADJ_SCREEN_OFF:
- pw.print("SOff/");
- break;
- case ADJ_SCREEN_ON:
- pw.print("SOn /");
- break;
- default:
- pw.print("????/");
- break;
- }
- }
-
- public static void printScreenLabelCsv(PrintWriter pw, int offset) {
- switch (offset) {
- case ADJ_NOTHING:
- break;
- case ADJ_SCREEN_OFF:
- pw.print(ADJ_SCREEN_NAMES_CSV[0]);
- break;
- case ADJ_SCREEN_ON:
- pw.print(ADJ_SCREEN_NAMES_CSV[1]);
- break;
- default:
- pw.print("???");
- break;
- }
- }
-
- private static void printMemLabel(PrintWriter pw, int offset, char sep) {
- switch (offset) {
- case ADJ_NOTHING:
- pw.print(" ");
- if (sep != 0) pw.print(' ');
- break;
- case ADJ_MEM_FACTOR_NORMAL:
- pw.print("Norm");
- if (sep != 0) pw.print(sep);
- break;
- case ADJ_MEM_FACTOR_MODERATE:
- pw.print("Mod ");
- if (sep != 0) pw.print(sep);
- break;
- case ADJ_MEM_FACTOR_LOW:
- pw.print("Low ");
- if (sep != 0) pw.print(sep);
- break;
- case ADJ_MEM_FACTOR_CRITICAL:
- pw.print("Crit");
- if (sep != 0) pw.print(sep);
- break;
- default:
- pw.print("????");
- if (sep != 0) pw.print(sep);
- break;
- }
- }
-
- public static void printMemLabelCsv(PrintWriter pw, int offset) {
- if (offset >= ADJ_MEM_FACTOR_NORMAL) {
- if (offset <= ADJ_MEM_FACTOR_CRITICAL) {
- pw.print(ADJ_MEM_NAMES_CSV[offset]);
- } else {
- pw.print("???");
- }
- }
- }
-
- public static long dumpSingleTime(PrintWriter pw, String prefix, long[] durations,
- int curState, long curStartTime, long now) {
- long totalTime = 0;
- int printedScreen = -1;
- for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) {
- int printedMem = -1;
- for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) {
- int state = imem+iscreen;
- long time = durations[state];
- String running = "";
- if (curState == state) {
- time += now - curStartTime;
- if (pw != null) {
- running = " (running)";
- }
- }
- if (time != 0) {
- if (pw != null) {
- pw.print(prefix);
- printScreenLabel(pw, printedScreen != iscreen
- ? iscreen : STATE_NOTHING);
- printedScreen = iscreen;
- printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING, (char)0);
- printedMem = imem;
- pw.print(": ");
- TimeUtils.formatDuration(time, pw); pw.println(running);
- }
- totalTime += time;
- }
- }
- }
- if (totalTime != 0 && pw != null) {
- pw.print(prefix);
- pw.print(" TOTAL: ");
- TimeUtils.formatDuration(totalTime, pw);
- pw.println();
- }
- return totalTime;
- }
-
- static void dumpAdjTimesCheckin(PrintWriter pw, String sep, long[] durations,
- int curState, long curStartTime, long now) {
- for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) {
- for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) {
- int state = imem+iscreen;
- long time = durations[state];
- if (curState == state) {
- time += now - curStartTime;
- }
- if (time != 0) {
- printAdjTagAndValue(pw, state, time);
- }
- }
- }
- }
-
- static void dumpServiceTimeCheckin(PrintWriter pw, String label, String packageName,
- int uid, int vers, String serviceName, ServiceState svc, int serviceType, int opCount,
- int curState, long curStartTime, long now) {
- if (opCount <= 0) {
- return;
- }
- pw.print(label);
- pw.print(",");
- pw.print(packageName);
- pw.print(",");
- pw.print(uid);
- pw.print(",");
- pw.print(vers);
- pw.print(",");
- pw.print(serviceName);
- pw.print(",");
- pw.print(opCount);
- boolean didCurState = false;
- for (int i=0; i<svc.mDurationsTableSize; i++) {
- int off = svc.mDurationsTable[i];
- int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
- int memFactor = type / ServiceState.SERVICE_COUNT;
- type %= ServiceState.SERVICE_COUNT;
- if (type != serviceType) {
- continue;
- }
- long time = svc.mStats.getLong(off, 0);
- if (curState == memFactor) {
- didCurState = true;
- time += now - curStartTime;
- }
- printAdjTagAndValue(pw, memFactor, time);
- }
- if (!didCurState && curState != STATE_NOTHING) {
- printAdjTagAndValue(pw, curState, now - curStartTime);
- }
- pw.println();
- }
-
- public static void computeProcessData(ProcessState proc, ProcessDataCollection data, long now) {
- data.totalTime = 0;
- data.numPss = data.minPss = data.avgPss = data.maxPss =
- data.minUss = data.avgUss = data.maxUss = 0;
- for (int is=0; is<data.screenStates.length; is++) {
- for (int im=0; im<data.memStates.length; im++) {
- for (int ip=0; ip<data.procStates.length; ip++) {
- int bucket = ((data.screenStates[is] + data.memStates[im]) * STATE_COUNT)
- + data.procStates[ip];
- data.totalTime += proc.getDuration(bucket, now);
- long samples = proc.getPssSampleCount(bucket);
- if (samples > 0) {
- long minPss = proc.getPssMinimum(bucket);
- long avgPss = proc.getPssAverage(bucket);
- long maxPss = proc.getPssMaximum(bucket);
- long minUss = proc.getPssUssMinimum(bucket);
- long avgUss = proc.getPssUssAverage(bucket);
- long maxUss = proc.getPssUssMaximum(bucket);
- if (data.numPss == 0) {
- data.minPss = minPss;
- data.avgPss = avgPss;
- data.maxPss = maxPss;
- data.minUss = minUss;
- data.avgUss = avgUss;
- data.maxUss = maxUss;
- } else {
- if (minPss < data.minPss) {
- data.minPss = minPss;
- }
- data.avgPss = (long)( ((data.avgPss*(double)data.numPss)
- + (avgPss*(double)samples)) / (data.numPss+samples) );
- if (maxPss > data.maxPss) {
- data.maxPss = maxPss;
- }
- if (minUss < data.minUss) {
- data.minUss = minUss;
- }
- data.avgUss = (long)( ((data.avgUss*(double)data.numPss)
- + (avgUss*(double)samples)) / (data.numPss+samples) );
- if (maxUss > data.maxUss) {
- data.maxUss = maxUss;
- }
- }
- data.numPss += samples;
- }
- }
- }
- }
- }
-
- static long computeProcessTimeLocked(ProcessState proc, int[] screenStates, int[] memStates,
- int[] procStates, long now) {
- long totalTime = 0;
- /*
- for (int i=0; i<proc.mDurationsTableSize; i++) {
- int val = proc.mDurationsTable[i];
- totalTime += proc.mState.getLong(val, 0);
- if ((val&0xff) == proc.mCurState) {
- totalTime += now - proc.mStartTime;
- }
- }
- */
- for (int is=0; is<screenStates.length; is++) {
- for (int im=0; im<memStates.length; im++) {
- for (int ip=0; ip<procStates.length; ip++) {
- int bucket = ((screenStates[is] + memStates[im]) * STATE_COUNT)
- + procStates[ip];
- totalTime += proc.getDuration(bucket, now);
- }
- }
- }
- proc.mTmpTotalTime = totalTime;
- return totalTime;
- }
-
- static class PssAggr {
- long pss = 0;
- long samples = 0;
-
- void add(long newPss, long newSamples) {
- pss = (long)( (pss*(double)samples) + (newPss*(double)newSamples) )
- / (samples+newSamples);
- samples += newSamples;
- }
- }
-
- public void computeTotalMemoryUse(TotalMemoryUseCollection data, long now) {
- data.totalTime = 0;
- for (int i=0; i<STATE_COUNT; i++) {
- data.processStateWeight[i] = 0;
- data.processStatePss[i] = 0;
- data.processStateTime[i] = 0;
- data.processStateSamples[i] = 0;
- }
- for (int i=0; i<SYS_MEM_USAGE_COUNT; i++) {
- data.sysMemUsage[i] = 0;
- }
- data.sysMemCachedWeight = 0;
- data.sysMemFreeWeight = 0;
- data.sysMemZRamWeight = 0;
- data.sysMemKernelWeight = 0;
- data.sysMemNativeWeight = 0;
- data.sysMemSamples = 0;
- long[] totalMemUsage = new long[SYS_MEM_USAGE_COUNT];
- for (int i=0; i<mSysMemUsageTableSize; i++) {
- int ent = mSysMemUsageTable[i];
- long[] longs = mLongs.get((ent>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
- int idx = (ent >> OFFSET_INDEX_SHIFT) & OFFSET_INDEX_MASK;
- addSysMemUsage(totalMemUsage, 0, longs, idx);
- }
- for (int is=0; is<data.screenStates.length; is++) {
- for (int im=0; im<data.memStates.length; im++) {
- int memBucket = data.screenStates[is] + data.memStates[im];
- int stateBucket = memBucket * STATE_COUNT;
- long memTime = mMemFactorDurations[memBucket];
- if (mMemFactor == memBucket) {
- memTime += now - mStartTime;
- }
- data.totalTime += memTime;
- int sysIdx = binarySearch(mSysMemUsageTable, mSysMemUsageTableSize, stateBucket);
- long[] longs = totalMemUsage;
- int idx = 0;
- if (sysIdx >= 0) {
- int ent = mSysMemUsageTable[sysIdx];
- long[] tmpLongs = mLongs.get((ent>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
- int tmpIdx = (ent >> OFFSET_INDEX_SHIFT) & OFFSET_INDEX_MASK;
- if (tmpLongs[tmpIdx+SYS_MEM_USAGE_SAMPLE_COUNT] >= 3) {
- addSysMemUsage(data.sysMemUsage, 0, longs, idx);
- longs = tmpLongs;
- idx = tmpIdx;
- }
- }
- data.sysMemCachedWeight += longs[idx+SYS_MEM_USAGE_CACHED_AVERAGE]
- * (double)memTime;
- data.sysMemFreeWeight += longs[idx+SYS_MEM_USAGE_FREE_AVERAGE]
- * (double)memTime;
- data.sysMemZRamWeight += longs[idx+SYS_MEM_USAGE_ZRAM_AVERAGE]
- * (double)memTime;
- data.sysMemKernelWeight += longs[idx+SYS_MEM_USAGE_KERNEL_AVERAGE]
- * (double)memTime;
- data.sysMemNativeWeight += longs[idx+SYS_MEM_USAGE_NATIVE_AVERAGE]
- * (double)memTime;
- data.sysMemSamples += longs[idx+SYS_MEM_USAGE_SAMPLE_COUNT];
- }
- }
- ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
- for (int iproc=0; iproc<procMap.size(); iproc++) {
- SparseArray<ProcessState> uids = procMap.valueAt(iproc);
- for (int iu=0; iu<uids.size(); iu++) {
- final ProcessState proc = uids.valueAt(iu);
- final PssAggr fgPss = new PssAggr();
- final PssAggr bgPss = new PssAggr();
- final PssAggr cachedPss = new PssAggr();
- boolean havePss = false;
- for (int i=0; i<proc.mDurationsTableSize; i++) {
- int off = proc.mDurationsTable[i];
- int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
- int procState = type % STATE_COUNT;
- long samples = proc.getPssSampleCount(type);
- if (samples > 0) {
- long avg = proc.getPssAverage(type);
- havePss = true;
- if (procState <= STATE_IMPORTANT_FOREGROUND) {
- fgPss.add(avg, samples);
- } else if (procState <= STATE_RECEIVER) {
- bgPss.add(avg, samples);
- } else {
- cachedPss.add(avg, samples);
- }
- }
- }
- if (!havePss) {
- continue;
- }
- boolean fgHasBg = false;
- boolean fgHasCached = false;
- boolean bgHasCached = false;
- if (fgPss.samples < 3 && bgPss.samples > 0) {
- fgHasBg = true;
- fgPss.add(bgPss.pss, bgPss.samples);
- }
- if (fgPss.samples < 3 && cachedPss.samples > 0) {
- fgHasCached = true;
- fgPss.add(cachedPss.pss, cachedPss.samples);
- }
- if (bgPss.samples < 3 && cachedPss.samples > 0) {
- bgHasCached = true;
- bgPss.add(cachedPss.pss, cachedPss.samples);
- }
- if (bgPss.samples < 3 && !fgHasBg && fgPss.samples > 0) {
- bgPss.add(fgPss.pss, fgPss.samples);
- }
- if (cachedPss.samples < 3 && !bgHasCached && bgPss.samples > 0) {
- cachedPss.add(bgPss.pss, bgPss.samples);
- }
- if (cachedPss.samples < 3 && !fgHasCached && fgPss.samples > 0) {
- cachedPss.add(fgPss.pss, fgPss.samples);
- }
- for (int i=0; i<proc.mDurationsTableSize; i++) {
- final int off = proc.mDurationsTable[i];
- final int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
- long time = getLong(off, 0);
- if (proc.mCurState == type) {
- time += now - proc.mStartTime;
- }
- final int procState = type % STATE_COUNT;
- data.processStateTime[procState] += time;
- long samples = proc.getPssSampleCount(type);
- long avg;
- if (samples > 0) {
- avg = proc.getPssAverage(type);
- } else if (procState <= STATE_IMPORTANT_FOREGROUND) {
- samples = fgPss.samples;
- avg = fgPss.pss;
- } else if (procState <= STATE_RECEIVER) {
- samples = bgPss.samples;
- avg = bgPss.pss;
- } else {
- samples = cachedPss.samples;
- avg = cachedPss.pss;
- }
- double newAvg = ( (data.processStatePss[procState]
- * (double)data.processStateSamples[procState])
- + (avg*(double)samples)
- ) / (data.processStateSamples[procState]+samples);
- data.processStatePss[procState] = (long)newAvg;
- data.processStateSamples[procState] += samples;
- data.processStateWeight[procState] += avg * (double)time;
- }
- }
- }
- }
-
- static void dumpProcessState(PrintWriter pw, String prefix, ProcessState proc,
- int[] screenStates, int[] memStates, int[] procStates, long now) {
- long totalTime = 0;
- int printedScreen = -1;
- for (int is=0; is<screenStates.length; is++) {
- int printedMem = -1;
- for (int im=0; im<memStates.length; im++) {
- for (int ip=0; ip<procStates.length; ip++) {
- final int iscreen = screenStates[is];
- final int imem = memStates[im];
- final int bucket = ((iscreen + imem) * STATE_COUNT) + procStates[ip];
- long time = proc.getDuration(bucket, now);
- String running = "";
- if (proc.mCurState == bucket) {
- running = " (running)";
- }
- if (time != 0) {
- pw.print(prefix);
- if (screenStates.length > 1) {
- printScreenLabel(pw, printedScreen != iscreen
- ? iscreen : STATE_NOTHING);
- printedScreen = iscreen;
- }
- if (memStates.length > 1) {
- printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING, '/');
- printedMem = imem;
- }
- pw.print(STATE_NAMES[procStates[ip]]); pw.print(": ");
- TimeUtils.formatDuration(time, pw); pw.println(running);
- totalTime += time;
- }
- }
- }
- }
- if (totalTime != 0) {
- pw.print(prefix);
- if (screenStates.length > 1) {
- printScreenLabel(pw, STATE_NOTHING);
- }
- if (memStates.length > 1) {
- printMemLabel(pw, STATE_NOTHING, '/');
- }
- pw.print("TOTAL : ");
- TimeUtils.formatDuration(totalTime, pw);
- pw.println();
- }
- }
-
- static void dumpProcessPss(PrintWriter pw, String prefix, ProcessState proc, int[] screenStates,
- int[] memStates, int[] procStates) {
- boolean printedHeader = false;
- int printedScreen = -1;
- for (int is=0; is<screenStates.length; is++) {
- int printedMem = -1;
- for (int im=0; im<memStates.length; im++) {
- for (int ip=0; ip<procStates.length; ip++) {
- final int iscreen = screenStates[is];
- final int imem = memStates[im];
- final int bucket = ((iscreen + imem) * STATE_COUNT) + procStates[ip];
- long count = proc.getPssSampleCount(bucket);
- if (count > 0) {
- if (!printedHeader) {
- pw.print(prefix);
- pw.print("PSS/USS (");
- pw.print(proc.mPssTableSize);
- pw.println(" entries):");
- printedHeader = true;
- }
- pw.print(prefix);
- pw.print(" ");
- if (screenStates.length > 1) {
- printScreenLabel(pw, printedScreen != iscreen
- ? iscreen : STATE_NOTHING);
- printedScreen = iscreen;
- }
- if (memStates.length > 1) {
- printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING, '/');
- printedMem = imem;
- }
- pw.print(STATE_NAMES[procStates[ip]]); pw.print(": ");
- pw.print(count);
- pw.print(" samples ");
- DebugUtils.printSizeValue(pw, proc.getPssMinimum(bucket) * 1024);
- pw.print(" ");
- DebugUtils.printSizeValue(pw, proc.getPssAverage(bucket) * 1024);
- pw.print(" ");
- DebugUtils.printSizeValue(pw, proc.getPssMaximum(bucket) * 1024);
- pw.print(" / ");
- DebugUtils.printSizeValue(pw, proc.getPssUssMinimum(bucket) * 1024);
- pw.print(" ");
- DebugUtils.printSizeValue(pw, proc.getPssUssAverage(bucket) * 1024);
- pw.print(" ");
- DebugUtils.printSizeValue(pw, proc.getPssUssMaximum(bucket) * 1024);
- pw.println();
- }
- }
- }
- }
- if (proc.mNumExcessiveWake != 0) {
- pw.print(prefix); pw.print("Killed for excessive wake locks: ");
- pw.print(proc.mNumExcessiveWake); pw.println(" times");
- }
- if (proc.mNumExcessiveCpu != 0) {
- pw.print(prefix); pw.print("Killed for excessive CPU use: ");
- pw.print(proc.mNumExcessiveCpu); pw.println(" times");
- }
- if (proc.mNumCachedKill != 0) {
- pw.print(prefix); pw.print("Killed from cached state: ");
- pw.print(proc.mNumCachedKill); pw.print(" times from pss ");
- DebugUtils.printSizeValue(pw, proc.mMinCachedKillPss * 1024); pw.print("-");
- DebugUtils.printSizeValue(pw, proc.mAvgCachedKillPss * 1024); pw.print("-");
- DebugUtils.printSizeValue(pw, proc.mMaxCachedKillPss * 1024); pw.println();
- }
- }
-
- long getSysMemUsageValue(int state, int index) {
- int idx = binarySearch(mSysMemUsageTable, mSysMemUsageTableSize, state);
- return idx >= 0 ? getLong(mSysMemUsageTable[idx], index) : 0;
- }
-
- void dumpSysMemUsageCategory(PrintWriter pw, String prefix, String label,
- int bucket, int index) {
- pw.print(prefix); pw.print(label);
- pw.print(": ");
- DebugUtils.printSizeValue(pw, getSysMemUsageValue(bucket, index) * 1024);
- pw.print(" min, ");
- DebugUtils.printSizeValue(pw, getSysMemUsageValue(bucket, index + 1) * 1024);
- pw.print(" avg, ");
- DebugUtils.printSizeValue(pw, getSysMemUsageValue(bucket, index+2) * 1024);
- pw.println(" max");
- }
-
- void dumpSysMemUsage(PrintWriter pw, String prefix, int[] screenStates,
- int[] memStates) {
- int printedScreen = -1;
- for (int is=0; is<screenStates.length; is++) {
- int printedMem = -1;
- for (int im=0; im<memStates.length; im++) {
- final int iscreen = screenStates[is];
- final int imem = memStates[im];
- final int bucket = ((iscreen + imem) * STATE_COUNT);
- long count = getSysMemUsageValue(bucket, SYS_MEM_USAGE_SAMPLE_COUNT);
- if (count > 0) {
- pw.print(prefix);
- if (screenStates.length > 1) {
- printScreenLabel(pw, printedScreen != iscreen
- ? iscreen : STATE_NOTHING);
- printedScreen = iscreen;
- }
- if (memStates.length > 1) {
- printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING, '\0');
- printedMem = imem;
- }
- pw.print(": ");
- pw.print(count);
- pw.println(" samples:");
- dumpSysMemUsageCategory(pw, prefix, " Cached", bucket,
- SYS_MEM_USAGE_CACHED_MINIMUM);
- dumpSysMemUsageCategory(pw, prefix, " Free", bucket,
- SYS_MEM_USAGE_FREE_MINIMUM);
- dumpSysMemUsageCategory(pw, prefix, " ZRam", bucket,
- SYS_MEM_USAGE_ZRAM_MINIMUM);
- dumpSysMemUsageCategory(pw, prefix, " Kernel", bucket,
- SYS_MEM_USAGE_KERNEL_MINIMUM);
- dumpSysMemUsageCategory(pw, prefix, " Native", bucket,
- SYS_MEM_USAGE_NATIVE_MINIMUM);
- }
- }
- }
- }
-
- static void dumpStateHeadersCsv(PrintWriter pw, String sep, int[] screenStates,
- int[] memStates, int[] procStates) {
- final int NS = screenStates != null ? screenStates.length : 1;
- final int NM = memStates != null ? memStates.length : 1;
- final int NP = procStates != null ? procStates.length : 1;
- for (int is=0; is<NS; is++) {
- for (int im=0; im<NM; im++) {
- for (int ip=0; ip<NP; ip++) {
- pw.print(sep);
- boolean printed = false;
- if (screenStates != null && screenStates.length > 1) {
- printScreenLabelCsv(pw, screenStates[is]);
- printed = true;
- }
- if (memStates != null && memStates.length > 1) {
- if (printed) {
- pw.print("-");
- }
- printMemLabelCsv(pw, memStates[im]);
- printed = true;
- }
- if (procStates != null && procStates.length > 1) {
- if (printed) {
- pw.print("-");
- }
- pw.print(STATE_NAMES_CSV[procStates[ip]]);
- }
- }
- }
- }
- }
-
- static void dumpProcessStateCsv(PrintWriter pw, ProcessState proc,
- boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates,
- boolean sepProcStates, int[] procStates, long now) {
- final int NSS = sepScreenStates ? screenStates.length : 1;
- final int NMS = sepMemStates ? memStates.length : 1;
- final int NPS = sepProcStates ? procStates.length : 1;
- for (int iss=0; iss<NSS; iss++) {
- for (int ims=0; ims<NMS; ims++) {
- for (int ips=0; ips<NPS; ips++) {
- final int vsscreen = sepScreenStates ? screenStates[iss] : 0;
- final int vsmem = sepMemStates ? memStates[ims] : 0;
- final int vsproc = sepProcStates ? procStates[ips] : 0;
- final int NSA = sepScreenStates ? 1 : screenStates.length;
- final int NMA = sepMemStates ? 1 : memStates.length;
- final int NPA = sepProcStates ? 1 : procStates.length;
- long totalTime = 0;
- for (int isa=0; isa<NSA; isa++) {
- for (int ima=0; ima<NMA; ima++) {
- for (int ipa=0; ipa<NPA; ipa++) {
- final int vascreen = sepScreenStates ? 0 : screenStates[isa];
- final int vamem = sepMemStates ? 0 : memStates[ima];
- final int vaproc = sepProcStates ? 0 : procStates[ipa];
- final int bucket = ((vsscreen + vascreen + vsmem + vamem)
- * STATE_COUNT) + vsproc + vaproc;
- totalTime += proc.getDuration(bucket, now);
- }
- }
- }
- pw.print(CSV_SEP);
- pw.print(totalTime);
- }
- }
- }
- }
-
- static void dumpProcessList(PrintWriter pw, String prefix, ArrayList<ProcessState> procs,
- int[] screenStates, int[] memStates, int[] procStates, long now) {
- String innerPrefix = prefix + " ";
- for (int i=procs.size()-1; i>=0; i--) {
- ProcessState proc = procs.get(i);
- pw.print(prefix);
- pw.print(proc.mName);
- pw.print(" / ");
- UserHandle.formatUid(pw, proc.mUid);
- pw.print(" (");
- pw.print(proc.mDurationsTableSize);
- pw.print(" entries)");
- pw.println(":");
- dumpProcessState(pw, innerPrefix, proc, screenStates, memStates, procStates, now);
- if (proc.mPssTableSize > 0) {
- dumpProcessPss(pw, innerPrefix, proc, screenStates, memStates, procStates);
- }
- }
- }
-
- static void dumpProcessSummaryDetails(PrintWriter pw, ProcessState proc, String prefix,
- String label, int[] screenStates, int[] memStates, int[] procStates,
- long now, long totalTime, boolean full) {
- ProcessDataCollection totals = new ProcessDataCollection(screenStates,
- memStates, procStates);
- computeProcessData(proc, totals, now);
- double percentage = (double) totals.totalTime / (double) totalTime * 100;
- // We don't print percentages < .01, so just drop those.
- if (percentage >= 0.005 || totals.numPss != 0) {
- if (prefix != null) {
- pw.print(prefix);
- }
- if (label != null) {
- pw.print(label);
- }
- totals.print(pw, totalTime, full);
- if (prefix != null) {
- pw.println();
- }
- }
- }
-
- static void dumpProcessSummaryLocked(PrintWriter pw, String prefix,
- ArrayList<ProcessState> procs, int[] screenStates, int[] memStates, int[] procStates,
- boolean inclUidVers, long now, long totalTime) {
- for (int i=procs.size()-1; i>=0; i--) {
- ProcessState proc = procs.get(i);
- pw.print(prefix);
- pw.print("* ");
- pw.print(proc.mName);
- pw.print(" / ");
- UserHandle.formatUid(pw, proc.mUid);
- pw.print(" / v");
- pw.print(proc.mVersion);
- pw.println(":");
- dumpProcessSummaryDetails(pw, proc, prefix, " TOTAL: ", screenStates, memStates,
- procStates, now, totalTime, true);
- dumpProcessSummaryDetails(pw, proc, prefix, " Persistent: ", screenStates, memStates,
- new int[] { STATE_PERSISTENT }, now, totalTime, true);
- dumpProcessSummaryDetails(pw, proc, prefix, " Top: ", screenStates, memStates,
- new int[] {STATE_TOP}, now, totalTime, true);
- dumpProcessSummaryDetails(pw, proc, prefix, " Imp Fg: ", screenStates, memStates,
- new int[] { STATE_IMPORTANT_FOREGROUND }, now, totalTime, true);
- dumpProcessSummaryDetails(pw, proc, prefix, " Imp Bg: ", screenStates, memStates,
- new int[] {STATE_IMPORTANT_BACKGROUND}, now, totalTime, true);
- dumpProcessSummaryDetails(pw, proc, prefix, " Backup: ", screenStates, memStates,
- new int[] {STATE_BACKUP}, now, totalTime, true);
- dumpProcessSummaryDetails(pw, proc, prefix, " Heavy Wgt: ", screenStates, memStates,
- new int[] {STATE_HEAVY_WEIGHT}, now, totalTime, true);
- dumpProcessSummaryDetails(pw, proc, prefix, " Service: ", screenStates, memStates,
- new int[] {STATE_SERVICE}, now, totalTime, true);
- dumpProcessSummaryDetails(pw, proc, prefix, " Service Rs: ", screenStates, memStates,
- new int[] {STATE_SERVICE_RESTARTING}, now, totalTime, true);
- dumpProcessSummaryDetails(pw, proc, prefix, " Receiver: ", screenStates, memStates,
- new int[] {STATE_RECEIVER}, now, totalTime, true);
- dumpProcessSummaryDetails(pw, proc, prefix, " (Home): ", screenStates, memStates,
- new int[] {STATE_HOME}, now, totalTime, true);
- dumpProcessSummaryDetails(pw, proc, prefix, " (Last Act): ", screenStates, memStates,
- new int[] {STATE_LAST_ACTIVITY}, now, totalTime, true);
- dumpProcessSummaryDetails(pw, proc, prefix, " (Cached): ", screenStates, memStates,
- new int[] {STATE_CACHED_ACTIVITY, STATE_CACHED_ACTIVITY_CLIENT,
- STATE_CACHED_EMPTY}, now, totalTime, true);
- }
- }
-
- static void printPercent(PrintWriter pw, double fraction) {
- fraction *= 100;
- if (fraction < 1) {
- pw.print(String.format("%.2f", fraction));
- } else if (fraction < 10) {
- pw.print(String.format("%.1f", fraction));
- } else {
- pw.print(String.format("%.0f", fraction));
- }
- pw.print("%");
- }
-
- public static void dumpProcessListCsv(PrintWriter pw, ArrayList<ProcessState> procs,
- boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates,
- boolean sepProcStates, int[] procStates, long now) {
- pw.print("process");
- pw.print(CSV_SEP);
- pw.print("uid");
- pw.print(CSV_SEP);
- pw.print("vers");
- dumpStateHeadersCsv(pw, CSV_SEP, sepScreenStates ? screenStates : null,
- sepMemStates ? memStates : null,
- sepProcStates ? procStates : null);
- pw.println();
- for (int i=procs.size()-1; i>=0; i--) {
- ProcessState proc = procs.get(i);
- pw.print(proc.mName);
- pw.print(CSV_SEP);
- UserHandle.formatUid(pw, proc.mUid);
- pw.print(CSV_SEP);
- pw.print(proc.mVersion);
- dumpProcessStateCsv(pw, proc, sepScreenStates, screenStates,
- sepMemStates, memStates, sepProcStates, procStates, now);
- pw.println();
- }
- }
-
- static int printArrayEntry(PrintWriter pw, String[] array, int value, int mod) {
- int index = value/mod;
- if (index >= 0 && index < array.length) {
- pw.print(array[index]);
- } else {
- pw.print('?');
- }
- return value - index*mod;
- }
-
- static void printProcStateTag(PrintWriter pw, int state) {
- state = printArrayEntry(pw, ADJ_SCREEN_TAGS, state, ADJ_SCREEN_MOD*STATE_COUNT);
- state = printArrayEntry(pw, ADJ_MEM_TAGS, state, STATE_COUNT);
- printArrayEntry(pw, STATE_TAGS, state, 1);
- }
-
- static void printAdjTag(PrintWriter pw, int state) {
- state = printArrayEntry(pw, ADJ_SCREEN_TAGS, state, ADJ_SCREEN_MOD);
- printArrayEntry(pw, ADJ_MEM_TAGS, state, 1);
- }
-
- static void printProcStateTagAndValue(PrintWriter pw, int state, long value) {
- pw.print(',');
- printProcStateTag(pw, state);
- pw.print(':');
- pw.print(value);
- }
-
- static void printAdjTagAndValue(PrintWriter pw, int state, long value) {
- pw.print(',');
- printAdjTag(pw, state);
- pw.print(':');
- pw.print(value);
- }
-
- static void dumpAllProcessStateCheckin(PrintWriter pw, ProcessState proc, long now) {
- boolean didCurState = false;
- for (int i=0; i<proc.mDurationsTableSize; i++) {
- int off = proc.mDurationsTable[i];
- int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
- long time = proc.mStats.getLong(off, 0);
- if (proc.mCurState == type) {
- didCurState = true;
- time += now - proc.mStartTime;
- }
- printProcStateTagAndValue(pw, type, time);
- }
- if (!didCurState && proc.mCurState != STATE_NOTHING) {
- printProcStateTagAndValue(pw, proc.mCurState, now - proc.mStartTime);
- }
- }
-
- static void dumpAllProcessPssCheckin(PrintWriter pw, ProcessState proc) {
- for (int i=0; i<proc.mPssTableSize; i++) {
- int off = proc.mPssTable[i];
- int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
- long count = proc.mStats.getLong(off, PSS_SAMPLE_COUNT);
- long min = proc.mStats.getLong(off, PSS_MINIMUM);
- long avg = proc.mStats.getLong(off, PSS_AVERAGE);
- long max = proc.mStats.getLong(off, PSS_MAXIMUM);
- long umin = proc.mStats.getLong(off, PSS_USS_MINIMUM);
- long uavg = proc.mStats.getLong(off, PSS_USS_AVERAGE);
- long umax = proc.mStats.getLong(off, PSS_USS_MAXIMUM);
- pw.print(',');
- printProcStateTag(pw, type);
- pw.print(':');
- pw.print(count);
- pw.print(':');
- pw.print(min);
- pw.print(':');
- pw.print(avg);
- pw.print(':');
- pw.print(max);
- pw.print(':');
- pw.print(umin);
- pw.print(':');
- pw.print(uavg);
- pw.print(':');
- pw.print(umax);
- }
- }
-
- public void reset() {
- if (DEBUG) Slog.d(TAG, "Resetting state of " + mTimePeriodStartClockStr);
- resetCommon();
- mPackages.getMap().clear();
- mProcesses.getMap().clear();
- mMemFactor = STATE_NOTHING;
- mStartTime = 0;
- if (DEBUG) Slog.d(TAG, "State reset; now " + mTimePeriodStartClockStr);
- }
-
- public void resetSafely() {
- if (DEBUG) Slog.d(TAG, "Safely resetting state of " + mTimePeriodStartClockStr);
- resetCommon();
-
- // First initialize use count of all common processes.
- final long now = SystemClock.uptimeMillis();
- final ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
- for (int ip=procMap.size()-1; ip>=0; ip--) {
- final SparseArray<ProcessState> uids = procMap.valueAt(ip);
- for (int iu=uids.size()-1; iu>=0; iu--) {
- uids.valueAt(iu).mTmpNumInUse = 0;
- }
- }
-
- // Next reset or prune all per-package processes, and for the ones that are reset
- // track this back to the common processes.
- final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
- for (int ip=pkgMap.size()-1; ip>=0; ip--) {
- final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip);
- for (int iu=uids.size()-1; iu>=0; iu--) {
- final SparseArray<PackageState> vpkgs = uids.valueAt(iu);
- for (int iv=vpkgs.size()-1; iv>=0; iv--) {
- final PackageState pkgState = vpkgs.valueAt(iv);
- for (int iproc=pkgState.mProcesses.size()-1; iproc>=0; iproc--) {
- final ProcessState ps = pkgState.mProcesses.valueAt(iproc);
- if (ps.isInUse()) {
- ps.resetSafely(now);
- ps.mCommonProcess.mTmpNumInUse++;
- ps.mCommonProcess.mTmpFoundSubProc = ps;
- } else {
- pkgState.mProcesses.valueAt(iproc).makeDead();
- pkgState.mProcesses.removeAt(iproc);
- }
- }
- for (int isvc=pkgState.mServices.size()-1; isvc>=0; isvc--) {
- final ServiceState ss = pkgState.mServices.valueAt(isvc);
- if (ss.isInUse()) {
- ss.resetSafely(now);
- } else {
- pkgState.mServices.removeAt(isvc);
- }
- }
- if (pkgState.mProcesses.size() <= 0 && pkgState.mServices.size() <= 0) {
- vpkgs.removeAt(iv);
- }
- }
- if (vpkgs.size() <= 0) {
- uids.removeAt(iu);
- }
- }
- if (uids.size() <= 0) {
- pkgMap.removeAt(ip);
- }
- }
-
- // Finally prune out any common processes that are no longer in use.
- for (int ip=procMap.size()-1; ip>=0; ip--) {
- final SparseArray<ProcessState> uids = procMap.valueAt(ip);
- for (int iu=uids.size()-1; iu>=0; iu--) {
- ProcessState ps = uids.valueAt(iu);
- if (ps.isInUse() || ps.mTmpNumInUse > 0) {
- // If this is a process for multiple packages, we could at this point
- // be back down to one package. In that case, we want to revert back
- // to a single shared ProcessState. We can do this by converting the
- // current package-specific ProcessState up to the shared ProcessState,
- // throwing away the current one we have here (because nobody else is
- // using it).
- if (!ps.mActive && ps.mMultiPackage && ps.mTmpNumInUse == 1) {
- // Here we go...
- ps = ps.mTmpFoundSubProc;
- ps.mCommonProcess = ps;
- uids.setValueAt(iu, ps);
- } else {
- ps.resetSafely(now);
- }
- } else {
- ps.makeDead();
- uids.removeAt(iu);
- }
- }
- if (uids.size() <= 0) {
- procMap.removeAt(ip);
- }
- }
-
- mStartTime = now;
- if (DEBUG) Slog.d(TAG, "State reset; now " + mTimePeriodStartClockStr);
- }
-
- private void resetCommon() {
- mTimePeriodStartClock = System.currentTimeMillis();
- buildTimePeriodStartClockStr();
- mTimePeriodStartRealtime = mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
- mTimePeriodStartUptime = mTimePeriodEndUptime = SystemClock.uptimeMillis();
- mLongs.clear();
- mLongs.add(new long[LONGS_SIZE]);
- mNextLong = 0;
- Arrays.fill(mMemFactorDurations, 0);
- mSysMemUsageTable = null;
- mSysMemUsageTableSize = 0;
- mStartTime = 0;
- mReadError = null;
- mFlags = 0;
- evaluateSystemProperties(true);
- }
-
- public boolean evaluateSystemProperties(boolean update) {
- boolean changed = false;
- String runtime = SystemProperties.get("persist.sys.dalvik.vm.lib.2",
- VMRuntime.getRuntime().vmLibrary());
- if (!Objects.equals(runtime, mRuntime)) {
- changed = true;
- if (update) {
- mRuntime = runtime;
- }
- }
- return changed;
- }
-
- private void buildTimePeriodStartClockStr() {
- mTimePeriodStartClockStr = DateFormat.format("yyyy-MM-dd-HH-mm-ss",
- mTimePeriodStartClock).toString();
- }
-
- static final int[] BAD_TABLE = new int[0];
-
- private int[] readTableFromParcel(Parcel in, String name, String what) {
- final int size = in.readInt();
- if (size < 0) {
- Slog.w(TAG, "Ignoring existing stats; bad " + what + " table size: " + size);
- return BAD_TABLE;
- }
- if (size == 0) {
- return null;
- }
- final int[] table = new int[size];
- for (int i=0; i<size; i++) {
- table[i] = in.readInt();
- if (DEBUG_PARCEL) Slog.i(TAG, "Reading in " + name + " table #" + i + ": "
- + ProcessStats.printLongOffset(table[i]));
- if (!validateLongOffset(table[i])) {
- Slog.w(TAG, "Ignoring existing stats; bad " + what + " table entry: "
- + ProcessStats.printLongOffset(table[i]));
- return null;
- }
- }
- return table;
- }
-
- private void writeCompactedLongArray(Parcel out, long[] array, int num) {
- for (int i=0; i<num; i++) {
- long val = array[i];
- if (val < 0) {
- Slog.w(TAG, "Time val negative: " + val);
- val = 0;
- }
- if (val <= Integer.MAX_VALUE) {
- out.writeInt((int)val);
- } else {
- int top = ~((int)((val>>32)&0x7fffffff));
- int bottom = (int)(val&0xfffffff);
- out.writeInt(top);
- out.writeInt(bottom);
- }
- }
- }
-
- private void readCompactedLongArray(Parcel in, int version, long[] array, int num) {
- if (version <= 10) {
- in.readLongArray(array);
- return;
- }
- final int alen = array.length;
- if (num > alen) {
- throw new RuntimeException("bad array lengths: got " + num + " array is " + alen);
- }
- int i;
- for (i=0; i<num; i++) {
- int val = in.readInt();
- if (val >= 0) {
- array[i] = val;
- } else {
- int bottom = in.readInt();
- array[i] = (((long)~val)<<32) | bottom;
- }
- }
- while (i < alen) {
- array[i] = 0;
- i++;
- }
- }
-
- private void writeCommonString(Parcel out, String name) {
- Integer index = mCommonStringToIndex.get(name);
- if (index != null) {
- out.writeInt(index);
- return;
- }
- index = mCommonStringToIndex.size();
- mCommonStringToIndex.put(name, index);
- out.writeInt(~index);
- out.writeString(name);
- }
-
- private String readCommonString(Parcel in, int version) {
- if (version <= 9) {
- return in.readString();
- }
- int index = in.readInt();
- if (index >= 0) {
- return mIndexToCommonString.get(index);
- }
- index = ~index;
- String name = in.readString();
- while (mIndexToCommonString.size() <= index) {
- mIndexToCommonString.add(null);
- }
- mIndexToCommonString.set(index, name);
- return name;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- writeToParcel(out, SystemClock.uptimeMillis(), flags);
- }
-
- /** @hide */
- public void writeToParcel(Parcel out, long now, int flags) {
- out.writeInt(MAGIC);
- out.writeInt(PARCEL_VERSION);
- out.writeInt(STATE_COUNT);
- out.writeInt(ADJ_COUNT);
- out.writeInt(PSS_COUNT);
- out.writeInt(SYS_MEM_USAGE_COUNT);
- out.writeInt(LONGS_SIZE);
-
- mCommonStringToIndex = new ArrayMap<String, Integer>(mProcesses.mMap.size());
-
- // First commit all running times.
- ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
- final int NPROC = procMap.size();
- for (int ip=0; ip<NPROC; ip++) {
- SparseArray<ProcessState> uids = procMap.valueAt(ip);
- final int NUID = uids.size();
- for (int iu=0; iu<NUID; iu++) {
- uids.valueAt(iu).commitStateTime(now);
- }
- }
- final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
- final int NPKG = pkgMap.size();
- for (int ip=0; ip<NPKG; ip++) {
- final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip);
- final int NUID = uids.size();
- for (int iu=0; iu<NUID; iu++) {
- final SparseArray<PackageState> vpkgs = uids.valueAt(iu);
- final int NVERS = vpkgs.size();
- for (int iv=0; iv<NVERS; iv++) {
- PackageState pkgState = vpkgs.valueAt(iv);
- final int NPROCS = pkgState.mProcesses.size();
- for (int iproc=0; iproc<NPROCS; iproc++) {
- ProcessState proc = pkgState.mProcesses.valueAt(iproc);
- if (proc.mCommonProcess != proc) {
- proc.commitStateTime(now);
- }
- }
- final int NSRVS = pkgState.mServices.size();
- for (int isvc=0; isvc<NSRVS; isvc++) {
- pkgState.mServices.valueAt(isvc).commitStateTime(now);
- }
- }
- }
- }
-
- out.writeLong(mTimePeriodStartClock);
- out.writeLong(mTimePeriodStartRealtime);
- out.writeLong(mTimePeriodEndRealtime);
- out.writeLong(mTimePeriodStartUptime);
- out.writeLong(mTimePeriodEndUptime);
- out.writeString(mRuntime);
- out.writeInt(mFlags);
-
- out.writeInt(mLongs.size());
- out.writeInt(mNextLong);
- for (int i=0; i<(mLongs.size()-1); i++) {
- long[] array = mLongs.get(i);
- writeCompactedLongArray(out, array, array.length);
- }
- long[] lastLongs = mLongs.get(mLongs.size() - 1);
- writeCompactedLongArray(out, lastLongs, mNextLong);
-
- if (mMemFactor != STATE_NOTHING) {
- mMemFactorDurations[mMemFactor] += now - mStartTime;
- mStartTime = now;
- }
- writeCompactedLongArray(out, mMemFactorDurations, mMemFactorDurations.length);
-
- out.writeInt(mSysMemUsageTableSize);
- for (int i=0; i<mSysMemUsageTableSize; i++) {
- if (DEBUG_PARCEL) Slog.i(TAG, "Writing sys mem usage #" + i + ": "
- + printLongOffset(mSysMemUsageTable[i]));
- out.writeInt(mSysMemUsageTable[i]);
- }
-
- out.writeInt(NPROC);
- for (int ip=0; ip<NPROC; ip++) {
- writeCommonString(out, procMap.keyAt(ip));
- final SparseArray<ProcessState> uids = procMap.valueAt(ip);
- final int NUID = uids.size();
- out.writeInt(NUID);
- for (int iu=0; iu<NUID; iu++) {
- out.writeInt(uids.keyAt(iu));
- final ProcessState proc = uids.valueAt(iu);
- writeCommonString(out, proc.mPackage);
- out.writeInt(proc.mVersion);
- proc.writeToParcel(out, now);
- }
- }
- out.writeInt(NPKG);
- for (int ip=0; ip<NPKG; ip++) {
- writeCommonString(out, pkgMap.keyAt(ip));
- final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip);
- final int NUID = uids.size();
- out.writeInt(NUID);
- for (int iu=0; iu<NUID; iu++) {
- out.writeInt(uids.keyAt(iu));
- final SparseArray<PackageState> vpkgs = uids.valueAt(iu);
- final int NVERS = vpkgs.size();
- out.writeInt(NVERS);
- for (int iv=0; iv<NVERS; iv++) {
- out.writeInt(vpkgs.keyAt(iv));
- final PackageState pkgState = vpkgs.valueAt(iv);
- final int NPROCS = pkgState.mProcesses.size();
- out.writeInt(NPROCS);
- for (int iproc=0; iproc<NPROCS; iproc++) {
- writeCommonString(out, pkgState.mProcesses.keyAt(iproc));
- final ProcessState proc = pkgState.mProcesses.valueAt(iproc);
- if (proc.mCommonProcess == proc) {
- // This is the same as the common process we wrote above.
- out.writeInt(0);
- } else {
- // There is separate data for this package's process.
- out.writeInt(1);
- proc.writeToParcel(out, now);
- }
- }
- final int NSRVS = pkgState.mServices.size();
- out.writeInt(NSRVS);
- for (int isvc=0; isvc<NSRVS; isvc++) {
- out.writeString(pkgState.mServices.keyAt(isvc));
- final ServiceState svc = pkgState.mServices.valueAt(isvc);
- writeCommonString(out, svc.mProcessName);
- svc.writeToParcel(out, now);
- }
- }
- }
- }
-
- mCommonStringToIndex = null;
- }
-
- private boolean readCheckedInt(Parcel in, int val, String what) {
- int got;
- if ((got=in.readInt()) != val) {
- mReadError = "bad " + what + ": " + got;
- return false;
- }
- return true;
- }
-
- static byte[] readFully(InputStream stream, int[] outLen) throws IOException {
- int pos = 0;
- final int initialAvail = stream.available();
- byte[] data = new byte[initialAvail > 0 ? (initialAvail+1) : 16384];
- while (true) {
- int amt = stream.read(data, pos, data.length-pos);
- if (DEBUG_PARCEL) Slog.i("foo", "Read " + amt + " bytes at " + pos
- + " of avail " + data.length);
- if (amt < 0) {
- if (DEBUG_PARCEL) Slog.i("foo", "**** FINISHED READING: pos=" + pos
- + " len=" + data.length);
- outLen[0] = pos;
- return data;
- }
- pos += amt;
- if (pos >= data.length) {
- byte[] newData = new byte[pos+16384];
- if (DEBUG_PARCEL) Slog.i(TAG, "Copying " + pos + " bytes to new array len "
- + newData.length);
- System.arraycopy(data, 0, newData, 0, pos);
- data = newData;
- }
- }
- }
-
- public void read(InputStream stream) {
- try {
- int[] len = new int[1];
- byte[] raw = readFully(stream, len);
- Parcel in = Parcel.obtain();
- in.unmarshall(raw, 0, len[0]);
- in.setDataPosition(0);
- stream.close();
-
- readFromParcel(in);
- } catch (IOException e) {
- mReadError = "caught exception: " + e;
- }
- }
-
- public void readFromParcel(Parcel in) {
- final boolean hadData = mPackages.getMap().size() > 0
- || mProcesses.getMap().size() > 0;
- if (hadData) {
- resetSafely();
- }
-
- if (!readCheckedInt(in, MAGIC, "magic number")) {
- return;
- }
- int version = in.readInt();
- if (version != PARCEL_VERSION) {
- mReadError = "bad version: " + version;
- return;
- }
- if (!readCheckedInt(in, STATE_COUNT, "state count")) {
- return;
- }
- if (!readCheckedInt(in, ADJ_COUNT, "adj count")) {
- return;
- }
- if (!readCheckedInt(in, PSS_COUNT, "pss count")) {
- return;
- }
- if (!readCheckedInt(in, SYS_MEM_USAGE_COUNT, "sys mem usage count")) {
- return;
- }
- if (!readCheckedInt(in, LONGS_SIZE, "longs size")) {
- return;
- }
-
- mIndexToCommonString = new ArrayList<String>();
-
- mTimePeriodStartClock = in.readLong();
- buildTimePeriodStartClockStr();
- mTimePeriodStartRealtime = in.readLong();
- mTimePeriodEndRealtime = in.readLong();
- mTimePeriodStartUptime = in.readLong();
- mTimePeriodEndUptime = in.readLong();
- mRuntime = in.readString();
- mFlags = in.readInt();
-
- final int NLONGS = in.readInt();
- final int NEXTLONG = in.readInt();
- mLongs.clear();
- for (int i=0; i<(NLONGS-1); i++) {
- while (i >= mLongs.size()) {
- mLongs.add(new long[LONGS_SIZE]);
- }
- readCompactedLongArray(in, version, mLongs.get(i), LONGS_SIZE);
- }
- long[] longs = new long[LONGS_SIZE];
- mNextLong = NEXTLONG;
- readCompactedLongArray(in, version, longs, NEXTLONG);
- mLongs.add(longs);
-
- readCompactedLongArray(in, version, mMemFactorDurations, mMemFactorDurations.length);
-
- mSysMemUsageTable = readTableFromParcel(in, TAG, "sys mem usage");
- if (mSysMemUsageTable == BAD_TABLE) {
- return;
- }
- mSysMemUsageTableSize = mSysMemUsageTable != null ? mSysMemUsageTable.length : 0;
-
- int NPROC = in.readInt();
- if (NPROC < 0) {
- mReadError = "bad process count: " + NPROC;
- return;
- }
- while (NPROC > 0) {
- NPROC--;
- final String procName = readCommonString(in, version);
- if (procName == null) {
- mReadError = "bad process name";
- return;
- }
- int NUID = in.readInt();
- if (NUID < 0) {
- mReadError = "bad uid count: " + NUID;
- return;
- }
- while (NUID > 0) {
- NUID--;
- final int uid = in.readInt();
- if (uid < 0) {
- mReadError = "bad uid: " + uid;
- return;
- }
- final String pkgName = readCommonString(in, version);
- if (pkgName == null) {
- mReadError = "bad process package name";
- return;
- }
- final int vers = in.readInt();
- ProcessState proc = hadData ? mProcesses.get(procName, uid) : null;
- if (proc != null) {
- if (!proc.readFromParcel(in, false)) {
- return;
- }
- } else {
- proc = new ProcessState(this, pkgName, uid, vers, procName);
- if (!proc.readFromParcel(in, true)) {
- return;
- }
- }
- if (DEBUG_PARCEL) Slog.d(TAG, "Adding process: " + procName + " " + uid
- + " " + proc);
- mProcesses.put(procName, uid, proc);
- }
- }
-
- if (DEBUG_PARCEL) Slog.d(TAG, "Read " + mProcesses.getMap().size() + " processes");
-
- int NPKG = in.readInt();
- if (NPKG < 0) {
- mReadError = "bad package count: " + NPKG;
- return;
- }
- while (NPKG > 0) {
- NPKG--;
- final String pkgName = readCommonString(in, version);
- if (pkgName == null) {
- mReadError = "bad package name";
- return;
- }
- int NUID = in.readInt();
- if (NUID < 0) {
- mReadError = "bad uid count: " + NUID;
- return;
- }
- while (NUID > 0) {
- NUID--;
- final int uid = in.readInt();
- if (uid < 0) {
- mReadError = "bad uid: " + uid;
- return;
- }
- int NVERS = in.readInt();
- if (NVERS < 0) {
- mReadError = "bad versions count: " + NVERS;
- return;
- }
- while (NVERS > 0) {
- NVERS--;
- final int vers = in.readInt();
- PackageState pkgState = new PackageState(pkgName, uid);
- SparseArray<PackageState> vpkg = mPackages.get(pkgName, uid);
- if (vpkg == null) {
- vpkg = new SparseArray<PackageState>();
- mPackages.put(pkgName, uid, vpkg);
- }
- vpkg.put(vers, pkgState);
- int NPROCS = in.readInt();
- if (NPROCS < 0) {
- mReadError = "bad package process count: " + NPROCS;
- return;
- }
- while (NPROCS > 0) {
- NPROCS--;
- String procName = readCommonString(in, version);
- if (procName == null) {
- mReadError = "bad package process name";
- return;
- }
- int hasProc = in.readInt();
- if (DEBUG_PARCEL) Slog.d(TAG, "Reading package " + pkgName + " " + uid
- + " process " + procName + " hasProc=" + hasProc);
- ProcessState commonProc = mProcesses.get(procName, uid);
- if (DEBUG_PARCEL) Slog.d(TAG, "Got common proc " + procName + " " + uid
- + ": " + commonProc);
- if (commonProc == null) {
- mReadError = "no common proc: " + procName;
- return;
- }
- if (hasProc != 0) {
- // The process for this package is unique to the package; we
- // need to load it. We don't need to do anything about it if
- // it is not unique because if someone later looks for it
- // they will find and use it from the global procs.
- ProcessState proc = hadData ? pkgState.mProcesses.get(procName) : null;
- if (proc != null) {
- if (!proc.readFromParcel(in, false)) {
- return;
- }
- } else {
- proc = new ProcessState(commonProc, pkgName, uid, vers, procName,
- 0);
- if (!proc.readFromParcel(in, true)) {
- return;
- }
- }
- if (DEBUG_PARCEL) Slog.d(TAG, "Adding package " + pkgName + " process: "
- + procName + " " + uid + " " + proc);
- pkgState.mProcesses.put(procName, proc);
- } else {
- if (DEBUG_PARCEL) Slog.d(TAG, "Adding package " + pkgName + " process: "
- + procName + " " + uid + " " + commonProc);
- pkgState.mProcesses.put(procName, commonProc);
- }
- }
- int NSRVS = in.readInt();
- if (NSRVS < 0) {
- mReadError = "bad package service count: " + NSRVS;
- return;
- }
- while (NSRVS > 0) {
- NSRVS--;
- String serviceName = in.readString();
- if (serviceName == null) {
- mReadError = "bad package service name";
- return;
- }
- String processName = version > 9 ? readCommonString(in, version) : null;
- ServiceState serv = hadData ? pkgState.mServices.get(serviceName) : null;
- if (serv == null) {
- serv = new ServiceState(this, pkgName, serviceName, processName, null);
- }
- if (!serv.readFromParcel(in)) {
- return;
- }
- if (DEBUG_PARCEL) Slog.d(TAG, "Adding package " + pkgName + " service: "
- + serviceName + " " + uid + " " + serv);
- pkgState.mServices.put(serviceName, serv);
- }
- }
- }
- }
-
- mIndexToCommonString = null;
-
- if (DEBUG_PARCEL) Slog.d(TAG, "Successfully read procstats!");
- }
-
- int addLongData(int index, int type, int num) {
- int off = allocLongData(num);
- mAddLongTable = GrowingArrayUtils.insert(
- mAddLongTable != null ? mAddLongTable : EmptyArray.INT,
- mAddLongTableSize, index, type | off);
- mAddLongTableSize++;
- return off;
- }
-
- int allocLongData(int num) {
- int whichLongs = mLongs.size()-1;
- long[] longs = mLongs.get(whichLongs);
- if (mNextLong + num > longs.length) {
- longs = new long[LONGS_SIZE];
- mLongs.add(longs);
- whichLongs++;
- mNextLong = 0;
- }
- int off = (whichLongs<<OFFSET_ARRAY_SHIFT) | (mNextLong<<OFFSET_INDEX_SHIFT);
- mNextLong += num;
- return off;
- }
-
- boolean validateLongOffset(int off) {
- int arr = (off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK;
- if (arr >= mLongs.size()) {
- return false;
- }
- int idx = (off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK;
- if (idx >= LONGS_SIZE) {
- return false;
- }
- if (DEBUG_PARCEL) Slog.d(TAG, "Validated long " + printLongOffset(off)
- + ": " + getLong(off, 0));
- return true;
- }
-
- static String printLongOffset(int off) {
- StringBuilder sb = new StringBuilder(16);
- sb.append("a"); sb.append((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
- sb.append("i"); sb.append((off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK);
- sb.append("t"); sb.append((off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK);
- return sb.toString();
- }
-
- void setLong(int off, int index, long value) {
- long[] longs = mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
- longs[index + ((off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK)] = value;
- }
-
- long getLong(int off, int index) {
- long[] longs = mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
- return longs[index + ((off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK)];
- }
-
- static int binarySearch(int[] array, int size, int value) {
- int lo = 0;
- int hi = size - 1;
-
- while (lo <= hi) {
- int mid = (lo + hi) >>> 1;
- int midVal = (array[mid] >> OFFSET_TYPE_SHIFT) & OFFSET_TYPE_MASK;
-
- if (midVal < value) {
- lo = mid + 1;
- } else if (midVal > value) {
- hi = mid - 1;
- } else {
- return mid; // value found
- }
- }
- return ~lo; // value not present
- }
-
- public PackageState getPackageStateLocked(String packageName, int uid, int vers) {
- SparseArray<PackageState> vpkg = mPackages.get(packageName, uid);
- if (vpkg == null) {
- vpkg = new SparseArray<PackageState>();
- mPackages.put(packageName, uid, vpkg);
- }
- PackageState as = vpkg.get(vers);
- if (as != null) {
- return as;
- }
- as = new PackageState(packageName, uid);
- vpkg.put(vers, as);
- return as;
- }
-
- public ProcessState getProcessStateLocked(String packageName, int uid, int vers,
- String processName) {
- final PackageState pkgState = getPackageStateLocked(packageName, uid, vers);
- ProcessState ps = pkgState.mProcesses.get(processName);
- if (ps != null) {
- return ps;
- }
- ProcessState commonProc = mProcesses.get(processName, uid);
- if (commonProc == null) {
- commonProc = new ProcessState(this, packageName, uid, vers, processName);
- mProcesses.put(processName, uid, commonProc);
- if (DEBUG) Slog.d(TAG, "GETPROC created new common " + commonProc);
- }
- if (!commonProc.mMultiPackage) {
- if (packageName.equals(commonProc.mPackage) && vers == commonProc.mVersion) {
- // This common process is not in use by multiple packages, and
- // is for the calling package, so we can just use it directly.
- ps = commonProc;
- if (DEBUG) Slog.d(TAG, "GETPROC also using for pkg " + commonProc);
- } else {
- if (DEBUG) Slog.d(TAG, "GETPROC need to split common proc!");
- // This common process has not been in use by multiple packages,
- // but it was created for a different package than the caller.
- // We need to convert it to a multi-package process.
- commonProc.mMultiPackage = true;
- // To do this, we need to make two new process states, one a copy
- // of the current state for the process under the original package
- // name, and the second a free new process state for it as the
- // new package name.
- long now = SystemClock.uptimeMillis();
- // First let's make a copy of the current process state and put
- // that under the now unique state for its original package name.
- final PackageState commonPkgState = getPackageStateLocked(commonProc.mPackage,
- uid, commonProc.mVersion);
- if (commonPkgState != null) {
- ProcessState cloned = commonProc.clone(commonProc.mPackage, now);
- if (DEBUG) Slog.d(TAG, "GETPROC setting clone to pkg " + commonProc.mPackage
- + ": " + cloned);
- commonPkgState.mProcesses.put(commonProc.mName, cloned);
- // If this has active services, we need to update their process pointer
- // to point to the new package-specific process state.
- for (int i=commonPkgState.mServices.size()-1; i>=0; i--) {
- ServiceState ss = commonPkgState.mServices.valueAt(i);
- if (ss.mProc == commonProc) {
- if (DEBUG) Slog.d(TAG, "GETPROC switching service to cloned: "
- + ss);
- ss.mProc = cloned;
- } else if (DEBUG) {
- Slog.d(TAG, "GETPROC leaving proc of " + ss);
- }
- }
- } else {
- Slog.w(TAG, "Cloning proc state: no package state " + commonProc.mPackage
- + "/" + uid + " for proc " + commonProc.mName);
- }
- // And now make a fresh new process state for the new package name.
- ps = new ProcessState(commonProc, packageName, uid, vers, processName, now);
- if (DEBUG) Slog.d(TAG, "GETPROC created new pkg " + ps);
- }
- } else {
- // The common process is for multiple packages, we need to create a
- // separate object for the per-package data.
- ps = new ProcessState(commonProc, packageName, uid, vers, processName,
- SystemClock.uptimeMillis());
- if (DEBUG) Slog.d(TAG, "GETPROC created new pkg " + ps);
- }
- pkgState.mProcesses.put(processName, ps);
- if (DEBUG) Slog.d(TAG, "GETPROC adding new pkg " + ps);
- return ps;
- }
-
- public ProcessStats.ServiceState getServiceStateLocked(String packageName, int uid, int vers,
- String processName, String className) {
- final ProcessStats.PackageState as = getPackageStateLocked(packageName, uid, vers);
- ProcessStats.ServiceState ss = as.mServices.get(className);
- if (ss != null) {
- if (DEBUG) Slog.d(TAG, "GETSVC: returning existing " + ss);
- return ss;
- }
- final ProcessStats.ProcessState ps = processName != null
- ? getProcessStateLocked(packageName, uid, vers, processName) : null;
- ss = new ProcessStats.ServiceState(this, packageName, className, processName, ps);
- as.mServices.put(className, ss);
- if (DEBUG) Slog.d(TAG, "GETSVC: creating " + ss + " in " + ps);
- return ss;
- }
-
- private void dumpProcessInternalLocked(PrintWriter pw, String prefix, ProcessState proc,
- boolean dumpAll) {
- if (dumpAll) {
- pw.print(prefix); pw.print("myID=");
- pw.print(Integer.toHexString(System.identityHashCode(proc)));
- pw.print(" mCommonProcess=");
- pw.print(Integer.toHexString(System.identityHashCode(proc.mCommonProcess)));
- pw.print(" mPackage="); pw.println(proc.mPackage);
- if (proc.mMultiPackage) {
- pw.print(prefix); pw.print("mMultiPackage="); pw.println(proc.mMultiPackage);
- }
- if (proc != proc.mCommonProcess) {
- pw.print(prefix); pw.print("Common Proc: "); pw.print(proc.mCommonProcess.mName);
- pw.print("/"); pw.print(proc.mCommonProcess.mUid);
- pw.print(" pkg="); pw.println(proc.mCommonProcess.mPackage);
- }
- }
- if (proc.mActive) {
- pw.print(prefix); pw.print("mActive="); pw.println(proc.mActive);
- }
- if (proc.mDead) {
- pw.print(prefix); pw.print("mDead="); pw.println(proc.mDead);
- }
- if (proc.mNumActiveServices != 0 || proc.mNumStartedServices != 0) {
- pw.print(prefix); pw.print("mNumActiveServices="); pw.print(proc.mNumActiveServices);
- pw.print(" mNumStartedServices=");
- pw.println(proc.mNumStartedServices);
- }
- }
-
- public void dumpLocked(PrintWriter pw, String reqPackage, long now, boolean dumpSummary,
- boolean dumpAll, boolean activeOnly) {
- long totalTime = dumpSingleTime(null, null, mMemFactorDurations, mMemFactor,
- mStartTime, now);
- boolean sepNeeded = false;
- if (mSysMemUsageTable != null) {
- pw.println("System memory usage:");
- dumpSysMemUsage(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ);
- sepNeeded = true;
- }
- ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
- boolean printedHeader = false;
- for (int ip=0; ip<pkgMap.size(); ip++) {
- final String pkgName = pkgMap.keyAt(ip);
- final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip);
- for (int iu=0; iu<uids.size(); iu++) {
- final int uid = uids.keyAt(iu);
- final SparseArray<PackageState> vpkgs = uids.valueAt(iu);
- for (int iv=0; iv<vpkgs.size(); iv++) {
- final int vers = vpkgs.keyAt(iv);
- final PackageState pkgState = vpkgs.valueAt(iv);
- final int NPROCS = pkgState.mProcesses.size();
- final int NSRVS = pkgState.mServices.size();
- final boolean pkgMatch = reqPackage == null || reqPackage.equals(pkgName);
- if (!pkgMatch) {
- boolean procMatch = false;
- for (int iproc=0; iproc<NPROCS; iproc++) {
- ProcessState proc = pkgState.mProcesses.valueAt(iproc);
- if (reqPackage.equals(proc.mName)) {
- procMatch = true;
- break;
- }
- }
- if (!procMatch) {
- continue;
- }
- }
- if (NPROCS > 0 || NSRVS > 0) {
- if (!printedHeader) {
- if (sepNeeded) pw.println();
- pw.println("Per-Package Stats:");
- printedHeader = true;
- sepNeeded = true;
- }
- pw.print(" * "); pw.print(pkgName); pw.print(" / ");
- UserHandle.formatUid(pw, uid); pw.print(" / v");
- pw.print(vers); pw.println(":");
- }
- if (!dumpSummary || dumpAll) {
- for (int iproc=0; iproc<NPROCS; iproc++) {
- ProcessState proc = pkgState.mProcesses.valueAt(iproc);
- if (!pkgMatch && !reqPackage.equals(proc.mName)) {
- continue;
- }
- if (activeOnly && !proc.isInUse()) {
- pw.print(" (Not active: ");
- pw.print(pkgState.mProcesses.keyAt(iproc)); pw.println(")");
- continue;
- }
- pw.print(" Process ");
- pw.print(pkgState.mProcesses.keyAt(iproc));
- if (proc.mCommonProcess.mMultiPackage) {
- pw.print(" (multi, ");
- } else {
- pw.print(" (unique, ");
- }
- pw.print(proc.mDurationsTableSize);
- pw.print(" entries)");
- pw.println(":");
- dumpProcessState(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
- ALL_PROC_STATES, now);
- dumpProcessPss(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
- ALL_PROC_STATES);
- dumpProcessInternalLocked(pw, " ", proc, dumpAll);
- }
- } else {
- ArrayList<ProcessState> procs = new ArrayList<ProcessState>();
- for (int iproc=0; iproc<NPROCS; iproc++) {
- ProcessState proc = pkgState.mProcesses.valueAt(iproc);
- if (!pkgMatch && !reqPackage.equals(proc.mName)) {
- continue;
- }
- if (activeOnly && !proc.isInUse()) {
- continue;
- }
- procs.add(proc);
- }
- dumpProcessSummaryLocked(pw, " ", procs, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
- NON_CACHED_PROC_STATES, false, now, totalTime);
- }
- for (int isvc=0; isvc<NSRVS; isvc++) {
- ServiceState svc = pkgState.mServices.valueAt(isvc);
- if (!pkgMatch && !reqPackage.equals(svc.mProcessName)) {
- continue;
- }
- if (activeOnly && !svc.isInUse()) {
- pw.print(" (Not active: ");
- pw.print(pkgState.mServices.keyAt(isvc)); pw.println(")");
- continue;
- }
- if (dumpAll) {
- pw.print(" Service ");
- } else {
- pw.print(" * ");
- }
- pw.print(pkgState.mServices.keyAt(isvc));
- pw.println(":");
- pw.print(" Process: "); pw.println(svc.mProcessName);
- dumpServiceStats(pw, " ", " ", " ", "Running", svc,
- svc.mRunCount, ServiceState.SERVICE_RUN, svc.mRunState,
- svc.mRunStartTime, now, totalTime, !dumpSummary || dumpAll);
- dumpServiceStats(pw, " ", " ", " ", "Started", svc,
- svc.mStartedCount, ServiceState.SERVICE_STARTED, svc.mStartedState,
- svc.mStartedStartTime, now, totalTime, !dumpSummary || dumpAll);
- dumpServiceStats(pw, " ", " ", " ", "Bound", svc,
- svc.mBoundCount, ServiceState.SERVICE_BOUND, svc.mBoundState,
- svc.mBoundStartTime, now, totalTime, !dumpSummary || dumpAll);
- dumpServiceStats(pw, " ", " ", " ", "Executing", svc,
- svc.mExecCount, ServiceState.SERVICE_EXEC, svc.mExecState,
- svc.mExecStartTime, now, totalTime, !dumpSummary || dumpAll);
- if (dumpAll) {
- if (svc.mOwner != null) {
- pw.print(" mOwner="); pw.println(svc.mOwner);
- }
- if (svc.mStarted || svc.mRestarting) {
- pw.print(" mStarted="); pw.print(svc.mStarted);
- pw.print(" mRestarting="); pw.println(svc.mRestarting);
- }
- }
- }
- }
- }
- }
-
- ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
- printedHeader = false;
- int numShownProcs = 0, numTotalProcs = 0;
- for (int ip=0; ip<procMap.size(); ip++) {
- String procName = procMap.keyAt(ip);
- SparseArray<ProcessState> uids = procMap.valueAt(ip);
- for (int iu=0; iu<uids.size(); iu++) {
- int uid = uids.keyAt(iu);
- numTotalProcs++;
- ProcessState proc = uids.valueAt(iu);
- if (proc.mDurationsTableSize == 0 && proc.mCurState == STATE_NOTHING
- && proc.mPssTableSize == 0) {
- continue;
- }
- if (!proc.mMultiPackage) {
- continue;
- }
- if (reqPackage != null && !reqPackage.equals(procName)
- && !reqPackage.equals(proc.mPackage)) {
- continue;
- }
- numShownProcs++;
- if (sepNeeded) {
- pw.println();
- }
- sepNeeded = true;
- if (!printedHeader) {
- pw.println("Multi-Package Common Processes:");
- printedHeader = true;
- }
- if (activeOnly && !proc.isInUse()) {
- pw.print(" (Not active: "); pw.print(procName); pw.println(")");
- continue;
- }
- pw.print(" * "); pw.print(procName); pw.print(" / ");
- UserHandle.formatUid(pw, uid);
- pw.print(" ("); pw.print(proc.mDurationsTableSize);
- pw.print(" entries)"); pw.println(":");
- dumpProcessState(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
- ALL_PROC_STATES, now);
- dumpProcessPss(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
- ALL_PROC_STATES);
- dumpProcessInternalLocked(pw, " ", proc, dumpAll);
- }
- }
- if (dumpAll) {
- pw.println();
- pw.print(" Total procs: "); pw.print(numShownProcs);
- pw.print(" shown of "); pw.print(numTotalProcs); pw.println(" total");
- }
-
- if (sepNeeded) {
- pw.println();
- }
- if (dumpSummary) {
- pw.println("Summary:");
- dumpSummaryLocked(pw, reqPackage, now, activeOnly);
- } else {
- dumpTotalsLocked(pw, now);
- }
-
- if (dumpAll) {
- pw.println();
- pw.println("Internal state:");
- pw.print(" Num long arrays: "); pw.println(mLongs.size());
- pw.print(" Next long entry: "); pw.println(mNextLong);
- pw.print(" mRunning="); pw.println(mRunning);
- }
- }
-
- public static long dumpSingleServiceTime(PrintWriter pw, String prefix, ServiceState service,
- int serviceType, int curState, long curStartTime, long now) {
- long totalTime = 0;
- int printedScreen = -1;
- for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) {
- int printedMem = -1;
- for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) {
- int state = imem+iscreen;
- long time = service.getDuration(serviceType, curState, curStartTime,
- state, now);
- String running = "";
- if (curState == state && pw != null) {
- running = " (running)";
- }
- if (time != 0) {
- if (pw != null) {
- pw.print(prefix);
- printScreenLabel(pw, printedScreen != iscreen
- ? iscreen : STATE_NOTHING);
- printedScreen = iscreen;
- printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING, (char)0);
- printedMem = imem;
- pw.print(": ");
- TimeUtils.formatDuration(time, pw); pw.println(running);
- }
- totalTime += time;
- }
- }
- }
- if (totalTime != 0 && pw != null) {
- pw.print(prefix);
- pw.print(" TOTAL: ");
- TimeUtils.formatDuration(totalTime, pw);
- pw.println();
- }
- return totalTime;
- }
-
- void dumpServiceStats(PrintWriter pw, String prefix, String prefixInner,
- String headerPrefix, String header, ServiceState service,
- int count, int serviceType, int state, long startTime, long now, long totalTime,
- boolean dumpAll) {
- if (count != 0) {
- if (dumpAll) {
- pw.print(prefix); pw.print(header);
- pw.print(" op count "); pw.print(count); pw.println(":");
- dumpSingleServiceTime(pw, prefixInner, service, serviceType, state, startTime,
- now);
- } else {
- long myTime = dumpSingleServiceTime(null, null, service, serviceType, state,
- startTime, now);
- pw.print(prefix); pw.print(headerPrefix); pw.print(header);
- pw.print(" count "); pw.print(count);
- pw.print(" / time ");
- printPercent(pw, (double)myTime/(double)totalTime);
- pw.println();
- }
- }
- }
-
- public void dumpSummaryLocked(PrintWriter pw, String reqPackage, long now, boolean activeOnly) {
- long totalTime = dumpSingleTime(null, null, mMemFactorDurations, mMemFactor,
- mStartTime, now);
- dumpFilteredSummaryLocked(pw, null, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ,
- ALL_PROC_STATES, NON_CACHED_PROC_STATES, now, totalTime, reqPackage, activeOnly);
- pw.println();
- dumpTotalsLocked(pw, now);
- }
-
- long printMemoryCategory(PrintWriter pw, String prefix, String label, double memWeight,
- long totalTime, long curTotalMem, int samples) {
- if (memWeight != 0) {
- long mem = (long)(memWeight * 1024 / totalTime);
- pw.print(prefix);
- pw.print(label);
- pw.print(": ");
- DebugUtils.printSizeValue(pw, mem);
- pw.print(" (");
- pw.print(samples);
- pw.print(" samples)");
- pw.println();
- return curTotalMem + mem;
- }
- return curTotalMem;
- }
-
- void dumpTotalsLocked(PrintWriter pw, long now) {
- pw.println("Run time Stats:");
- dumpSingleTime(pw, " ", mMemFactorDurations, mMemFactor, mStartTime, now);
- pw.println();
- pw.println("Memory usage:");
- TotalMemoryUseCollection totalMem = new TotalMemoryUseCollection(ALL_SCREEN_ADJ,
- ALL_MEM_ADJ);
- computeTotalMemoryUse(totalMem, now);
- long totalPss = 0;
- totalPss = printMemoryCategory(pw, " ", "Kernel ", totalMem.sysMemKernelWeight,
- totalMem.totalTime, totalPss, totalMem.sysMemSamples);
- totalPss = printMemoryCategory(pw, " ", "Native ", totalMem.sysMemNativeWeight,
- totalMem.totalTime, totalPss, totalMem.sysMemSamples);
- for (int i=0; i<STATE_COUNT; i++) {
- // Skip restarting service state -- that is not actually a running process.
- if (i != STATE_SERVICE_RESTARTING) {
- totalPss = printMemoryCategory(pw, " ", STATE_NAMES[i],
- totalMem.processStateWeight[i], totalMem.totalTime, totalPss,
- totalMem.processStateSamples[i]);
- }
- }
- totalPss = printMemoryCategory(pw, " ", "Cached ", totalMem.sysMemCachedWeight,
- totalMem.totalTime, totalPss, totalMem.sysMemSamples);
- totalPss = printMemoryCategory(pw, " ", "Free ", totalMem.sysMemFreeWeight,
- totalMem.totalTime, totalPss, totalMem.sysMemSamples);
- totalPss = printMemoryCategory(pw, " ", "Z-Ram ", totalMem.sysMemZRamWeight,
- totalMem.totalTime, totalPss, totalMem.sysMemSamples);
- pw.print(" TOTAL : ");
- DebugUtils.printSizeValue(pw, totalPss);
- pw.println();
- printMemoryCategory(pw, " ", STATE_NAMES[STATE_SERVICE_RESTARTING],
- totalMem.processStateWeight[STATE_SERVICE_RESTARTING], totalMem.totalTime, totalPss,
- totalMem.processStateSamples[STATE_SERVICE_RESTARTING]);
- pw.println();
- pw.print(" Start time: ");
- pw.print(DateFormat.format("yyyy-MM-dd HH:mm:ss", mTimePeriodStartClock));
- pw.println();
- pw.print(" Total elapsed time: ");
- TimeUtils.formatDuration(
- (mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime)
- - mTimePeriodStartRealtime, pw);
- boolean partial = true;
- if ((mFlags&FLAG_SHUTDOWN) != 0) {
- pw.print(" (shutdown)");
- partial = false;
- }
- if ((mFlags&FLAG_SYSPROPS) != 0) {
- pw.print(" (sysprops)");
- partial = false;
- }
- if ((mFlags&FLAG_COMPLETE) != 0) {
- pw.print(" (complete)");
- partial = false;
- }
- if (partial) {
- pw.print(" (partial)");
- }
- pw.print(' ');
- pw.print(mRuntime);
- pw.println();
- }
-
- void dumpFilteredSummaryLocked(PrintWriter pw, String header, String prefix,
- int[] screenStates, int[] memStates, int[] procStates,
- int[] sortProcStates, long now, long totalTime, String reqPackage, boolean activeOnly) {
- ArrayList<ProcessState> procs = collectProcessesLocked(screenStates, memStates,
- procStates, sortProcStates, now, reqPackage, activeOnly);
- if (procs.size() > 0) {
- if (header != null) {
- pw.println();
- pw.println(header);
- }
- dumpProcessSummaryLocked(pw, prefix, procs, screenStates, memStates,
- sortProcStates, true, now, totalTime);
- }
- }
-
- public ArrayList<ProcessState> collectProcessesLocked(int[] screenStates, int[] memStates,
- int[] procStates, int sortProcStates[], long now, String reqPackage,
- boolean activeOnly) {
- final ArraySet<ProcessState> foundProcs = new ArraySet<ProcessState>();
- final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
- for (int ip=0; ip<pkgMap.size(); ip++) {
- final String pkgName = pkgMap.keyAt(ip);
- final SparseArray<SparseArray<PackageState>> procs = pkgMap.valueAt(ip);
- for (int iu=0; iu<procs.size(); iu++) {
- final SparseArray<PackageState> vpkgs = procs.valueAt(iu);
- final int NVERS = vpkgs.size();
- for (int iv=0; iv<NVERS; iv++) {
- final PackageState state = vpkgs.valueAt(iv);
- final int NPROCS = state.mProcesses.size();
- final boolean pkgMatch = reqPackage == null || reqPackage.equals(pkgName);
- for (int iproc=0; iproc<NPROCS; iproc++) {
- final ProcessState proc = state.mProcesses.valueAt(iproc);
- if (!pkgMatch && !reqPackage.equals(proc.mName)) {
- continue;
- }
- if (activeOnly && !proc.isInUse()) {
- continue;
- }
- foundProcs.add(proc.mCommonProcess);
- }
- }
- }
- }
- ArrayList<ProcessState> outProcs = new ArrayList<ProcessState>(foundProcs.size());
- for (int i=0; i<foundProcs.size(); i++) {
- ProcessState proc = foundProcs.valueAt(i);
- if (computeProcessTimeLocked(proc, screenStates, memStates, procStates, now) > 0) {
- outProcs.add(proc);
- if (procStates != sortProcStates) {
- computeProcessTimeLocked(proc, screenStates, memStates, sortProcStates, now);
- }
- }
- }
- Collections.sort(outProcs, new Comparator<ProcessState>() {
- @Override
- public int compare(ProcessState lhs, ProcessState rhs) {
- if (lhs.mTmpTotalTime < rhs.mTmpTotalTime) {
- return -1;
- } else if (lhs.mTmpTotalTime > rhs.mTmpTotalTime) {
- return 1;
- }
- return 0;
- }
- });
- return outProcs;
- }
-
- String collapseString(String pkgName, String itemName) {
- if (itemName.startsWith(pkgName)) {
- final int ITEMLEN = itemName.length();
- final int PKGLEN = pkgName.length();
- if (ITEMLEN == PKGLEN) {
- return "";
- } else if (ITEMLEN >= PKGLEN) {
- if (itemName.charAt(PKGLEN) == '.') {
- return itemName.substring(PKGLEN);
- }
- }
- }
- return itemName;
- }
-
- public void dumpCheckinLocked(PrintWriter pw, String reqPackage) {
- final long now = SystemClock.uptimeMillis();
- final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
- pw.println("vers,5");
- pw.print("period,"); pw.print(mTimePeriodStartClockStr);
- pw.print(","); pw.print(mTimePeriodStartRealtime); pw.print(",");
- pw.print(mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime);
- boolean partial = true;
- if ((mFlags&FLAG_SHUTDOWN) != 0) {
- pw.print(",shutdown");
- partial = false;
- }
- if ((mFlags&FLAG_SYSPROPS) != 0) {
- pw.print(",sysprops");
- partial = false;
- }
- if ((mFlags&FLAG_COMPLETE) != 0) {
- pw.print(",complete");
- partial = false;
- }
- if (partial) {
- pw.print(",partial");
- }
- pw.println();
- pw.print("config,"); pw.println(mRuntime);
- for (int ip=0; ip<pkgMap.size(); ip++) {
- final String pkgName = pkgMap.keyAt(ip);
- if (reqPackage != null && !reqPackage.equals(pkgName)) {
- continue;
- }
- final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip);
- for (int iu=0; iu<uids.size(); iu++) {
- final int uid = uids.keyAt(iu);
- final SparseArray<PackageState> vpkgs = uids.valueAt(iu);
- for (int iv=0; iv<vpkgs.size(); iv++) {
- final int vers = vpkgs.keyAt(iv);
- final PackageState pkgState = vpkgs.valueAt(iv);
- final int NPROCS = pkgState.mProcesses.size();
- final int NSRVS = pkgState.mServices.size();
- for (int iproc=0; iproc<NPROCS; iproc++) {
- ProcessState proc = pkgState.mProcesses.valueAt(iproc);
- pw.print("pkgproc,");
- pw.print(pkgName);
- pw.print(",");
- pw.print(uid);
- pw.print(",");
- pw.print(vers);
- pw.print(",");
- pw.print(collapseString(pkgName, pkgState.mProcesses.keyAt(iproc)));
- dumpAllProcessStateCheckin(pw, proc, now);
- pw.println();
- if (proc.mPssTableSize > 0) {
- pw.print("pkgpss,");
- pw.print(pkgName);
- pw.print(",");
- pw.print(uid);
- pw.print(",");
- pw.print(vers);
- pw.print(",");
- pw.print(collapseString(pkgName, pkgState.mProcesses.keyAt(iproc)));
- dumpAllProcessPssCheckin(pw, proc);
- pw.println();
- }
- if (proc.mNumExcessiveWake > 0 || proc.mNumExcessiveCpu > 0
- || proc.mNumCachedKill > 0) {
- pw.print("pkgkills,");
- pw.print(pkgName);
- pw.print(",");
- pw.print(uid);
- pw.print(",");
- pw.print(vers);
- pw.print(",");
- pw.print(collapseString(pkgName, pkgState.mProcesses.keyAt(iproc)));
- pw.print(",");
- pw.print(proc.mNumExcessiveWake);
- pw.print(",");
- pw.print(proc.mNumExcessiveCpu);
- pw.print(",");
- pw.print(proc.mNumCachedKill);
- pw.print(",");
- pw.print(proc.mMinCachedKillPss);
- pw.print(":");
- pw.print(proc.mAvgCachedKillPss);
- pw.print(":");
- pw.print(proc.mMaxCachedKillPss);
- pw.println();
- }
- }
- for (int isvc=0; isvc<NSRVS; isvc++) {
- String serviceName = collapseString(pkgName,
- pkgState.mServices.keyAt(isvc));
- ServiceState svc = pkgState.mServices.valueAt(isvc);
- dumpServiceTimeCheckin(pw, "pkgsvc-run", pkgName, uid, vers, serviceName,
- svc, ServiceState.SERVICE_RUN, svc.mRunCount,
- svc.mRunState, svc.mRunStartTime, now);
- dumpServiceTimeCheckin(pw, "pkgsvc-start", pkgName, uid, vers, serviceName,
- svc, ServiceState.SERVICE_STARTED, svc.mStartedCount,
- svc.mStartedState, svc.mStartedStartTime, now);
- dumpServiceTimeCheckin(pw, "pkgsvc-bound", pkgName, uid, vers, serviceName,
- svc, ServiceState.SERVICE_BOUND, svc.mBoundCount,
- svc.mBoundState, svc.mBoundStartTime, now);
- dumpServiceTimeCheckin(pw, "pkgsvc-exec", pkgName, uid, vers, serviceName,
- svc, ServiceState.SERVICE_EXEC, svc.mExecCount,
- svc.mExecState, svc.mExecStartTime, now);
- }
- }
- }
- }
-
- ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
- for (int ip=0; ip<procMap.size(); ip++) {
- String procName = procMap.keyAt(ip);
- SparseArray<ProcessState> uids = procMap.valueAt(ip);
- for (int iu=0; iu<uids.size(); iu++) {
- int uid = uids.keyAt(iu);
- ProcessState procState = uids.valueAt(iu);
- if (procState.mDurationsTableSize > 0) {
- pw.print("proc,");
- pw.print(procName);
- pw.print(",");
- pw.print(uid);
- dumpAllProcessStateCheckin(pw, procState, now);
- pw.println();
- }
- if (procState.mPssTableSize > 0) {
- pw.print("pss,");
- pw.print(procName);
- pw.print(",");
- pw.print(uid);
- dumpAllProcessPssCheckin(pw, procState);
- pw.println();
- }
- if (procState.mNumExcessiveWake > 0 || procState.mNumExcessiveCpu > 0
- || procState.mNumCachedKill > 0) {
- pw.print("kills,");
- pw.print(procName);
- pw.print(",");
- pw.print(uid);
- pw.print(",");
- pw.print(procState.mNumExcessiveWake);
- pw.print(",");
- pw.print(procState.mNumExcessiveCpu);
- pw.print(",");
- pw.print(procState.mNumCachedKill);
- pw.print(",");
- pw.print(procState.mMinCachedKillPss);
- pw.print(":");
- pw.print(procState.mAvgCachedKillPss);
- pw.print(":");
- pw.print(procState.mMaxCachedKillPss);
- pw.println();
- }
- }
- }
- pw.print("total");
- dumpAdjTimesCheckin(pw, ",", mMemFactorDurations, mMemFactor,
- mStartTime, now);
- pw.println();
- if (mSysMemUsageTable != null) {
- pw.print("sysmemusage");
- for (int i=0; i<mSysMemUsageTableSize; i++) {
- int off = mSysMemUsageTable[i];
- int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
- pw.print(",");
- printProcStateTag(pw, type);
- for (int j=SYS_MEM_USAGE_SAMPLE_COUNT; j<SYS_MEM_USAGE_COUNT; j++) {
- if (j > SYS_MEM_USAGE_CACHED_MINIMUM) {
- pw.print(":");
- }
- pw.print(getLong(off, j));
- }
- }
- }
- pw.println();
- TotalMemoryUseCollection totalMem = new TotalMemoryUseCollection(ALL_SCREEN_ADJ,
- ALL_MEM_ADJ);
- computeTotalMemoryUse(totalMem, now);
- pw.print("weights,");
- pw.print(totalMem.totalTime);
- pw.print(",");
- pw.print(totalMem.sysMemCachedWeight);
- pw.print(":");
- pw.print(totalMem.sysMemSamples);
- pw.print(",");
- pw.print(totalMem.sysMemFreeWeight);
- pw.print(":");
- pw.print(totalMem.sysMemSamples);
- pw.print(",");
- pw.print(totalMem.sysMemZRamWeight);
- pw.print(":");
- pw.print(totalMem.sysMemSamples);
- pw.print(",");
- pw.print(totalMem.sysMemKernelWeight);
- pw.print(":");
- pw.print(totalMem.sysMemSamples);
- pw.print(",");
- pw.print(totalMem.sysMemNativeWeight);
- pw.print(":");
- pw.print(totalMem.sysMemSamples);
- for (int i=0; i<STATE_COUNT; i++) {
- pw.print(",");
- pw.print(totalMem.processStateWeight[i]);
- pw.print(":");
- pw.print(totalMem.processStateSamples[i]);
- }
- pw.println();
- }
-
- public static class DurationsTable {
- public final ProcessStats mStats;
- public final String mName;
- public int[] mDurationsTable;
- public int mDurationsTableSize;
-
- public DurationsTable(ProcessStats stats, String name) {
- mStats = stats;
- mName = name;
- }
-
- void copyDurationsTo(DurationsTable other) {
- if (mDurationsTable != null) {
- mStats.mAddLongTable = new int[mDurationsTable.length];
- mStats.mAddLongTableSize = 0;
- for (int i=0; i<mDurationsTableSize; i++) {
- int origEnt = mDurationsTable[i];
- int type = (origEnt>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
- int newOff = mStats.addLongData(i, type, 1);
- mStats.mAddLongTable[i] = newOff | type;
- mStats.setLong(newOff, 0, mStats.getLong(origEnt, 0));
- }
- other.mDurationsTable = mStats.mAddLongTable;
- other.mDurationsTableSize = mStats.mAddLongTableSize;
- } else {
- other.mDurationsTable = null;
- other.mDurationsTableSize = 0;
- }
- }
-
- void addDurations(DurationsTable other) {
- for (int i=0; i<other.mDurationsTableSize; i++) {
- int ent = other.mDurationsTable[i];
- int state = (ent>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
- if (DEBUG) Slog.d(TAG, "Adding state " + state + " duration "
- + other.mStats.getLong(ent, 0));
- addDuration(state, other.mStats.getLong(ent, 0));
- }
- }
-
- void resetDurationsSafely() {
- mDurationsTable = null;
- mDurationsTableSize = 0;
- }
-
- void writeDurationsToParcel(Parcel out) {
- out.writeInt(mDurationsTableSize);
- for (int i=0; i<mDurationsTableSize; i++) {
- if (DEBUG_PARCEL) Slog.i(TAG, "Writing in " + mName + " dur #" + i + ": "
- + printLongOffset(mDurationsTable[i]));
- out.writeInt(mDurationsTable[i]);
- }
- }
-
- boolean readDurationsFromParcel(Parcel in) {
- mDurationsTable = mStats.readTableFromParcel(in, mName, "durations");
- if (mDurationsTable == BAD_TABLE) {
- return false;
- }
- mDurationsTableSize = mDurationsTable != null ? mDurationsTable.length : 0;
- return true;
- }
-
- void addDuration(int state, long dur) {
- int idx = binarySearch(mDurationsTable, mDurationsTableSize, state);
- int off;
- if (idx >= 0) {
- off = mDurationsTable[idx];
- } else {
- mStats.mAddLongTable = mDurationsTable;
- mStats.mAddLongTableSize = mDurationsTableSize;
- off = mStats.addLongData(~idx, state, 1);
- mDurationsTable = mStats.mAddLongTable;
- mDurationsTableSize = mStats.mAddLongTableSize;
- }
- long[] longs = mStats.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
- if (DEBUG) Slog.d(TAG, "Duration of " + mName + " state " + state + " inc by " + dur
- + " from " + longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK]);
- longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK] += dur;
- }
-
- long getDuration(int state, long now) {
- int idx = binarySearch(mDurationsTable, mDurationsTableSize, state);
- return idx >= 0 ? mStats.getLong(mDurationsTable[idx], 0) : 0;
- }
- }
-
- final public static class ProcessStateHolder {
- public final int appVersion;
- public ProcessStats.ProcessState state;
-
- public ProcessStateHolder(int _appVersion) {
- appVersion = _appVersion;
- }
- }
-
- public static final class ProcessState extends DurationsTable {
- public ProcessState mCommonProcess;
- public final String mPackage;
- public final int mUid;
- public final int mVersion;
-
- //final long[] mDurations = new long[STATE_COUNT*ADJ_COUNT];
- int mCurState = STATE_NOTHING;
- long mStartTime;
-
- int mLastPssState = STATE_NOTHING;
- long mLastPssTime;
- int[] mPssTable;
- int mPssTableSize;
-
- boolean mActive;
- int mNumActiveServices;
- int mNumStartedServices;
-
- int mNumExcessiveWake;
- int mNumExcessiveCpu;
-
- int mNumCachedKill;
- long mMinCachedKillPss;
- long mAvgCachedKillPss;
- long mMaxCachedKillPss;
-
- boolean mMultiPackage;
- boolean mDead;
-
- public long mTmpTotalTime;
- int mTmpNumInUse;
- ProcessState mTmpFoundSubProc;
-
- /**
- * Create a new top-level process state, for the initial case where there is only
- * a single package running in a process. The initial state is not running.
- */
- public ProcessState(ProcessStats processStats, String pkg, int uid, int vers, String name) {
- super(processStats, name);
- mCommonProcess = this;
- mPackage = pkg;
- mUid = uid;
- mVersion = vers;
- }
-
- /**
- * Create a new per-package process state for an existing top-level process
- * state. The current running state of the top-level process is also copied,
- * marked as started running at 'now'.
- */
- public ProcessState(ProcessState commonProcess, String pkg, int uid, int vers, String name,
- long now) {
- super(commonProcess.mStats, name);
- mCommonProcess = commonProcess;
- mPackage = pkg;
- mUid = uid;
- mVersion = vers;
- mCurState = commonProcess.mCurState;
- mStartTime = now;
- }
-
- ProcessState clone(String pkg, long now) {
- ProcessState pnew = new ProcessState(this, pkg, mUid, mVersion, mName, now);
- copyDurationsTo(pnew);
- if (mPssTable != null) {
- mStats.mAddLongTable = new int[mPssTable.length];
- mStats.mAddLongTableSize = 0;
- for (int i=0; i<mPssTableSize; i++) {
- int origEnt = mPssTable[i];
- int type = (origEnt>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
- int newOff = mStats.addLongData(i, type, PSS_COUNT);
- mStats.mAddLongTable[i] = newOff | type;
- for (int j=0; j<PSS_COUNT; j++) {
- mStats.setLong(newOff, j, mStats.getLong(origEnt, j));
- }
- }
- pnew.mPssTable = mStats.mAddLongTable;
- pnew.mPssTableSize = mStats.mAddLongTableSize;
- }
- pnew.mNumExcessiveWake = mNumExcessiveWake;
- pnew.mNumExcessiveCpu = mNumExcessiveCpu;
- pnew.mNumCachedKill = mNumCachedKill;
- pnew.mMinCachedKillPss = mMinCachedKillPss;
- pnew.mAvgCachedKillPss = mAvgCachedKillPss;
- pnew.mMaxCachedKillPss = mMaxCachedKillPss;
- pnew.mActive = mActive;
- pnew.mNumActiveServices = mNumActiveServices;
- pnew.mNumStartedServices = mNumStartedServices;
- return pnew;
- }
-
- void add(ProcessState other) {
- addDurations(other);
- for (int i=0; i<other.mPssTableSize; i++) {
- int ent = other.mPssTable[i];
- int state = (ent>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
- addPss(state, (int) other.mStats.getLong(ent, PSS_SAMPLE_COUNT),
- other.mStats.getLong(ent, PSS_MINIMUM),
- other.mStats.getLong(ent, PSS_AVERAGE),
- other.mStats.getLong(ent, PSS_MAXIMUM),
- other.mStats.getLong(ent, PSS_USS_MINIMUM),
- other.mStats.getLong(ent, PSS_USS_AVERAGE),
- other.mStats.getLong(ent, PSS_USS_MAXIMUM));
- }
- mNumExcessiveWake += other.mNumExcessiveWake;
- mNumExcessiveCpu += other.mNumExcessiveCpu;
- if (other.mNumCachedKill > 0) {
- addCachedKill(other.mNumCachedKill, other.mMinCachedKillPss,
- other.mAvgCachedKillPss, other.mMaxCachedKillPss);
- }
- }
-
- void resetSafely(long now) {
- resetDurationsSafely();
- mStartTime = now;
- mLastPssState = STATE_NOTHING;
- mLastPssTime = 0;
- mPssTable = null;
- mPssTableSize = 0;
- mNumExcessiveWake = 0;
- mNumExcessiveCpu = 0;
- mNumCachedKill = 0;
- mMinCachedKillPss = mAvgCachedKillPss = mMaxCachedKillPss = 0;
- }
-
- void makeDead() {
- mDead = true;
- }
-
- private void ensureNotDead() {
- if (!mDead) {
- return;
- }
- Slog.wtfStack(TAG, "ProcessState dead: name=" + mName
- + " pkg=" + mPackage + " uid=" + mUid + " common.name=" + mCommonProcess.mName);
- }
-
- void writeToParcel(Parcel out, long now) {
- out.writeInt(mMultiPackage ? 1 : 0);
- writeDurationsToParcel(out);
- out.writeInt(mPssTableSize);
- for (int i=0; i<mPssTableSize; i++) {
- if (DEBUG_PARCEL) Slog.i(TAG, "Writing in " + mName + " pss #" + i + ": "
- + printLongOffset(mPssTable[i]));
- out.writeInt(mPssTable[i]);
- }
- out.writeInt(mNumExcessiveWake);
- out.writeInt(mNumExcessiveCpu);
- out.writeInt(mNumCachedKill);
- if (mNumCachedKill > 0) {
- out.writeLong(mMinCachedKillPss);
- out.writeLong(mAvgCachedKillPss);
- out.writeLong(mMaxCachedKillPss);
- }
- }
-
- boolean readFromParcel(Parcel in, boolean fully) {
- boolean multiPackage = in.readInt() != 0;
- if (fully) {
- mMultiPackage = multiPackage;
- }
- if (DEBUG_PARCEL) Slog.d(TAG, "Reading durations table...");
- if (!readDurationsFromParcel(in)) {
- return false;
- }
- if (DEBUG_PARCEL) Slog.d(TAG, "Reading pss table...");
- mPssTable = mStats.readTableFromParcel(in, mName, "pss");
- if (mPssTable == BAD_TABLE) {
- return false;
- }
- mPssTableSize = mPssTable != null ? mPssTable.length : 0;
- mNumExcessiveWake = in.readInt();
- mNumExcessiveCpu = in.readInt();
- mNumCachedKill = in.readInt();
- if (mNumCachedKill > 0) {
- mMinCachedKillPss = in.readLong();
- mAvgCachedKillPss = in.readLong();
- mMaxCachedKillPss = in.readLong();
- } else {
- mMinCachedKillPss = mAvgCachedKillPss = mMaxCachedKillPss = 0;
- }
- return true;
- }
-
- public void makeActive() {
- ensureNotDead();
- mActive = true;
- }
-
- public void makeInactive() {
- mActive = false;
- }
-
- public boolean isInUse() {
- return mActive || mNumActiveServices > 0 || mNumStartedServices > 0
- || mCurState != STATE_NOTHING;
- }
-
- /**
- * Update the current state of the given list of processes.
- *
- * @param state Current ActivityManager.PROCESS_STATE_*
- * @param memFactor Current mem factor constant.
- * @param now Current time.
- * @param pkgList Processes to update.
- */
- public void setState(int state, int memFactor, long now,
- ArrayMap<String, ProcessStateHolder> pkgList) {
- if (state < 0) {
- state = mNumStartedServices > 0
- ? (STATE_SERVICE_RESTARTING+(memFactor*STATE_COUNT)) : STATE_NOTHING;
- } else {
- state = PROCESS_STATE_TO_STATE[state] + (memFactor*STATE_COUNT);
- }
-
- // First update the common process.
- mCommonProcess.setState(state, now);
-
- // If the common process is not multi-package, there is nothing else to do.
- if (!mCommonProcess.mMultiPackage) {
- return;
- }
-
- if (pkgList != null) {
- for (int ip=pkgList.size()-1; ip>=0; ip--) {
- pullFixedProc(pkgList, ip).setState(state, now);
- }
- }
- }
-
- void setState(int state, long now) {
- ensureNotDead();
- if (mCurState != state) {
- //Slog.i(TAG, "Setting state in " + mName + "/" + mPackage + ": " + state);
- commitStateTime(now);
- mCurState = state;
- }
- }
-
- void commitStateTime(long now) {
- if (mCurState != STATE_NOTHING) {
- long dur = now - mStartTime;
- if (dur > 0) {
- addDuration(mCurState, dur);
- }
- }
- mStartTime = now;
- }
-
- void incActiveServices(String serviceName) {
- if (DEBUG && "".equals(mName)) {
- RuntimeException here = new RuntimeException("here");
- here.fillInStackTrace();
- Slog.d(TAG, "incActiveServices: " + this + " service=" + serviceName
- + " to " + (mNumActiveServices+1), here);
- }
- if (mCommonProcess != this) {
- mCommonProcess.incActiveServices(serviceName);
- }
- mNumActiveServices++;
- }
-
- void decActiveServices(String serviceName) {
- if (DEBUG && "".equals(mName)) {
- RuntimeException here = new RuntimeException("here");
- here.fillInStackTrace();
- Slog.d(TAG, "decActiveServices: " + this + " service=" + serviceName
- + " to " + (mNumActiveServices-1), here);
- }
- if (mCommonProcess != this) {
- mCommonProcess.decActiveServices(serviceName);
- }
- mNumActiveServices--;
- if (mNumActiveServices < 0) {
- Slog.wtfStack(TAG, "Proc active services underrun: pkg=" + mPackage
- + " uid=" + mUid + " proc=" + mName + " service=" + serviceName);
- mNumActiveServices = 0;
- }
- }
-
- void incStartedServices(int memFactor, long now, String serviceName) {
- if (false) {
- RuntimeException here = new RuntimeException("here");
- here.fillInStackTrace();
- Slog.d(TAG, "incStartedServices: " + this + " service=" + serviceName
- + " to " + (mNumStartedServices+1), here);
- }
- if (mCommonProcess != this) {
- mCommonProcess.incStartedServices(memFactor, now, serviceName);
- }
- mNumStartedServices++;
- if (mNumStartedServices == 1 && mCurState == STATE_NOTHING) {
- setState(STATE_SERVICE_RESTARTING + (memFactor*STATE_COUNT), now);
- }
- }
-
- void decStartedServices(int memFactor, long now, String serviceName) {
- if (false) {
- RuntimeException here = new RuntimeException("here");
- here.fillInStackTrace();
- Slog.d(TAG, "decActiveServices: " + this + " service=" + serviceName
- + " to " + (mNumStartedServices-1), here);
- }
- if (mCommonProcess != this) {
- mCommonProcess.decStartedServices(memFactor, now, serviceName);
- }
- mNumStartedServices--;
- if (mNumStartedServices == 0 && (mCurState%STATE_COUNT) == STATE_SERVICE_RESTARTING) {
- setState(STATE_NOTHING, now);
- } else if (mNumStartedServices < 0) {
- Slog.wtfStack(TAG, "Proc started services underrun: pkg="
- + mPackage + " uid=" + mUid + " name=" + mName);
- mNumStartedServices = 0;
- }
- }
-
- public void addPss(long pss, long uss, boolean always,
- ArrayMap<String, ProcessStateHolder> pkgList) {
- ensureNotDead();
- if (!always) {
- if (mLastPssState == mCurState && SystemClock.uptimeMillis()
- < (mLastPssTime+(30*1000))) {
- return;
- }
- }
- mLastPssState = mCurState;
- mLastPssTime = SystemClock.uptimeMillis();
- if (mCurState != STATE_NOTHING) {
- // First update the common process.
- mCommonProcess.addPss(mCurState, 1, pss, pss, pss, uss, uss, uss);
-
- // If the common process is not multi-package, there is nothing else to do.
- if (!mCommonProcess.mMultiPackage) {
- return;
- }
-
- if (pkgList != null) {
- for (int ip=pkgList.size()-1; ip>=0; ip--) {
- pullFixedProc(pkgList, ip).addPss(mCurState, 1,
- pss, pss, pss, uss, uss, uss);
- }
- }
- }
- }
-
- void addPss(int state, int inCount, long minPss, long avgPss, long maxPss, long minUss,
- long avgUss, long maxUss) {
- int idx = binarySearch(mPssTable, mPssTableSize, state);
- int off;
- if (idx >= 0) {
- off = mPssTable[idx];
- } else {
- mStats.mAddLongTable = mPssTable;
- mStats.mAddLongTableSize = mPssTableSize;
- off = mStats.addLongData(~idx, state, PSS_COUNT);
- mPssTable = mStats.mAddLongTable;
- mPssTableSize = mStats.mAddLongTableSize;
- }
- long[] longs = mStats.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
- idx = (off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK;
- long count = longs[idx+PSS_SAMPLE_COUNT];
- if (count == 0) {
- longs[idx+PSS_SAMPLE_COUNT] = inCount;
- longs[idx+PSS_MINIMUM] = minPss;
- longs[idx+PSS_AVERAGE] = avgPss;
- longs[idx+PSS_MAXIMUM] = maxPss;
- longs[idx+PSS_USS_MINIMUM] = minUss;
- longs[idx+PSS_USS_AVERAGE] = avgUss;
- longs[idx+PSS_USS_MAXIMUM] = maxUss;
- } else {
- longs[idx+PSS_SAMPLE_COUNT] = count+inCount;
- if (longs[idx+PSS_MINIMUM] > minPss) {
- longs[idx+PSS_MINIMUM] = minPss;
- }
- longs[idx+PSS_AVERAGE] = (long)(
- ((longs[idx+PSS_AVERAGE]*(double)count)+(avgPss*(double)inCount))
- / (count+inCount) );
- if (longs[idx+PSS_MAXIMUM] < maxPss) {
- longs[idx+PSS_MAXIMUM] = maxPss;
- }
- if (longs[idx+PSS_USS_MINIMUM] > minUss) {
- longs[idx+PSS_USS_MINIMUM] = minUss;
- }
- longs[idx+PSS_USS_AVERAGE] = (long)(
- ((longs[idx+PSS_USS_AVERAGE]*(double)count)+(avgUss*(double)inCount))
- / (count+inCount) );
- if (longs[idx+PSS_USS_MAXIMUM] < maxUss) {
- longs[idx+PSS_USS_MAXIMUM] = maxUss;
- }
- }
- }
-
- public void reportExcessiveWake(ArrayMap<String, ProcessStateHolder> pkgList) {
- ensureNotDead();
- mCommonProcess.mNumExcessiveWake++;
- if (!mCommonProcess.mMultiPackage) {
- return;
- }
-
- for (int ip=pkgList.size()-1; ip>=0; ip--) {
- pullFixedProc(pkgList, ip).mNumExcessiveWake++;
- }
- }
-
- public void reportExcessiveCpu(ArrayMap<String, ProcessStateHolder> pkgList) {
- ensureNotDead();
- mCommonProcess.mNumExcessiveCpu++;
- if (!mCommonProcess.mMultiPackage) {
- return;
- }
-
- for (int ip=pkgList.size()-1; ip>=0; ip--) {
- pullFixedProc(pkgList, ip).mNumExcessiveCpu++;
- }
- }
-
- private void addCachedKill(int num, long minPss, long avgPss, long maxPss) {
- if (mNumCachedKill <= 0) {
- mNumCachedKill = num;
- mMinCachedKillPss = minPss;
- mAvgCachedKillPss = avgPss;
- mMaxCachedKillPss = maxPss;
- } else {
- if (minPss < mMinCachedKillPss) {
- mMinCachedKillPss = minPss;
- }
- if (maxPss > mMaxCachedKillPss) {
- mMaxCachedKillPss = maxPss;
- }
- mAvgCachedKillPss = (long)( ((mAvgCachedKillPss*(double)mNumCachedKill) + avgPss)
- / (mNumCachedKill+num) );
- mNumCachedKill += num;
- }
- }
-
- public void reportCachedKill(ArrayMap<String, ProcessStateHolder> pkgList, long pss) {
- ensureNotDead();
- mCommonProcess.addCachedKill(1, pss, pss, pss);
- if (!mCommonProcess.mMultiPackage) {
- return;
- }
-
- for (int ip=pkgList.size()-1; ip>=0; ip--) {
- pullFixedProc(pkgList, ip).addCachedKill(1, pss, pss, pss);
- }
- }
-
- ProcessState pullFixedProc(String pkgName) {
- if (mMultiPackage) {
- // The array map is still pointing to a common process state
- // that is now shared across packages. Update it to point to
- // the new per-package state.
- SparseArray<PackageState> vpkg = mStats.mPackages.get(pkgName, mUid);
- if (vpkg == null) {
- throw new IllegalStateException("Didn't find package " + pkgName
- + " / " + mUid);
- }
- PackageState pkg = vpkg.get(mVersion);
- if (pkg == null) {
- throw new IllegalStateException("Didn't find package " + pkgName
- + " / " + mUid + " vers " + mVersion);
- }
- ProcessState proc = pkg.mProcesses.get(mName);
- if (proc == null) {
- throw new IllegalStateException("Didn't create per-package process "
- + mName + " in pkg " + pkgName + " / " + mUid + " vers " + mVersion);
- }
- return proc;
- }
- return this;
- }
-
- private ProcessState pullFixedProc(ArrayMap<String, ProcessStateHolder> pkgList,
- int index) {
- ProcessStateHolder holder = pkgList.valueAt(index);
- ProcessState proc = holder.state;
- if (mDead && proc.mCommonProcess != proc) {
- // Somehow we are contining to use a process state that is dead, because
- // it was not being told it was active during the last commit. We can recover
- // from this by generating a fresh new state, but this is bad because we
- // are losing whatever data we had in the old process state.
- Log.wtf(TAG, "Pulling dead proc: name=" + mName + " pkg=" + mPackage
- + " uid=" + mUid + " common.name=" + mCommonProcess.mName);
- proc = mStats.getProcessStateLocked(proc.mPackage, proc.mUid, proc.mVersion,
- proc.mName);
- }
- if (proc.mMultiPackage) {
- // The array map is still pointing to a common process state
- // that is now shared across packages. Update it to point to
- // the new per-package state.
- SparseArray<PackageState> vpkg = mStats.mPackages.get(pkgList.keyAt(index),
- proc.mUid);
- if (vpkg == null) {
- throw new IllegalStateException("No existing package "
- + pkgList.keyAt(index) + "/" + proc.mUid
- + " for multi-proc " + proc.mName);
- }
- PackageState pkg = vpkg.get(proc.mVersion);
- if (pkg == null) {
- throw new IllegalStateException("No existing package "
- + pkgList.keyAt(index) + "/" + proc.mUid
- + " for multi-proc " + proc.mName + " version " + proc.mVersion);
- }
- String savedName = proc.mName;
- proc = pkg.mProcesses.get(proc.mName);
- if (proc == null) {
- throw new IllegalStateException("Didn't create per-package process "
- + savedName + " in pkg " + pkg.mPackageName + "/" + pkg.mUid);
- }
- holder.state = proc;
- }
- return proc;
- }
-
- long getDuration(int state, long now) {
- long time = super.getDuration(state, now);
- if (mCurState == state) {
- time += now - mStartTime;
- }
- return time;
- }
-
- long getPssSampleCount(int state) {
- int idx = binarySearch(mPssTable, mPssTableSize, state);
- return idx >= 0 ? mStats.getLong(mPssTable[idx], PSS_SAMPLE_COUNT) : 0;
- }
-
- long getPssMinimum(int state) {
- int idx = binarySearch(mPssTable, mPssTableSize, state);
- return idx >= 0 ? mStats.getLong(mPssTable[idx], PSS_MINIMUM) : 0;
- }
-
- long getPssAverage(int state) {
- int idx = binarySearch(mPssTable, mPssTableSize, state);
- return idx >= 0 ? mStats.getLong(mPssTable[idx], PSS_AVERAGE) : 0;
- }
-
- long getPssMaximum(int state) {
- int idx = binarySearch(mPssTable, mPssTableSize, state);
- return idx >= 0 ? mStats.getLong(mPssTable[idx], PSS_MAXIMUM) : 0;
- }
-
- long getPssUssMinimum(int state) {
- int idx = binarySearch(mPssTable, mPssTableSize, state);
- return idx >= 0 ? mStats.getLong(mPssTable[idx], PSS_USS_MINIMUM) : 0;
- }
-
- long getPssUssAverage(int state) {
- int idx = binarySearch(mPssTable, mPssTableSize, state);
- return idx >= 0 ? mStats.getLong(mPssTable[idx], PSS_USS_AVERAGE) : 0;
- }
-
- long getPssUssMaximum(int state) {
- int idx = binarySearch(mPssTable, mPssTableSize, state);
- return idx >= 0 ? mStats.getLong(mPssTable[idx], PSS_USS_MAXIMUM) : 0;
- }
-
- public String toString() {
- StringBuilder sb = new StringBuilder(128);
- sb.append("ProcessState{").append(Integer.toHexString(System.identityHashCode(this)))
- .append(" ").append(mName).append("/").append(mUid)
- .append(" pkg=").append(mPackage);
- if (mMultiPackage) sb.append(" (multi)");
- if (mCommonProcess != this) sb.append(" (sub)");
- sb.append("}");
- return sb.toString();
- }
- }
-
- public static final class ServiceState extends DurationsTable {
- public final String mPackage;
- public final String mProcessName;
- ProcessState mProc;
-
- Object mOwner;
-
- public static final int SERVICE_RUN = 0;
- public static final int SERVICE_STARTED = 1;
- public static final int SERVICE_BOUND = 2;
- public static final int SERVICE_EXEC = 3;
- static final int SERVICE_COUNT = 4;
-
- int mRunCount;
- public int mRunState = STATE_NOTHING;
- long mRunStartTime;
-
- boolean mStarted;
- boolean mRestarting;
- int mStartedCount;
- public int mStartedState = STATE_NOTHING;
- long mStartedStartTime;
-
- int mBoundCount;
- public int mBoundState = STATE_NOTHING;
- long mBoundStartTime;
-
- int mExecCount;
- public int mExecState = STATE_NOTHING;
- long mExecStartTime;
-
- public ServiceState(ProcessStats processStats, String pkg, String name,
- String processName, ProcessState proc) {
- super(processStats, name);
- mPackage = pkg;
- mProcessName = processName;
- mProc = proc;
- }
-
- public void applyNewOwner(Object newOwner) {
- if (mOwner != newOwner) {
- if (mOwner == null) {
- mOwner = newOwner;
- mProc.incActiveServices(mName);
- } else {
- // There was already an old owner, reset this object for its
- // new owner.
- mOwner = newOwner;
- if (mStarted || mBoundState != STATE_NOTHING || mExecState != STATE_NOTHING) {
- long now = SystemClock.uptimeMillis();
- if (mStarted) {
- if (DEBUG) Slog.d(TAG, "Service has new owner " + newOwner
- + " from " + mOwner + " while started: pkg="
- + mPackage + " service=" + mName + " proc=" + mProc);
- setStarted(false, 0, now);
- }
- if (mBoundState != STATE_NOTHING) {
- if (DEBUG) Slog.d(TAG, "Service has new owner " + newOwner
- + " from " + mOwner + " while bound: pkg="
- + mPackage + " service=" + mName + " proc=" + mProc);
- setBound(false, 0, now);
- }
- if (mExecState != STATE_NOTHING) {
- if (DEBUG) Slog.d(TAG, "Service has new owner " + newOwner
- + " from " + mOwner + " while executing: pkg="
- + mPackage + " service=" + mName + " proc=" + mProc);
- setExecuting(false, 0, now);
- }
- }
- }
- }
- }
-
- public void clearCurrentOwner(Object owner, boolean silently) {
- if (mOwner == owner) {
- mProc.decActiveServices(mName);
- if (mStarted || mBoundState != STATE_NOTHING || mExecState != STATE_NOTHING) {
- long now = SystemClock.uptimeMillis();
- if (mStarted) {
- if (!silently) {
- Slog.wtfStack(TAG, "Service owner " + owner
- + " cleared while started: pkg=" + mPackage + " service="
- + mName + " proc=" + mProc);
- }
- setStarted(false, 0, now);
- }
- if (mBoundState != STATE_NOTHING) {
- if (!silently) {
- Slog.wtfStack(TAG, "Service owner " + owner
- + " cleared while bound: pkg=" + mPackage + " service="
- + mName + " proc=" + mProc);
- }
- setBound(false, 0, now);
- }
- if (mExecState != STATE_NOTHING) {
- if (!silently) {
- Slog.wtfStack(TAG, "Service owner " + owner
- + " cleared while exec: pkg=" + mPackage + " service="
- + mName + " proc=" + mProc);
- }
- setExecuting(false, 0, now);
- }
- }
- mOwner = null;
- }
- }
-
- public boolean isInUse() {
- return mOwner != null || mRestarting;
- }
-
- public boolean isRestarting() {
- return mRestarting;
- }
-
- void add(ServiceState other) {
- addDurations(other);
- mRunCount += other.mRunCount;
- mStartedCount += other.mStartedCount;
- mBoundCount += other.mBoundCount;
- mExecCount += other.mExecCount;
- }
-
- void resetSafely(long now) {
- resetDurationsSafely();
- mRunCount = mRunState != STATE_NOTHING ? 1 : 0;
- mStartedCount = mStartedState != STATE_NOTHING ? 1 : 0;
- mBoundCount = mBoundState != STATE_NOTHING ? 1 : 0;
- mExecCount = mExecState != STATE_NOTHING ? 1 : 0;
- mRunStartTime = mStartedStartTime = mBoundStartTime = mExecStartTime = now;
- }
-
- void writeToParcel(Parcel out, long now) {
- writeDurationsToParcel(out);
- out.writeInt(mRunCount);
- out.writeInt(mStartedCount);
- out.writeInt(mBoundCount);
- out.writeInt(mExecCount);
- }
-
- boolean readFromParcel(Parcel in) {
- if (!readDurationsFromParcel(in)) {
- return false;
- }
- mRunCount = in.readInt();
- mStartedCount = in.readInt();
- mBoundCount = in.readInt();
- mExecCount = in.readInt();
- return true;
- }
-
- void commitStateTime(long now) {
- if (mRunState != STATE_NOTHING) {
- addDuration(SERVICE_RUN + (mRunState*SERVICE_COUNT), now - mRunStartTime);
- mRunStartTime = now;
- }
- if (mStartedState != STATE_NOTHING) {
- addDuration(SERVICE_STARTED + (mStartedState*SERVICE_COUNT),
- now - mStartedStartTime);
- mStartedStartTime = now;
- }
- if (mBoundState != STATE_NOTHING) {
- addDuration(SERVICE_BOUND + (mBoundState*SERVICE_COUNT), now - mBoundStartTime);
- mBoundStartTime = now;
- }
- if (mExecState != STATE_NOTHING) {
- addDuration(SERVICE_EXEC + (mExecState*SERVICE_COUNT), now - mExecStartTime);
- mExecStartTime = now;
- }
- }
-
- private void updateRunning(int memFactor, long now) {
- final int state = (mStartedState != STATE_NOTHING || mBoundState != STATE_NOTHING
- || mExecState != STATE_NOTHING) ? memFactor : STATE_NOTHING;
- if (mRunState != state) {
- if (mRunState != STATE_NOTHING) {
- addDuration(SERVICE_RUN + (mRunState*SERVICE_COUNT),
- now - mRunStartTime);
- } else if (state != STATE_NOTHING) {
- mRunCount++;
- }
- mRunState = state;
- mRunStartTime = now;
- }
- }
-
- public void setStarted(boolean started, int memFactor, long now) {
- if (mOwner == null) {
- Slog.wtf(TAG, "Starting service " + this + " without owner");
- }
- mStarted = started;
- updateStartedState(memFactor, now);
- }
-
- public void setRestarting(boolean restarting, int memFactor, long now) {
- mRestarting = restarting;
- updateStartedState(memFactor, now);
- }
-
- void updateStartedState(int memFactor, long now) {
- final boolean wasStarted = mStartedState != STATE_NOTHING;
- final boolean started = mStarted || mRestarting;
- final int state = started ? memFactor : STATE_NOTHING;
- if (mStartedState != state) {
- if (mStartedState != STATE_NOTHING) {
- addDuration(SERVICE_STARTED + (mStartedState*SERVICE_COUNT),
- now - mStartedStartTime);
- } else if (started) {
- mStartedCount++;
- }
- mStartedState = state;
- mStartedStartTime = now;
- mProc = mProc.pullFixedProc(mPackage);
- if (wasStarted != started) {
- if (started) {
- mProc.incStartedServices(memFactor, now, mName);
- } else {
- mProc.decStartedServices(memFactor, now, mName);
- }
- }
- updateRunning(memFactor, now);
- }
- }
-
- public void setBound(boolean bound, int memFactor, long now) {
- if (mOwner == null) {
- Slog.wtf(TAG, "Binding service " + this + " without owner");
- }
- final int state = bound ? memFactor : STATE_NOTHING;
- if (mBoundState != state) {
- if (mBoundState != STATE_NOTHING) {
- addDuration(SERVICE_BOUND + (mBoundState*SERVICE_COUNT),
- now - mBoundStartTime);
- } else if (bound) {
- mBoundCount++;
- }
- mBoundState = state;
- mBoundStartTime = now;
- updateRunning(memFactor, now);
- }
- }
-
- public void setExecuting(boolean executing, int memFactor, long now) {
- if (mOwner == null) {
- Slog.wtf(TAG, "Executing service " + this + " without owner");
- }
- final int state = executing ? memFactor : STATE_NOTHING;
- if (mExecState != state) {
- if (mExecState != STATE_NOTHING) {
- addDuration(SERVICE_EXEC + (mExecState*SERVICE_COUNT), now - mExecStartTime);
- } else if (executing) {
- mExecCount++;
- }
- mExecState = state;
- mExecStartTime = now;
- updateRunning(memFactor, now);
- }
- }
-
- private long getDuration(int opType, int curState, long startTime, int memFactor,
- long now) {
- int state = opType + (memFactor*SERVICE_COUNT);
- long time = getDuration(state, now);
- if (curState == memFactor) {
- time += now - startTime;
- }
- return time;
- }
-
- public String toString() {
- return "ServiceState{" + Integer.toHexString(System.identityHashCode(this))
- + " " + mName + " pkg=" + mPackage + " proc="
- + Integer.toHexString(System.identityHashCode(this)) + "}";
- }
- }
-
- public static final class PackageState {
- public final ArrayMap<String, ProcessState> mProcesses
- = new ArrayMap<String, ProcessState>();
- public final ArrayMap<String, ServiceState> mServices
- = new ArrayMap<String, ServiceState>();
- public final String mPackageName;
- public final int mUid;
-
- public PackageState(String packageName, int uid) {
- mUid = uid;
- mPackageName = packageName;
- }
- }
-
- public static final class ProcessDataCollection {
- final int[] screenStates;
- final int[] memStates;
- final int[] procStates;
-
- public long totalTime;
- public long numPss;
- public long minPss;
- public long avgPss;
- public long maxPss;
- public long minUss;
- public long avgUss;
- public long maxUss;
-
- public ProcessDataCollection(int[] _screenStates, int[] _memStates, int[] _procStates) {
- screenStates = _screenStates;
- memStates = _memStates;
- procStates = _procStates;
- }
-
- void print(PrintWriter pw, long overallTime, boolean full) {
- if (totalTime > overallTime) {
- pw.print("*");
- }
- printPercent(pw, (double) totalTime / (double) overallTime);
- if (numPss > 0) {
- pw.print(" (");
- DebugUtils.printSizeValue(pw, minPss * 1024);
- pw.print("-");
- DebugUtils.printSizeValue(pw, avgPss * 1024);
- pw.print("-");
- DebugUtils.printSizeValue(pw, maxPss * 1024);
- pw.print("/");
- DebugUtils.printSizeValue(pw, minUss * 1024);
- pw.print("-");
- DebugUtils.printSizeValue(pw, avgUss * 1024);
- pw.print("-");
- DebugUtils.printSizeValue(pw, maxUss * 1024);
- if (full) {
- pw.print(" over ");
- pw.print(numPss);
- }
- pw.print(")");
- }
- }
- }
-
- public static class TotalMemoryUseCollection {
- final int[] screenStates;
- final int[] memStates;
-
- public TotalMemoryUseCollection(int[] _screenStates, int[] _memStates) {
- screenStates = _screenStates;
- memStates = _memStates;
- }
-
- public long totalTime;
- public long[] processStatePss = new long[STATE_COUNT];
- public double[] processStateWeight = new double[STATE_COUNT];
- public long[] processStateTime = new long[STATE_COUNT];
- public int[] processStateSamples = new int[STATE_COUNT];
- public long[] sysMemUsage = new long[SYS_MEM_USAGE_COUNT];
- public double sysMemCachedWeight;
- public double sysMemFreeWeight;
- public double sysMemZRamWeight;
- public double sysMemKernelWeight;
- public double sysMemNativeWeight;
- public int sysMemSamples;
- }
-}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 5726de1..ff680e2 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -243,10 +243,6 @@
return;
}
- // Prevent the Resolver window from becoming the top fullscreen window and thus from taking
- // control of the system bars.
- getWindow().clearFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR);
-
final ResolverDrawerLayout rdl = (ResolverDrawerLayout) findViewById(R.id.contentPanel);
if (rdl != null) {
rdl.setOnDismissedListener(new ResolverDrawerLayout.OnDismissedListener() {
@@ -393,12 +389,7 @@
final DisplayResolveInfo dri = mAdapter.getOtherProfile();
if (dri != null) {
mProfileView.setVisibility(View.VISIBLE);
- final ImageView icon = (ImageView) mProfileView.findViewById(R.id.icon);
- final TextView text = (TextView) mProfileView.findViewById(R.id.text1);
- if (!dri.hasDisplayIcon()) {
- new LoadIconIntoViewTask(dri, icon).execute();
- }
- icon.setImageDrawable(dri.getDisplayIcon());
+ final TextView text = (TextView) mProfileView.findViewById(R.id.profile_button);
text.setText(dri.getDisplayLabel());
} else {
mProfileView.setVisibility(View.GONE);
diff --git a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
index 98102ea..e2d29e3 100644
--- a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
+++ b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
@@ -49,6 +49,7 @@
private static final int TYPE_HEADER_SUGGESTED = 0;
private static final int TYPE_HEADER_ALL_OTHERS = 1;
private static final int TYPE_LOCALE = 2;
+ private static final int MIN_REGIONS_FOR_SUGGESTIONS = 6;
private ArrayList<LocaleStore.LocaleInfo> mLocaleOptions;
private ArrayList<LocaleStore.LocaleInfo> mOriginalLocaleOptions;
@@ -171,7 +172,15 @@
}
private boolean showHeaders() {
- if (mCountryMode) { // never show suggestions in country mode
+ // We don't want to show suggestions for locales with very few regions
+ // (e.g. Romanian, with 2 regions)
+ // So we put a (somewhat) arbitrary limit.
+ //
+ // The initial idea was to make that limit dependent on the screen height.
+ // But that would mean rotating the screen could make the suggestions disappear,
+ // as the number of countries that fits on the screen would be different in portrait
+ // and landscape mode.
+ if (mCountryMode && mLocaleOptions.size() < MIN_REGIONS_FOR_SUGGESTIONS) {
return false;
}
return mSuggestionCount != 0 && mSuggestionCount != mLocaleOptions.size();
diff --git a/core/java/com/android/internal/app/procstats/DumpUtils.java b/core/java/com/android/internal/app/procstats/DumpUtils.java
new file mode 100644
index 0000000..ebedc89
--- /dev/null
+++ b/core/java/com/android/internal/app/procstats/DumpUtils.java
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.app.procstats;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.text.format.DateFormat;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.DebugUtils;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TimeUtils;
+
+import static com.android.internal.app.procstats.ProcessStats.*;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Objects;
+
+/**
+ * Utilities for dumping.
+ */
+public final class DumpUtils {
+ public static final String[] STATE_NAMES = new String[] {
+ "Persist", "Top ", "ImpFg ", "ImpBg ",
+ "Backup ", "HeavyWt", "Service", "ServRst",
+ "Receivr", "Home ",
+ "LastAct", "CchAct ", "CchCAct", "CchEmty"
+ };
+
+ public static final String[] ADJ_SCREEN_NAMES_CSV = new String[] {
+ "off", "on"
+ };
+
+ public static final String[] ADJ_MEM_NAMES_CSV = new String[] {
+ "norm", "mod", "low", "crit"
+ };
+
+ public static final String[] STATE_NAMES_CSV = new String[] {
+ "pers", "top", "impfg", "impbg", "backup", "heavy",
+ "service", "service-rs", "receiver", "home", "lastact",
+ "cch-activity", "cch-aclient", "cch-empty"
+ };
+
+ static final String[] ADJ_SCREEN_TAGS = new String[] {
+ "0", "1"
+ };
+
+ static final String[] ADJ_MEM_TAGS = new String[] {
+ "n", "m", "l", "c"
+ };
+
+ static final String[] STATE_TAGS = new String[] {
+ "p", "t", "f", "b", "u", "w",
+ "s", "x", "r", "h", "l", "a", "c", "e"
+ };
+
+ static final String CSV_SEP = "\t";
+
+ /**
+ * No instantiate
+ */
+ private DumpUtils() {
+ }
+
+ public static void printScreenLabel(PrintWriter pw, int offset) {
+ switch (offset) {
+ case ADJ_NOTHING:
+ pw.print(" ");
+ break;
+ case ADJ_SCREEN_OFF:
+ pw.print("SOff/");
+ break;
+ case ADJ_SCREEN_ON:
+ pw.print("SOn /");
+ break;
+ default:
+ pw.print("????/");
+ break;
+ }
+ }
+
+ public static void printScreenLabelCsv(PrintWriter pw, int offset) {
+ switch (offset) {
+ case ADJ_NOTHING:
+ break;
+ case ADJ_SCREEN_OFF:
+ pw.print(ADJ_SCREEN_NAMES_CSV[0]);
+ break;
+ case ADJ_SCREEN_ON:
+ pw.print(ADJ_SCREEN_NAMES_CSV[1]);
+ break;
+ default:
+ pw.print("???");
+ break;
+ }
+ }
+
+ public static void printMemLabel(PrintWriter pw, int offset, char sep) {
+ switch (offset) {
+ case ADJ_NOTHING:
+ pw.print(" ");
+ if (sep != 0) pw.print(' ');
+ break;
+ case ADJ_MEM_FACTOR_NORMAL:
+ pw.print("Norm");
+ if (sep != 0) pw.print(sep);
+ break;
+ case ADJ_MEM_FACTOR_MODERATE:
+ pw.print("Mod ");
+ if (sep != 0) pw.print(sep);
+ break;
+ case ADJ_MEM_FACTOR_LOW:
+ pw.print("Low ");
+ if (sep != 0) pw.print(sep);
+ break;
+ case ADJ_MEM_FACTOR_CRITICAL:
+ pw.print("Crit");
+ if (sep != 0) pw.print(sep);
+ break;
+ default:
+ pw.print("????");
+ if (sep != 0) pw.print(sep);
+ break;
+ }
+ }
+
+ public static void printMemLabelCsv(PrintWriter pw, int offset) {
+ if (offset >= ADJ_MEM_FACTOR_NORMAL) {
+ if (offset <= ADJ_MEM_FACTOR_CRITICAL) {
+ pw.print(ADJ_MEM_NAMES_CSV[offset]);
+ } else {
+ pw.print("???");
+ }
+ }
+ }
+
+ public static void printPercent(PrintWriter pw, double fraction) {
+ fraction *= 100;
+ if (fraction < 1) {
+ pw.print(String.format("%.2f", fraction));
+ } else if (fraction < 10) {
+ pw.print(String.format("%.1f", fraction));
+ } else {
+ pw.print(String.format("%.0f", fraction));
+ }
+ pw.print("%");
+ }
+
+ public static void printProcStateTag(PrintWriter pw, int state) {
+ state = printArrayEntry(pw, ADJ_SCREEN_TAGS, state, ADJ_SCREEN_MOD*STATE_COUNT);
+ state = printArrayEntry(pw, ADJ_MEM_TAGS, state, STATE_COUNT);
+ printArrayEntry(pw, STATE_TAGS, state, 1);
+ }
+
+ public static void printAdjTag(PrintWriter pw, int state) {
+ state = printArrayEntry(pw, ADJ_SCREEN_TAGS, state, ADJ_SCREEN_MOD);
+ printArrayEntry(pw, ADJ_MEM_TAGS, state, 1);
+ }
+
+ public static void printProcStateTagAndValue(PrintWriter pw, int state, long value) {
+ pw.print(',');
+ printProcStateTag(pw, state);
+ pw.print(':');
+ pw.print(value);
+ }
+
+ public static void printAdjTagAndValue(PrintWriter pw, int state, long value) {
+ pw.print(',');
+ printAdjTag(pw, state);
+ pw.print(':');
+ pw.print(value);
+ }
+
+ public static long dumpSingleTime(PrintWriter pw, String prefix, long[] durations,
+ int curState, long curStartTime, long now) {
+ long totalTime = 0;
+ int printedScreen = -1;
+ for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) {
+ int printedMem = -1;
+ for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) {
+ int state = imem+iscreen;
+ long time = durations[state];
+ String running = "";
+ if (curState == state) {
+ time += now - curStartTime;
+ if (pw != null) {
+ running = " (running)";
+ }
+ }
+ if (time != 0) {
+ if (pw != null) {
+ pw.print(prefix);
+ printScreenLabel(pw, printedScreen != iscreen
+ ? iscreen : STATE_NOTHING);
+ printedScreen = iscreen;
+ printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING, (char)0);
+ printedMem = imem;
+ pw.print(": ");
+ TimeUtils.formatDuration(time, pw); pw.println(running);
+ }
+ totalTime += time;
+ }
+ }
+ }
+ if (totalTime != 0 && pw != null) {
+ pw.print(prefix);
+ pw.print(" TOTAL: ");
+ TimeUtils.formatDuration(totalTime, pw);
+ pw.println();
+ }
+ return totalTime;
+ }
+
+ public static void dumpAdjTimesCheckin(PrintWriter pw, String sep, long[] durations,
+ int curState, long curStartTime, long now) {
+ for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) {
+ for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) {
+ int state = imem+iscreen;
+ long time = durations[state];
+ if (curState == state) {
+ time += now - curStartTime;
+ }
+ if (time != 0) {
+ printAdjTagAndValue(pw, state, time);
+ }
+ }
+ }
+ }
+
+ private static void dumpStateHeadersCsv(PrintWriter pw, String sep, int[] screenStates,
+ int[] memStates, int[] procStates) {
+ final int NS = screenStates != null ? screenStates.length : 1;
+ final int NM = memStates != null ? memStates.length : 1;
+ final int NP = procStates != null ? procStates.length : 1;
+ for (int is=0; is<NS; is++) {
+ for (int im=0; im<NM; im++) {
+ for (int ip=0; ip<NP; ip++) {
+ pw.print(sep);
+ boolean printed = false;
+ if (screenStates != null && screenStates.length > 1) {
+ printScreenLabelCsv(pw, screenStates[is]);
+ printed = true;
+ }
+ if (memStates != null && memStates.length > 1) {
+ if (printed) {
+ pw.print("-");
+ }
+ printMemLabelCsv(pw, memStates[im]);
+ printed = true;
+ }
+ if (procStates != null && procStates.length > 1) {
+ if (printed) {
+ pw.print("-");
+ }
+ pw.print(STATE_NAMES_CSV[procStates[ip]]);
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * Doesn't seem to be used.
+ *
+ public static void dumpProcessList(PrintWriter pw, String prefix, ArrayList<ProcessState> procs,
+ int[] screenStates, int[] memStates, int[] procStates, long now) {
+ String innerPrefix = prefix + " ";
+ for (int i=procs.size()-1; i>=0; i--) {
+ ProcessState proc = procs.get(i);
+ pw.print(prefix);
+ pw.print(proc.mName);
+ pw.print(" / ");
+ UserHandle.formatUid(pw, proc.mUid);
+ pw.print(" (");
+ pw.print(proc.durations.getKeyCount());
+ pw.print(" entries)");
+ pw.println(":");
+ proc.dumpProcessState(pw, innerPrefix, screenStates, memStates, procStates, now);
+ if (proc.pssTable.getKeyCount() > 0) {
+ proc.dumpPss(pw, innerPrefix, screenStates, memStates, procStates);
+ }
+ }
+ }
+ */
+
+ public static void dumpProcessSummaryLocked(PrintWriter pw, String prefix,
+ ArrayList<ProcessState> procs, int[] screenStates, int[] memStates, int[] procStates,
+ long now, long totalTime) {
+ for (int i=procs.size()-1; i>=0; i--) {
+ final ProcessState proc = procs.get(i);
+ proc.dumpSummary(pw, prefix, screenStates, memStates, procStates, now, totalTime);
+ }
+ }
+
+ public static void dumpProcessListCsv(PrintWriter pw, ArrayList<ProcessState> procs,
+ boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates,
+ boolean sepProcStates, int[] procStates, long now) {
+ pw.print("process");
+ pw.print(CSV_SEP);
+ pw.print("uid");
+ pw.print(CSV_SEP);
+ pw.print("vers");
+ dumpStateHeadersCsv(pw, CSV_SEP, sepScreenStates ? screenStates : null,
+ sepMemStates ? memStates : null,
+ sepProcStates ? procStates : null);
+ pw.println();
+ for (int i=procs.size()-1; i>=0; i--) {
+ ProcessState proc = procs.get(i);
+ pw.print(proc.getName());
+ pw.print(CSV_SEP);
+ UserHandle.formatUid(pw, proc.getUid());
+ pw.print(CSV_SEP);
+ pw.print(proc.getVersion());
+ proc.dumpCsv(pw, sepScreenStates, screenStates, sepMemStates,
+ memStates, sepProcStates, procStates, now);
+ pw.println();
+ }
+ }
+
+ public static int printArrayEntry(PrintWriter pw, String[] array, int value, int mod) {
+ int index = value/mod;
+ if (index >= 0 && index < array.length) {
+ pw.print(array[index]);
+ } else {
+ pw.print('?');
+ }
+ return value - index*mod;
+ }
+
+ public static String collapseString(String pkgName, String itemName) {
+ if (itemName.startsWith(pkgName)) {
+ final int ITEMLEN = itemName.length();
+ final int PKGLEN = pkgName.length();
+ if (ITEMLEN == PKGLEN) {
+ return "";
+ } else if (ITEMLEN >= PKGLEN) {
+ if (itemName.charAt(PKGLEN) == '.') {
+ return itemName.substring(PKGLEN);
+ }
+ }
+ }
+ return itemName;
+ }
+}
diff --git a/core/java/com/android/internal/app/procstats/DurationsTable.java b/core/java/com/android/internal/app/procstats/DurationsTable.java
new file mode 100644
index 0000000..b711ca1
--- /dev/null
+++ b/core/java/com/android/internal/app/procstats/DurationsTable.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.app.procstats;
+
+/**
+ * Sparse mapping table to store durations of processes, etc running in different
+ * states.
+ */
+public class DurationsTable extends SparseMappingTable.Table {
+ public DurationsTable(SparseMappingTable tableData) {
+ super(tableData);
+ }
+
+ /**
+ * Add all of the durations from the other table into this one.
+ * Resultant durations will be the sum of what is currently in the table
+ * and the new value.
+ */
+ public void addDurations(DurationsTable from) {
+ final int N = from.getKeyCount();
+ for (int i=0; i<N; i++) {
+ final int key = from.getKeyAt(i);
+ this.addDuration(SparseMappingTable.getIdFromKey(key), from.getValue(key));
+ }
+ }
+
+ /**
+ * Add the value into the value stored for the state.
+ *
+ * Resultant duration will be the sum of what is currently in the table
+ * and the new value.
+ */
+ public void addDuration(int state, long value) {
+ final int key = getOrAddKey((byte)state, 1);
+ setValue(key, getValue(key) + value);
+ }
+
+ /*
+ public long getDuration(int state, long now) {
+ final int key = getKey((byte)state);
+ if (key != SparseMappingTable.INVALID_KEY) {
+ return getValue(key);
+ } else {
+ return 0;
+ }
+ }
+ */
+}
+
+
diff --git a/core/java/com/android/internal/app/IProcessStats.aidl b/core/java/com/android/internal/app/procstats/IProcessStats.aidl
similarity index 89%
rename from core/java/com/android/internal/app/IProcessStats.aidl
rename to core/java/com/android/internal/app/procstats/IProcessStats.aidl
index 6fadf2f..44867c7 100644
--- a/core/java/com/android/internal/app/IProcessStats.aidl
+++ b/core/java/com/android/internal/app/procstats/IProcessStats.aidl
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.android.internal.app;
+package com.android.internal.app.procstats;
import android.content.ComponentName;
import android.os.ParcelFileDescriptor;
-import com.android.internal.app.ProcessStats;
+import com.android.internal.app.procstats.ProcessStats;
interface IProcessStats {
byte[] getCurrentStats(out List<ParcelFileDescriptor> historic);
diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java
new file mode 100644
index 0000000..80d6070
--- /dev/null
+++ b/core/java/com/android/internal/app/procstats/ProcessState.java
@@ -0,0 +1,1188 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.app.procstats;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.text.format.DateFormat;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.DebugUtils;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TimeUtils;
+
+import com.android.internal.app.procstats.ProcessStats;
+import com.android.internal.app.procstats.ProcessStats.PackageState;
+import com.android.internal.app.procstats.ProcessStats.ProcessStateHolder;
+import com.android.internal.app.procstats.ProcessStats.TotalMemoryUseCollection;
+import static com.android.internal.app.procstats.ProcessStats.PSS_SAMPLE_COUNT;
+import static com.android.internal.app.procstats.ProcessStats.PSS_MINIMUM;
+import static com.android.internal.app.procstats.ProcessStats.PSS_AVERAGE;
+import static com.android.internal.app.procstats.ProcessStats.PSS_MAXIMUM;
+import static com.android.internal.app.procstats.ProcessStats.PSS_USS_MINIMUM;
+import static com.android.internal.app.procstats.ProcessStats.PSS_USS_AVERAGE;
+import static com.android.internal.app.procstats.ProcessStats.PSS_USS_MAXIMUM;
+import static com.android.internal.app.procstats.ProcessStats.PSS_COUNT;
+import static com.android.internal.app.procstats.ProcessStats.STATE_NOTHING;
+import static com.android.internal.app.procstats.ProcessStats.STATE_PERSISTENT;
+import static com.android.internal.app.procstats.ProcessStats.STATE_TOP;
+import static com.android.internal.app.procstats.ProcessStats.STATE_IMPORTANT_FOREGROUND;
+import static com.android.internal.app.procstats.ProcessStats.STATE_IMPORTANT_BACKGROUND;
+import static com.android.internal.app.procstats.ProcessStats.STATE_BACKUP;
+import static com.android.internal.app.procstats.ProcessStats.STATE_HEAVY_WEIGHT;
+import static com.android.internal.app.procstats.ProcessStats.STATE_SERVICE;
+import static com.android.internal.app.procstats.ProcessStats.STATE_SERVICE_RESTARTING;
+import static com.android.internal.app.procstats.ProcessStats.STATE_RECEIVER;
+import static com.android.internal.app.procstats.ProcessStats.STATE_HOME;
+import static com.android.internal.app.procstats.ProcessStats.STATE_LAST_ACTIVITY;
+import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_ACTIVITY;
+import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_ACTIVITY_CLIENT;
+import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_EMPTY;
+import static com.android.internal.app.procstats.ProcessStats.STATE_COUNT;
+
+import dalvik.system.VMRuntime;
+import libcore.util.EmptyArray;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Objects;
+
+public final class ProcessState {
+ private static final String TAG = "ProcessStats";
+ private static final boolean DEBUG = false;
+ private static final boolean DEBUG_PARCEL = false;
+
+ // Map from process states to the states we track.
+ private static final int[] PROCESS_STATE_TO_STATE = new int[] {
+ STATE_PERSISTENT, // ActivityManager.PROCESS_STATE_PERSISTENT
+ STATE_PERSISTENT, // ActivityManager.PROCESS_STATE_PERSISTENT_UI
+ STATE_TOP, // ActivityManager.PROCESS_STATE_TOP
+ STATE_IMPORTANT_FOREGROUND, // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
+ STATE_IMPORTANT_FOREGROUND, // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
+ STATE_TOP, // ActivityManager.PROCESS_STATE_TOP_SLEEPING
+ STATE_IMPORTANT_FOREGROUND, // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
+ STATE_IMPORTANT_BACKGROUND, // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
+ STATE_BACKUP, // ActivityManager.PROCESS_STATE_BACKUP
+ STATE_HEAVY_WEIGHT, // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
+ STATE_SERVICE, // ActivityManager.PROCESS_STATE_SERVICE
+ STATE_RECEIVER, // ActivityManager.PROCESS_STATE_RECEIVER
+ STATE_HOME, // ActivityManager.PROCESS_STATE_HOME
+ STATE_LAST_ACTIVITY, // ActivityManager.PROCESS_STATE_LAST_ACTIVITY
+ STATE_CACHED_ACTIVITY, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY
+ STATE_CACHED_ACTIVITY_CLIENT, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT
+ STATE_CACHED_EMPTY, // ActivityManager.PROCESS_STATE_CACHED_EMPTY
+ };
+
+ public static final Comparator<ProcessState> COMPARATOR = new Comparator<ProcessState>() {
+ @Override
+ public int compare(ProcessState lhs, ProcessState rhs) {
+ if (lhs.mTmpTotalTime < rhs.mTmpTotalTime) {
+ return -1;
+ } else if (lhs.mTmpTotalTime > rhs.mTmpTotalTime) {
+ return 1;
+ }
+ return 0;
+ }
+ };
+
+ static class PssAggr {
+ long pss = 0;
+ long samples = 0;
+
+ void add(long newPss, long newSamples) {
+ pss = (long)( (pss*(double)samples) + (newPss*(double)newSamples) )
+ / (samples+newSamples);
+ samples += newSamples;
+ }
+ }
+
+ // Used by reset to count rather than storing extra maps. Be careful.
+ public int tmpNumInUse;
+ public ProcessState tmpFoundSubProc;
+
+ private final ProcessStats mStats;
+ private final String mName;
+ private final String mPackage;
+ private final int mUid;
+ private final int mVersion;
+ private final DurationsTable mDurations;
+ private final PssTable mPssTable;
+
+ private ProcessState mCommonProcess;
+ private int mCurState = STATE_NOTHING;
+ private long mStartTime;
+
+ private int mLastPssState = STATE_NOTHING;
+ private long mLastPssTime;
+
+ private boolean mActive;
+ private int mNumActiveServices;
+ private int mNumStartedServices;
+
+ private int mNumExcessiveWake;
+ private int mNumExcessiveCpu;
+
+ private int mNumCachedKill;
+ private long mMinCachedKillPss;
+ private long mAvgCachedKillPss;
+ private long mMaxCachedKillPss;
+
+ private boolean mMultiPackage;
+ private boolean mDead;
+
+ // Set in computeProcessTimeLocked and used by COMPARATOR to sort. Be careful.
+ private long mTmpTotalTime;
+
+ /**
+ * Create a new top-level process state, for the initial case where there is only
+ * a single package running in a process. The initial state is not running.
+ */
+ public ProcessState(ProcessStats processStats, String pkg, int uid, int vers, String name) {
+ mStats = processStats;
+ mName = name;
+ mCommonProcess = this;
+ mPackage = pkg;
+ mUid = uid;
+ mVersion = vers;
+ mDurations = new DurationsTable(processStats.mTableData);
+ mPssTable = new PssTable(processStats.mTableData);
+ }
+
+ /**
+ * Create a new per-package process state for an existing top-level process
+ * state. The current running state of the top-level process is also copied,
+ * marked as started running at 'now'.
+ */
+ public ProcessState(ProcessState commonProcess, String pkg, int uid, int vers, String name,
+ long now) {
+ mStats = commonProcess.mStats;
+ mName = name;
+ mCommonProcess = commonProcess;
+ mPackage = pkg;
+ mUid = uid;
+ mVersion = vers;
+ mCurState = commonProcess.mCurState;
+ mStartTime = now;
+ mDurations = new DurationsTable(commonProcess.mStats.mTableData);
+ mPssTable = new PssTable(commonProcess.mStats.mTableData);
+ }
+
+ public ProcessState clone(long now) {
+ ProcessState pnew = new ProcessState(this, mPackage, mUid, mVersion, mName, now);
+ pnew.mDurations.addDurations(mDurations);
+ pnew.mPssTable.copyFrom(mPssTable, PSS_COUNT);
+ pnew.mNumExcessiveWake = mNumExcessiveWake;
+ pnew.mNumExcessiveCpu = mNumExcessiveCpu;
+ pnew.mNumCachedKill = mNumCachedKill;
+ pnew.mMinCachedKillPss = mMinCachedKillPss;
+ pnew.mAvgCachedKillPss = mAvgCachedKillPss;
+ pnew.mMaxCachedKillPss = mMaxCachedKillPss;
+ pnew.mActive = mActive;
+ pnew.mNumActiveServices = mNumActiveServices;
+ pnew.mNumStartedServices = mNumStartedServices;
+ return pnew;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public ProcessState getCommonProcess() {
+ return mCommonProcess;
+ }
+
+ /**
+ * Say that we are not part of a shared process, so mCommonProcess = this.
+ */
+ public void makeStandalone() {
+ mCommonProcess = this;
+ }
+
+ public String getPackage() {
+ return mPackage;
+ }
+
+ public int getUid() {
+ return mUid;
+ }
+
+ public int getVersion() {
+ return mVersion;
+ }
+
+ public boolean isMultiPackage() {
+ return mMultiPackage;
+ }
+
+ public void setMultiPackage(boolean val) {
+ mMultiPackage = val;
+ }
+
+ public int getDurationsBucketCount() {
+ return mDurations.getKeyCount();
+ }
+
+ public void add(ProcessState other) {
+ mDurations.addDurations(other.mDurations);
+ mPssTable.mergeStats(other.mPssTable);
+ mNumExcessiveWake += other.mNumExcessiveWake;
+ mNumExcessiveCpu += other.mNumExcessiveCpu;
+ if (other.mNumCachedKill > 0) {
+ addCachedKill(other.mNumCachedKill, other.mMinCachedKillPss,
+ other.mAvgCachedKillPss, other.mMaxCachedKillPss);
+ }
+ }
+
+ public void resetSafely(long now) {
+ mDurations.resetTable();
+ mPssTable.resetTable();
+ mStartTime = now;
+ mLastPssState = STATE_NOTHING;
+ mLastPssTime = 0;
+ mNumExcessiveWake = 0;
+ mNumExcessiveCpu = 0;
+ mNumCachedKill = 0;
+ mMinCachedKillPss = mAvgCachedKillPss = mMaxCachedKillPss = 0;
+ }
+
+ public void makeDead() {
+ mDead = true;
+ }
+
+ private void ensureNotDead() {
+ if (!mDead) {
+ return;
+ }
+ Slog.wtfStack(TAG, "ProcessState dead: name=" + mName
+ + " pkg=" + mPackage + " uid=" + mUid + " common.name=" + mCommonProcess.mName);
+ }
+
+ public void writeToParcel(Parcel out, long now) {
+ out.writeInt(mMultiPackage ? 1 : 0);
+ mDurations.writeToParcel(out);
+ mPssTable.writeToParcel(out);
+ out.writeInt(mNumExcessiveWake);
+ out.writeInt(mNumExcessiveCpu);
+ out.writeInt(mNumCachedKill);
+ if (mNumCachedKill > 0) {
+ out.writeLong(mMinCachedKillPss);
+ out.writeLong(mAvgCachedKillPss);
+ out.writeLong(mMaxCachedKillPss);
+ }
+ }
+
+ public boolean readFromParcel(Parcel in, boolean fully) {
+ boolean multiPackage = in.readInt() != 0;
+ if (fully) {
+ mMultiPackage = multiPackage;
+ }
+ if (DEBUG_PARCEL) Slog.d(TAG, "Reading durations table...");
+ if (!mDurations.readFromParcel(in)) {
+ return false;
+ }
+ if (DEBUG_PARCEL) Slog.d(TAG, "Reading pss table...");
+ if (!mPssTable.readFromParcel(in)) {
+ return false;
+ }
+ mNumExcessiveWake = in.readInt();
+ mNumExcessiveCpu = in.readInt();
+ mNumCachedKill = in.readInt();
+ if (mNumCachedKill > 0) {
+ mMinCachedKillPss = in.readLong();
+ mAvgCachedKillPss = in.readLong();
+ mMaxCachedKillPss = in.readLong();
+ } else {
+ mMinCachedKillPss = mAvgCachedKillPss = mMaxCachedKillPss = 0;
+ }
+ return true;
+ }
+
+ public void makeActive() {
+ ensureNotDead();
+ mActive = true;
+ }
+
+ public void makeInactive() {
+ mActive = false;
+ }
+
+ public boolean isInUse() {
+ return mActive || mNumActiveServices > 0 || mNumStartedServices > 0
+ || mCurState != STATE_NOTHING;
+ }
+
+ public boolean isActive() {
+ return mActive;
+ }
+
+ public boolean hasAnyData() {
+ return !(mDurations.getKeyCount() == 0
+ && mCurState == STATE_NOTHING
+ && mPssTable.getKeyCount() == 0);
+ }
+
+ /**
+ * Update the current state of the given list of processes.
+ *
+ * @param state Current ActivityManager.PROCESS_STATE_*
+ * @param memFactor Current mem factor constant.
+ * @param now Current time.
+ * @param pkgList Processes to update.
+ */
+ public void setState(int state, int memFactor, long now,
+ ArrayMap<String, ProcessStateHolder> pkgList) {
+ if (state < 0) {
+ state = mNumStartedServices > 0
+ ? (STATE_SERVICE_RESTARTING+(memFactor*STATE_COUNT)) : STATE_NOTHING;
+ } else {
+ state = PROCESS_STATE_TO_STATE[state] + (memFactor*STATE_COUNT);
+ }
+
+ // First update the common process.
+ mCommonProcess.setState(state, now);
+
+ // If the common process is not multi-package, there is nothing else to do.
+ if (!mCommonProcess.mMultiPackage) {
+ return;
+ }
+
+ if (pkgList != null) {
+ for (int ip=pkgList.size()-1; ip>=0; ip--) {
+ pullFixedProc(pkgList, ip).setState(state, now);
+ }
+ }
+ }
+
+ public void setState(int state, long now) {
+ ensureNotDead();
+ if (mCurState != state) {
+ //Slog.i(TAG, "Setting state in " + mName + "/" + mPackage + ": " + state);
+ commitStateTime(now);
+ mCurState = state;
+ }
+ }
+
+ public void commitStateTime(long now) {
+ if (mCurState != STATE_NOTHING) {
+ long dur = now - mStartTime;
+ if (dur > 0) {
+ mDurations.addDuration(mCurState, dur);
+ }
+ }
+ mStartTime = now;
+ }
+
+ public void incActiveServices(String serviceName) {
+ if (DEBUG && "".equals(mName)) {
+ RuntimeException here = new RuntimeException("here");
+ here.fillInStackTrace();
+ Slog.d(TAG, "incActiveServices: " + this + " service=" + serviceName
+ + " to " + (mNumActiveServices+1), here);
+ }
+ if (mCommonProcess != this) {
+ mCommonProcess.incActiveServices(serviceName);
+ }
+ mNumActiveServices++;
+ }
+
+ public void decActiveServices(String serviceName) {
+ if (DEBUG && "".equals(mName)) {
+ RuntimeException here = new RuntimeException("here");
+ here.fillInStackTrace();
+ Slog.d(TAG, "decActiveServices: " + this + " service=" + serviceName
+ + " to " + (mNumActiveServices-1), here);
+ }
+ if (mCommonProcess != this) {
+ mCommonProcess.decActiveServices(serviceName);
+ }
+ mNumActiveServices--;
+ if (mNumActiveServices < 0) {
+ Slog.wtfStack(TAG, "Proc active services underrun: pkg=" + mPackage
+ + " uid=" + mUid + " proc=" + mName + " service=" + serviceName);
+ mNumActiveServices = 0;
+ }
+ }
+
+ public void incStartedServices(int memFactor, long now, String serviceName) {
+ if (false) {
+ RuntimeException here = new RuntimeException("here");
+ here.fillInStackTrace();
+ Slog.d(TAG, "incStartedServices: " + this + " service=" + serviceName
+ + " to " + (mNumStartedServices+1), here);
+ }
+ if (mCommonProcess != this) {
+ mCommonProcess.incStartedServices(memFactor, now, serviceName);
+ }
+ mNumStartedServices++;
+ if (mNumStartedServices == 1 && mCurState == STATE_NOTHING) {
+ setState(STATE_SERVICE_RESTARTING + (memFactor*STATE_COUNT), now);
+ }
+ }
+
+ public void decStartedServices(int memFactor, long now, String serviceName) {
+ if (false) {
+ RuntimeException here = new RuntimeException("here");
+ here.fillInStackTrace();
+ Slog.d(TAG, "decActiveServices: " + this + " service=" + serviceName
+ + " to " + (mNumStartedServices-1), here);
+ }
+ if (mCommonProcess != this) {
+ mCommonProcess.decStartedServices(memFactor, now, serviceName);
+ }
+ mNumStartedServices--;
+ if (mNumStartedServices == 0 && (mCurState%STATE_COUNT) == STATE_SERVICE_RESTARTING) {
+ setState(STATE_NOTHING, now);
+ } else if (mNumStartedServices < 0) {
+ Slog.wtfStack(TAG, "Proc started services underrun: pkg="
+ + mPackage + " uid=" + mUid + " name=" + mName);
+ mNumStartedServices = 0;
+ }
+ }
+
+ public void addPss(long pss, long uss, boolean always,
+ ArrayMap<String, ProcessStateHolder> pkgList) {
+ ensureNotDead();
+ if (!always) {
+ if (mLastPssState == mCurState && SystemClock.uptimeMillis()
+ < (mLastPssTime+(30*1000))) {
+ return;
+ }
+ }
+ mLastPssState = mCurState;
+ mLastPssTime = SystemClock.uptimeMillis();
+ if (mCurState != STATE_NOTHING) {
+ // First update the common process.
+ mCommonProcess.mPssTable.mergeStats(mCurState, 1, pss, pss, pss, uss, uss, uss);
+
+ // If the common process is not multi-package, there is nothing else to do.
+ if (!mCommonProcess.mMultiPackage) {
+ return;
+ }
+
+ if (pkgList != null) {
+ for (int ip=pkgList.size()-1; ip>=0; ip--) {
+ pullFixedProc(pkgList, ip).mPssTable.mergeStats(mCurState, 1,
+ pss, pss, pss, uss, uss, uss);
+ }
+ }
+ }
+ }
+
+ public void reportExcessiveWake(ArrayMap<String, ProcessStateHolder> pkgList) {
+ ensureNotDead();
+ mCommonProcess.mNumExcessiveWake++;
+ if (!mCommonProcess.mMultiPackage) {
+ return;
+ }
+
+ for (int ip=pkgList.size()-1; ip>=0; ip--) {
+ pullFixedProc(pkgList, ip).mNumExcessiveWake++;
+ }
+ }
+
+ public void reportExcessiveCpu(ArrayMap<String, ProcessStateHolder> pkgList) {
+ ensureNotDead();
+ mCommonProcess.mNumExcessiveCpu++;
+ if (!mCommonProcess.mMultiPackage) {
+ return;
+ }
+
+ for (int ip=pkgList.size()-1; ip>=0; ip--) {
+ pullFixedProc(pkgList, ip).mNumExcessiveCpu++;
+ }
+ }
+
+ private void addCachedKill(int num, long minPss, long avgPss, long maxPss) {
+ if (mNumCachedKill <= 0) {
+ mNumCachedKill = num;
+ mMinCachedKillPss = minPss;
+ mAvgCachedKillPss = avgPss;
+ mMaxCachedKillPss = maxPss;
+ } else {
+ if (minPss < mMinCachedKillPss) {
+ mMinCachedKillPss = minPss;
+ }
+ if (maxPss > mMaxCachedKillPss) {
+ mMaxCachedKillPss = maxPss;
+ }
+ mAvgCachedKillPss = (long)( ((mAvgCachedKillPss*(double)mNumCachedKill) + avgPss)
+ / (mNumCachedKill+num) );
+ mNumCachedKill += num;
+ }
+ }
+
+ public void reportCachedKill(ArrayMap<String, ProcessStateHolder> pkgList, long pss) {
+ ensureNotDead();
+ mCommonProcess.addCachedKill(1, pss, pss, pss);
+ if (!mCommonProcess.mMultiPackage) {
+ return;
+ }
+
+ for (int ip=pkgList.size()-1; ip>=0; ip--) {
+ pullFixedProc(pkgList, ip).addCachedKill(1, pss, pss, pss);
+ }
+ }
+
+ public ProcessState pullFixedProc(String pkgName) {
+ if (mMultiPackage) {
+ // The array map is still pointing to a common process state
+ // that is now shared across packages. Update it to point to
+ // the new per-package state.
+ SparseArray<PackageState> vpkg = mStats.mPackages.get(pkgName, mUid);
+ if (vpkg == null) {
+ throw new IllegalStateException("Didn't find package " + pkgName
+ + " / " + mUid);
+ }
+ PackageState pkg = vpkg.get(mVersion);
+ if (pkg == null) {
+ throw new IllegalStateException("Didn't find package " + pkgName
+ + " / " + mUid + " vers " + mVersion);
+ }
+ ProcessState proc = pkg.mProcesses.get(mName);
+ if (proc == null) {
+ throw new IllegalStateException("Didn't create per-package process "
+ + mName + " in pkg " + pkgName + " / " + mUid + " vers " + mVersion);
+ }
+ return proc;
+ }
+ return this;
+ }
+
+ private ProcessState pullFixedProc(ArrayMap<String, ProcessStateHolder> pkgList,
+ int index) {
+ ProcessStateHolder holder = pkgList.valueAt(index);
+ ProcessState proc = holder.state;
+ if (mDead && proc.mCommonProcess != proc) {
+ // Somehow we are contining to use a process state that is dead, because
+ // it was not being told it was active during the last commit. We can recover
+ // from this by generating a fresh new state, but this is bad because we
+ // are losing whatever data we had in the old process state.
+ Log.wtf(TAG, "Pulling dead proc: name=" + mName + " pkg=" + mPackage
+ + " uid=" + mUid + " common.name=" + mCommonProcess.mName);
+ proc = mStats.getProcessStateLocked(proc.mPackage, proc.mUid, proc.mVersion,
+ proc.mName);
+ }
+ if (proc.mMultiPackage) {
+ // The array map is still pointing to a common process state
+ // that is now shared across packages. Update it to point to
+ // the new per-package state.
+ SparseArray<PackageState> vpkg = mStats.mPackages.get(pkgList.keyAt(index),
+ proc.mUid);
+ if (vpkg == null) {
+ throw new IllegalStateException("No existing package "
+ + pkgList.keyAt(index) + "/" + proc.mUid
+ + " for multi-proc " + proc.mName);
+ }
+ PackageState pkg = vpkg.get(proc.mVersion);
+ if (pkg == null) {
+ throw new IllegalStateException("No existing package "
+ + pkgList.keyAt(index) + "/" + proc.mUid
+ + " for multi-proc " + proc.mName + " version " + proc.mVersion);
+ }
+ String savedName = proc.mName;
+ proc = pkg.mProcesses.get(proc.mName);
+ if (proc == null) {
+ throw new IllegalStateException("Didn't create per-package process "
+ + savedName + " in pkg " + pkg.mPackageName + "/" + pkg.mUid);
+ }
+ holder.state = proc;
+ }
+ return proc;
+ }
+
+ public long getDuration(int state, long now) {
+ long time = mDurations.getValueForId((byte)state);
+ if (mCurState == state) {
+ time += now - mStartTime;
+ }
+ return time;
+ }
+
+ public long getPssSampleCount(int state) {
+ return mPssTable.getValueForId((byte)state, PSS_SAMPLE_COUNT);
+ }
+
+ public long getPssMinimum(int state) {
+ return mPssTable.getValueForId((byte)state, PSS_MINIMUM);
+ }
+
+ public long getPssAverage(int state) {
+ return mPssTable.getValueForId((byte)state, PSS_AVERAGE);
+ }
+
+ public long getPssMaximum(int state) {
+ return mPssTable.getValueForId((byte)state, PSS_MAXIMUM);
+ }
+
+ public long getPssUssMinimum(int state) {
+ return mPssTable.getValueForId((byte)state, PSS_USS_MINIMUM);
+ }
+
+ public long getPssUssAverage(int state) {
+ return mPssTable.getValueForId((byte)state, PSS_USS_AVERAGE);
+ }
+
+ public long getPssUssMaximum(int state) {
+ return mPssTable.getValueForId((byte)state, PSS_USS_MAXIMUM);
+ }
+
+ /**
+ * Sums up the PSS data and adds it to 'data'.
+ *
+ * @param data The aggregate data is added here.
+ * @param now SystemClock.uptimeMillis()
+ */
+ public void aggregatePss(TotalMemoryUseCollection data, long now) {
+ final PssAggr fgPss = new PssAggr();
+ final PssAggr bgPss = new PssAggr();
+ final PssAggr cachedPss = new PssAggr();
+ boolean havePss = false;
+ for (int i=0; i<mDurations.getKeyCount(); i++) {
+ final int key = mDurations.getKeyAt(i);
+ int type = SparseMappingTable.getIdFromKey(key);
+ int procState = type % STATE_COUNT;
+ long samples = getPssSampleCount(type);
+ if (samples > 0) {
+ long avg = getPssAverage(type);
+ havePss = true;
+ if (procState <= STATE_IMPORTANT_FOREGROUND) {
+ fgPss.add(avg, samples);
+ } else if (procState <= STATE_RECEIVER) {
+ bgPss.add(avg, samples);
+ } else {
+ cachedPss.add(avg, samples);
+ }
+ }
+ }
+ if (!havePss) {
+ return;
+ }
+ boolean fgHasBg = false;
+ boolean fgHasCached = false;
+ boolean bgHasCached = false;
+ if (fgPss.samples < 3 && bgPss.samples > 0) {
+ fgHasBg = true;
+ fgPss.add(bgPss.pss, bgPss.samples);
+ }
+ if (fgPss.samples < 3 && cachedPss.samples > 0) {
+ fgHasCached = true;
+ fgPss.add(cachedPss.pss, cachedPss.samples);
+ }
+ if (bgPss.samples < 3 && cachedPss.samples > 0) {
+ bgHasCached = true;
+ bgPss.add(cachedPss.pss, cachedPss.samples);
+ }
+ if (bgPss.samples < 3 && !fgHasBg && fgPss.samples > 0) {
+ bgPss.add(fgPss.pss, fgPss.samples);
+ }
+ if (cachedPss.samples < 3 && !bgHasCached && bgPss.samples > 0) {
+ cachedPss.add(bgPss.pss, bgPss.samples);
+ }
+ if (cachedPss.samples < 3 && !fgHasCached && fgPss.samples > 0) {
+ cachedPss.add(fgPss.pss, fgPss.samples);
+ }
+ for (int i=0; i<mDurations.getKeyCount(); i++) {
+ final int key = mDurations.getKeyAt(i);
+ final int type = SparseMappingTable.getIdFromKey(key);
+ long time = mDurations.getValue(key);
+ if (mCurState == type) {
+ time += now - mStartTime;
+ }
+ final int procState = type % STATE_COUNT;
+ data.processStateTime[procState] += time;
+ long samples = getPssSampleCount(type);
+ long avg;
+ if (samples > 0) {
+ avg = getPssAverage(type);
+ } else if (procState <= STATE_IMPORTANT_FOREGROUND) {
+ samples = fgPss.samples;
+ avg = fgPss.pss;
+ } else if (procState <= STATE_RECEIVER) {
+ samples = bgPss.samples;
+ avg = bgPss.pss;
+ } else {
+ samples = cachedPss.samples;
+ avg = cachedPss.pss;
+ }
+ double newAvg = ( (data.processStatePss[procState]
+ * (double)data.processStateSamples[procState])
+ + (avg*(double)samples)
+ ) / (data.processStateSamples[procState]+samples);
+ data.processStatePss[procState] = (long)newAvg;
+ data.processStateSamples[procState] += samples;
+ data.processStateWeight[procState] += avg * (double)time;
+ }
+ }
+
+ public long computeProcessTimeLocked(int[] screenStates, int[] memStates,
+ int[] procStates, long now) {
+ long totalTime = 0;
+ for (int is=0; is<screenStates.length; is++) {
+ for (int im=0; im<memStates.length; im++) {
+ for (int ip=0; ip<procStates.length; ip++) {
+ int bucket = ((screenStates[is] + memStates[im]) * STATE_COUNT)
+ + procStates[ip];
+ totalTime += getDuration(bucket, now);
+ }
+ }
+ }
+ mTmpTotalTime = totalTime;
+ return totalTime;
+ }
+
+ public void dumpSummary(PrintWriter pw, String prefix,
+ int[] screenStates, int[] memStates, int[] procStates,
+ long now, long totalTime) {
+ pw.print(prefix);
+ pw.print("* ");
+ pw.print(mName);
+ pw.print(" / ");
+ UserHandle.formatUid(pw, mUid);
+ pw.print(" / v");
+ pw.print(mVersion);
+ pw.println(":");
+ dumpProcessSummaryDetails(pw, prefix, " TOTAL: ", screenStates, memStates,
+ procStates, now, totalTime, true);
+ dumpProcessSummaryDetails(pw, prefix, " Persistent: ", screenStates, memStates,
+ new int[] { STATE_PERSISTENT }, now, totalTime, true);
+ dumpProcessSummaryDetails(pw, prefix, " Top: ", screenStates, memStates,
+ new int[] {STATE_TOP}, now, totalTime, true);
+ dumpProcessSummaryDetails(pw, prefix, " Imp Fg: ", screenStates, memStates,
+ new int[] { STATE_IMPORTANT_FOREGROUND }, now, totalTime, true);
+ dumpProcessSummaryDetails(pw, prefix, " Imp Bg: ", screenStates, memStates,
+ new int[] {STATE_IMPORTANT_BACKGROUND}, now, totalTime, true);
+ dumpProcessSummaryDetails(pw, prefix, " Backup: ", screenStates, memStates,
+ new int[] {STATE_BACKUP}, now, totalTime, true);
+ dumpProcessSummaryDetails(pw, prefix, " Heavy Wgt: ", screenStates, memStates,
+ new int[] {STATE_HEAVY_WEIGHT}, now, totalTime, true);
+ dumpProcessSummaryDetails(pw, prefix, " Service: ", screenStates, memStates,
+ new int[] {STATE_SERVICE}, now, totalTime, true);
+ dumpProcessSummaryDetails(pw, prefix, " Service Rs: ", screenStates, memStates,
+ new int[] {STATE_SERVICE_RESTARTING}, now, totalTime, true);
+ dumpProcessSummaryDetails(pw, prefix, " Receiver: ", screenStates, memStates,
+ new int[] {STATE_RECEIVER}, now, totalTime, true);
+ dumpProcessSummaryDetails(pw, prefix, " (Home): ", screenStates, memStates,
+ new int[] {STATE_HOME}, now, totalTime, true);
+ dumpProcessSummaryDetails(pw, prefix, " (Last Act): ", screenStates, memStates,
+ new int[] {STATE_LAST_ACTIVITY}, now, totalTime, true);
+ dumpProcessSummaryDetails(pw, prefix, " (Cached): ", screenStates, memStates,
+ new int[] {STATE_CACHED_ACTIVITY, STATE_CACHED_ACTIVITY_CLIENT,
+ STATE_CACHED_EMPTY}, now, totalTime, true);
+ }
+
+ public void dumpProcessState(PrintWriter pw, String prefix,
+ int[] screenStates, int[] memStates, int[] procStates, long now) {
+ long totalTime = 0;
+ int printedScreen = -1;
+ for (int is=0; is<screenStates.length; is++) {
+ int printedMem = -1;
+ for (int im=0; im<memStates.length; im++) {
+ for (int ip=0; ip<procStates.length; ip++) {
+ final int iscreen = screenStates[is];
+ final int imem = memStates[im];
+ final int bucket = ((iscreen + imem) * STATE_COUNT) + procStates[ip];
+ long time = mDurations.getValueForId((byte)bucket);
+ String running = "";
+ if (mCurState == bucket) {
+ running = " (running)";
+ }
+ if (time != 0) {
+ pw.print(prefix);
+ if (screenStates.length > 1) {
+ DumpUtils.printScreenLabel(pw, printedScreen != iscreen
+ ? iscreen : STATE_NOTHING);
+ printedScreen = iscreen;
+ }
+ if (memStates.length > 1) {
+ DumpUtils.printMemLabel(pw,
+ printedMem != imem ? imem : STATE_NOTHING, '/');
+ printedMem = imem;
+ }
+ pw.print(DumpUtils.STATE_NAMES[procStates[ip]]); pw.print(": ");
+ TimeUtils.formatDuration(time, pw); pw.println(running);
+ totalTime += time;
+ }
+ }
+ }
+ }
+ if (totalTime != 0) {
+ pw.print(prefix);
+ if (screenStates.length > 1) {
+ DumpUtils.printScreenLabel(pw, STATE_NOTHING);
+ }
+ if (memStates.length > 1) {
+ DumpUtils.printMemLabel(pw, STATE_NOTHING, '/');
+ }
+ pw.print("TOTAL : ");
+ TimeUtils.formatDuration(totalTime, pw);
+ pw.println();
+ }
+ }
+
+ public void dumpPss(PrintWriter pw, String prefix,
+ int[] screenStates, int[] memStates, int[] procStates) {
+ boolean printedHeader = false;
+ int printedScreen = -1;
+ for (int is=0; is<screenStates.length; is++) {
+ int printedMem = -1;
+ for (int im=0; im<memStates.length; im++) {
+ for (int ip=0; ip<procStates.length; ip++) {
+ final int iscreen = screenStates[is];
+ final int imem = memStates[im];
+ final int bucket = ((iscreen + imem) * STATE_COUNT) + procStates[ip];
+ long count = getPssSampleCount(bucket);
+ if (count > 0) {
+ if (!printedHeader) {
+ pw.print(prefix);
+ pw.print("PSS/USS (");
+ pw.print(mPssTable.getKeyCount());
+ pw.println(" entries):");
+ printedHeader = true;
+ }
+ pw.print(prefix);
+ pw.print(" ");
+ if (screenStates.length > 1) {
+ DumpUtils.printScreenLabel(pw,
+ printedScreen != iscreen ? iscreen : STATE_NOTHING);
+ printedScreen = iscreen;
+ }
+ if (memStates.length > 1) {
+ DumpUtils.printMemLabel(pw,
+ printedMem != imem ? imem : STATE_NOTHING, '/');
+ printedMem = imem;
+ }
+ pw.print(DumpUtils.STATE_NAMES[procStates[ip]]); pw.print(": ");
+ pw.print(count);
+ pw.print(" samples ");
+ DebugUtils.printSizeValue(pw, getPssMinimum(bucket) * 1024);
+ pw.print(" ");
+ DebugUtils.printSizeValue(pw, getPssAverage(bucket) * 1024);
+ pw.print(" ");
+ DebugUtils.printSizeValue(pw, getPssMaximum(bucket) * 1024);
+ pw.print(" / ");
+ DebugUtils.printSizeValue(pw, getPssUssMinimum(bucket) * 1024);
+ pw.print(" ");
+ DebugUtils.printSizeValue(pw, getPssUssAverage(bucket) * 1024);
+ pw.print(" ");
+ DebugUtils.printSizeValue(pw, getPssUssMaximum(bucket) * 1024);
+ pw.println();
+ }
+ }
+ }
+ }
+ if (mNumExcessiveWake != 0) {
+ pw.print(prefix); pw.print("Killed for excessive wake locks: ");
+ pw.print(mNumExcessiveWake); pw.println(" times");
+ }
+ if (mNumExcessiveCpu != 0) {
+ pw.print(prefix); pw.print("Killed for excessive CPU use: ");
+ pw.print(mNumExcessiveCpu); pw.println(" times");
+ }
+ if (mNumCachedKill != 0) {
+ pw.print(prefix); pw.print("Killed from cached state: ");
+ pw.print(mNumCachedKill); pw.print(" times from pss ");
+ DebugUtils.printSizeValue(pw, mMinCachedKillPss * 1024); pw.print("-");
+ DebugUtils.printSizeValue(pw, mAvgCachedKillPss * 1024); pw.print("-");
+ DebugUtils.printSizeValue(pw, mMaxCachedKillPss * 1024); pw.println();
+ }
+ }
+
+ private void dumpProcessSummaryDetails(PrintWriter pw, String prefix,
+ String label, int[] screenStates, int[] memStates, int[] procStates,
+ long now, long totalTime, boolean full) {
+ ProcessStats.ProcessDataCollection totals = new ProcessStats.ProcessDataCollection(
+ screenStates, memStates, procStates);
+ computeProcessData(totals, now);
+ final double percentage = (double) totals.totalTime / (double) totalTime * 100;
+ // We don't print percentages < .01, so just drop those.
+ if (percentage >= 0.005 || totals.numPss != 0) {
+ if (prefix != null) {
+ pw.print(prefix);
+ }
+ if (label != null) {
+ pw.print(label);
+ }
+ totals.print(pw, totalTime, full);
+ if (prefix != null) {
+ pw.println();
+ }
+ }
+ }
+
+ public void dumpInternalLocked(PrintWriter pw, String prefix, boolean dumpAll) {
+ if (dumpAll) {
+ pw.print(prefix); pw.print("myID=");
+ pw.print(Integer.toHexString(System.identityHashCode(this)));
+ pw.print(" mCommonProcess=");
+ pw.print(Integer.toHexString(System.identityHashCode(mCommonProcess)));
+ pw.print(" mPackage="); pw.println(mPackage);
+ if (mMultiPackage) {
+ pw.print(prefix); pw.print("mMultiPackage="); pw.println(mMultiPackage);
+ }
+ if (this != mCommonProcess) {
+ pw.print(prefix); pw.print("Common Proc: "); pw.print(mCommonProcess.mName);
+ pw.print("/"); pw.print(mCommonProcess.mUid);
+ pw.print(" pkg="); pw.println(mCommonProcess.mPackage);
+ }
+ }
+ if (mActive) {
+ pw.print(prefix); pw.print("mActive="); pw.println(mActive);
+ }
+ if (mDead) {
+ pw.print(prefix); pw.print("mDead="); pw.println(mDead);
+ }
+ if (mNumActiveServices != 0 || mNumStartedServices != 0) {
+ pw.print(prefix); pw.print("mNumActiveServices="); pw.print(mNumActiveServices);
+ pw.print(" mNumStartedServices=");
+ pw.println(mNumStartedServices);
+ }
+ }
+
+ public void computeProcessData(ProcessStats.ProcessDataCollection data, long now) {
+ data.totalTime = 0;
+ data.numPss = data.minPss = data.avgPss = data.maxPss =
+ data.minUss = data.avgUss = data.maxUss = 0;
+ for (int is=0; is<data.screenStates.length; is++) {
+ for (int im=0; im<data.memStates.length; im++) {
+ for (int ip=0; ip<data.procStates.length; ip++) {
+ int bucket = ((data.screenStates[is] + data.memStates[im]) * STATE_COUNT)
+ + data.procStates[ip];
+ data.totalTime += getDuration(bucket, now);
+ long samples = getPssSampleCount(bucket);
+ if (samples > 0) {
+ long minPss = getPssMinimum(bucket);
+ long avgPss = getPssAverage(bucket);
+ long maxPss = getPssMaximum(bucket);
+ long minUss = getPssUssMinimum(bucket);
+ long avgUss = getPssUssAverage(bucket);
+ long maxUss = getPssUssMaximum(bucket);
+ if (data.numPss == 0) {
+ data.minPss = minPss;
+ data.avgPss = avgPss;
+ data.maxPss = maxPss;
+ data.minUss = minUss;
+ data.avgUss = avgUss;
+ data.maxUss = maxUss;
+ } else {
+ if (minPss < data.minPss) {
+ data.minPss = minPss;
+ }
+ data.avgPss = (long)( ((data.avgPss*(double)data.numPss)
+ + (avgPss*(double)samples)) / (data.numPss+samples) );
+ if (maxPss > data.maxPss) {
+ data.maxPss = maxPss;
+ }
+ if (minUss < data.minUss) {
+ data.minUss = minUss;
+ }
+ data.avgUss = (long)( ((data.avgUss*(double)data.numPss)
+ + (avgUss*(double)samples)) / (data.numPss+samples) );
+ if (maxUss > data.maxUss) {
+ data.maxUss = maxUss;
+ }
+ }
+ data.numPss += samples;
+ }
+ }
+ }
+ }
+ }
+
+ public void dumpCsv(PrintWriter pw,
+ boolean sepScreenStates, int[] screenStates, boolean sepMemStates,
+ int[] memStates, boolean sepProcStates, int[] procStates, long now) {
+ final int NSS = sepScreenStates ? screenStates.length : 1;
+ final int NMS = sepMemStates ? memStates.length : 1;
+ final int NPS = sepProcStates ? procStates.length : 1;
+ for (int iss=0; iss<NSS; iss++) {
+ for (int ims=0; ims<NMS; ims++) {
+ for (int ips=0; ips<NPS; ips++) {
+ final int vsscreen = sepScreenStates ? screenStates[iss] : 0;
+ final int vsmem = sepMemStates ? memStates[ims] : 0;
+ final int vsproc = sepProcStates ? procStates[ips] : 0;
+ final int NSA = sepScreenStates ? 1 : screenStates.length;
+ final int NMA = sepMemStates ? 1 : memStates.length;
+ final int NPA = sepProcStates ? 1 : procStates.length;
+ long totalTime = 0;
+ for (int isa=0; isa<NSA; isa++) {
+ for (int ima=0; ima<NMA; ima++) {
+ for (int ipa=0; ipa<NPA; ipa++) {
+ final int vascreen = sepScreenStates ? 0 : screenStates[isa];
+ final int vamem = sepMemStates ? 0 : memStates[ima];
+ final int vaproc = sepProcStates ? 0 : procStates[ipa];
+ final int bucket = ((vsscreen + vascreen + vsmem + vamem)
+ * STATE_COUNT) + vsproc + vaproc;
+ totalTime += getDuration(bucket, now);
+ }
+ }
+ }
+ pw.print(DumpUtils.CSV_SEP);
+ pw.print(totalTime);
+ }
+ }
+ }
+ }
+
+ public void dumpPackageProcCheckin(PrintWriter pw, String pkgName, int uid, int vers,
+ String itemName, long now) {
+ pw.print("pkgproc,");
+ pw.print(pkgName);
+ pw.print(",");
+ pw.print(uid);
+ pw.print(",");
+ pw.print(vers);
+ pw.print(",");
+ pw.print(DumpUtils.collapseString(pkgName, itemName));
+ dumpAllStateCheckin(pw, now);
+ pw.println();
+ if (mPssTable.getKeyCount() > 0) {
+ pw.print("pkgpss,");
+ pw.print(pkgName);
+ pw.print(",");
+ pw.print(uid);
+ pw.print(",");
+ pw.print(vers);
+ pw.print(",");
+ pw.print(DumpUtils.collapseString(pkgName, itemName));
+ dumpAllPssCheckin(pw);
+ pw.println();
+ }
+ if (mNumExcessiveWake > 0 || mNumExcessiveCpu > 0 || mNumCachedKill > 0) {
+ pw.print("pkgkills,");
+ pw.print(pkgName);
+ pw.print(",");
+ pw.print(uid);
+ pw.print(",");
+ pw.print(vers);
+ pw.print(",");
+ pw.print(DumpUtils.collapseString(pkgName, itemName));
+ pw.print(",");
+ pw.print(mNumExcessiveWake);
+ pw.print(",");
+ pw.print(mNumExcessiveCpu);
+ pw.print(",");
+ pw.print(mNumCachedKill);
+ pw.print(",");
+ pw.print(mMinCachedKillPss);
+ pw.print(":");
+ pw.print(mAvgCachedKillPss);
+ pw.print(":");
+ pw.print(mMaxCachedKillPss);
+ pw.println();
+ }
+ }
+
+ public void dumpProcCheckin(PrintWriter pw, String procName, int uid, long now) {
+ if (mDurations.getKeyCount() > 0) {
+ pw.print("proc,");
+ pw.print(procName);
+ pw.print(",");
+ pw.print(uid);
+ dumpAllStateCheckin(pw, now);
+ pw.println();
+ }
+ if (mPssTable.getKeyCount() > 0) {
+ pw.print("pss,");
+ pw.print(procName);
+ pw.print(",");
+ pw.print(uid);
+ dumpAllPssCheckin(pw);
+ pw.println();
+ }
+ if (mNumExcessiveWake > 0 || mNumExcessiveCpu > 0 || mNumCachedKill > 0) {
+ pw.print("kills,");
+ pw.print(procName);
+ pw.print(",");
+ pw.print(uid);
+ pw.print(",");
+ pw.print(mNumExcessiveWake);
+ pw.print(",");
+ pw.print(mNumExcessiveCpu);
+ pw.print(",");
+ pw.print(mNumCachedKill);
+ pw.print(",");
+ pw.print(mMinCachedKillPss);
+ pw.print(":");
+ pw.print(mAvgCachedKillPss);
+ pw.print(":");
+ pw.print(mMaxCachedKillPss);
+ pw.println();
+ }
+ }
+
+ public void dumpAllStateCheckin(PrintWriter pw, long now) {
+ boolean didCurState = false;
+ for (int i=0; i<mDurations.getKeyCount(); i++) {
+ final int key = mDurations.getKeyAt(i);
+ final int type = SparseMappingTable.getIdFromKey(key);
+ long time = mDurations.getValue(key);
+ if (mCurState == type) {
+ didCurState = true;
+ time += now - mStartTime;
+ }
+ DumpUtils.printProcStateTagAndValue(pw, type, time);
+ }
+ if (!didCurState && mCurState != STATE_NOTHING) {
+ DumpUtils.printProcStateTagAndValue(pw, mCurState, now - mStartTime);
+ }
+ }
+
+ public void dumpAllPssCheckin(PrintWriter pw) {
+ final int N = mPssTable.getKeyCount();
+ for (int i=0; i<N; i++) {
+ final int key = mPssTable.getKeyAt(i);
+ final int type = SparseMappingTable.getIdFromKey(key);
+ pw.print(',');
+ DumpUtils.printProcStateTag(pw, type);
+ pw.print(':');
+ pw.print(mPssTable.getValue(key, PSS_SAMPLE_COUNT));
+ pw.print(':');
+ pw.print(mPssTable.getValue(key, PSS_MINIMUM));
+ pw.print(':');
+ pw.print(mPssTable.getValue(key, PSS_AVERAGE));
+ pw.print(':');
+ pw.print(mPssTable.getValue(key, PSS_MAXIMUM));
+ pw.print(':');
+ pw.print(mPssTable.getValue(key, PSS_USS_MINIMUM));
+ pw.print(':');
+ pw.print(mPssTable.getValue(key, PSS_USS_AVERAGE));
+ pw.print(':');
+ pw.print(mPssTable.getValue(key, PSS_USS_MAXIMUM));
+ }
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("ProcessState{").append(Integer.toHexString(System.identityHashCode(this)))
+ .append(" ").append(mName).append("/").append(mUid)
+ .append(" pkg=").append(mPackage);
+ if (mMultiPackage) sb.append(" (multi)");
+ if (mCommonProcess != this) sb.append(" (sub)");
+ sb.append("}");
+ return sb.toString();
+ }
+}
diff --git a/core/java/com/android/internal/app/ProcessStats.aidl b/core/java/com/android/internal/app/procstats/ProcessStats.aidl
similarity index 93%
rename from core/java/com/android/internal/app/ProcessStats.aidl
rename to core/java/com/android/internal/app/procstats/ProcessStats.aidl
index 48b1f85..33639a0 100644
--- a/core/java/com/android/internal/app/ProcessStats.aidl
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.aidl
@@ -14,6 +14,6 @@
** limitations under the License.
*/
-package com.android.internal.app;
+package com.android.internal.app.procstats;
parcelable ProcessStats;
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
new file mode 100644
index 0000000..06542f7
--- /dev/null
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -0,0 +1,1621 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.app.procstats;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.text.format.DateFormat;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.DebugUtils;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TimeUtils;
+
+import com.android.internal.app.ProcessMap;
+import com.android.internal.app.procstats.DurationsTable;
+import com.android.internal.app.procstats.ProcessState;
+import com.android.internal.app.procstats.PssTable;
+import com.android.internal.app.procstats.ServiceState;
+import com.android.internal.app.procstats.SparseMappingTable;
+import com.android.internal.app.procstats.SysMemUsageTable;
+import com.android.internal.app.procstats.DumpUtils.*;
+
+import dalvik.system.VMRuntime;
+import libcore.util.EmptyArray;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Objects;
+
+public final class ProcessStats implements Parcelable {
+ public static final String TAG = "ProcessStats";
+ static final boolean DEBUG = false;
+ static final boolean DEBUG_PARCEL = false;
+
+ public static final String SERVICE_NAME = "procstats";
+
+ // How often the service commits its data, giving the minimum batching
+ // that is done.
+ public static long COMMIT_PERIOD = 3*60*60*1000; // Commit current stats every 3 hours
+
+ // Minimum uptime period before committing. If the COMMIT_PERIOD has elapsed but
+ // the total uptime has not exceeded this amount, then the commit will be held until
+ // it is reached.
+ public static long COMMIT_UPTIME_PERIOD = 60*60*1000; // Must have at least 1 hour elapsed
+
+ public static final int STATE_NOTHING = -1;
+ public static final int STATE_PERSISTENT = 0;
+ public static final int STATE_TOP = 1;
+ public static final int STATE_IMPORTANT_FOREGROUND = 2;
+ public static final int STATE_IMPORTANT_BACKGROUND = 3;
+ public static final int STATE_BACKUP = 4;
+ public static final int STATE_HEAVY_WEIGHT = 5;
+ public static final int STATE_SERVICE = 6;
+ public static final int STATE_SERVICE_RESTARTING = 7;
+ public static final int STATE_RECEIVER = 8;
+ public static final int STATE_HOME = 9;
+ public static final int STATE_LAST_ACTIVITY = 10;
+ public static final int STATE_CACHED_ACTIVITY = 11;
+ public static final int STATE_CACHED_ACTIVITY_CLIENT = 12;
+ public static final int STATE_CACHED_EMPTY = 13;
+ public static final int STATE_COUNT = STATE_CACHED_EMPTY+1;
+
+ public static final int PSS_SAMPLE_COUNT = 0;
+ public static final int PSS_MINIMUM = 1;
+ public static final int PSS_AVERAGE = 2;
+ public static final int PSS_MAXIMUM = 3;
+ public static final int PSS_USS_MINIMUM = 4;
+ public static final int PSS_USS_AVERAGE = 5;
+ public static final int PSS_USS_MAXIMUM = 6;
+ public static final int PSS_COUNT = PSS_USS_MAXIMUM+1;
+
+ public static final int SYS_MEM_USAGE_SAMPLE_COUNT = 0;
+ public static final int SYS_MEM_USAGE_CACHED_MINIMUM = 1;
+ public static final int SYS_MEM_USAGE_CACHED_AVERAGE = 2;
+ public static final int SYS_MEM_USAGE_CACHED_MAXIMUM = 3;
+ public static final int SYS_MEM_USAGE_FREE_MINIMUM = 4;
+ public static final int SYS_MEM_USAGE_FREE_AVERAGE = 5;
+ public static final int SYS_MEM_USAGE_FREE_MAXIMUM = 6;
+ public static final int SYS_MEM_USAGE_ZRAM_MINIMUM = 7;
+ public static final int SYS_MEM_USAGE_ZRAM_AVERAGE = 8;
+ public static final int SYS_MEM_USAGE_ZRAM_MAXIMUM = 9;
+ public static final int SYS_MEM_USAGE_KERNEL_MINIMUM = 10;
+ public static final int SYS_MEM_USAGE_KERNEL_AVERAGE = 11;
+ public static final int SYS_MEM_USAGE_KERNEL_MAXIMUM = 12;
+ public static final int SYS_MEM_USAGE_NATIVE_MINIMUM = 13;
+ public static final int SYS_MEM_USAGE_NATIVE_AVERAGE = 14;
+ public static final int SYS_MEM_USAGE_NATIVE_MAXIMUM = 15;
+ public static final int SYS_MEM_USAGE_COUNT = SYS_MEM_USAGE_NATIVE_MAXIMUM+1;
+
+ public static final int ADJ_NOTHING = -1;
+ public static final int ADJ_MEM_FACTOR_NORMAL = 0;
+ public static final int ADJ_MEM_FACTOR_MODERATE = 1;
+ public static final int ADJ_MEM_FACTOR_LOW = 2;
+ public static final int ADJ_MEM_FACTOR_CRITICAL = 3;
+ public static final int ADJ_MEM_FACTOR_COUNT = ADJ_MEM_FACTOR_CRITICAL+1;
+ public static final int ADJ_SCREEN_MOD = ADJ_MEM_FACTOR_COUNT;
+ public static final int ADJ_SCREEN_OFF = 0;
+ public static final int ADJ_SCREEN_ON = ADJ_SCREEN_MOD;
+ public static final int ADJ_COUNT = ADJ_SCREEN_ON*2;
+
+ public static final int FLAG_COMPLETE = 1<<0;
+ public static final int FLAG_SHUTDOWN = 1<<1;
+ public static final int FLAG_SYSPROPS = 1<<2;
+
+ public static final int[] ALL_MEM_ADJ = new int[] { ADJ_MEM_FACTOR_NORMAL,
+ ADJ_MEM_FACTOR_MODERATE, ADJ_MEM_FACTOR_LOW, ADJ_MEM_FACTOR_CRITICAL };
+
+ public static final int[] ALL_SCREEN_ADJ = new int[] { ADJ_SCREEN_OFF, ADJ_SCREEN_ON };
+
+ public static final int[] NON_CACHED_PROC_STATES = new int[] {
+ STATE_PERSISTENT, STATE_TOP, STATE_IMPORTANT_FOREGROUND,
+ STATE_IMPORTANT_BACKGROUND, STATE_BACKUP, STATE_HEAVY_WEIGHT,
+ STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER
+ };
+
+ public static final int[] BACKGROUND_PROC_STATES = new int[] {
+ STATE_IMPORTANT_FOREGROUND, STATE_IMPORTANT_BACKGROUND, STATE_BACKUP,
+ STATE_HEAVY_WEIGHT, STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER
+ };
+
+ public static final int[] ALL_PROC_STATES = new int[] { STATE_PERSISTENT,
+ STATE_TOP, STATE_IMPORTANT_FOREGROUND, STATE_IMPORTANT_BACKGROUND, STATE_BACKUP,
+ STATE_HEAVY_WEIGHT, STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER,
+ STATE_HOME, STATE_LAST_ACTIVITY, STATE_CACHED_ACTIVITY,
+ STATE_CACHED_ACTIVITY_CLIENT, STATE_CACHED_EMPTY
+ };
+
+ // Current version of the parcel format.
+ private static final int PARCEL_VERSION = 19;
+ // In-memory Parcel magic number, used to detect attempts to unmarshall bad data
+ private static final int MAGIC = 0x50535454;
+
+ public String mReadError;
+ public String mTimePeriodStartClockStr;
+ public int mFlags;
+
+ public final ProcessMap<SparseArray<PackageState>> mPackages
+ = new ProcessMap<SparseArray<PackageState>>();
+ public final ProcessMap<ProcessState> mProcesses = new ProcessMap<ProcessState>();
+
+ public final long[] mMemFactorDurations = new long[ADJ_COUNT];
+ public int mMemFactor = STATE_NOTHING;
+ public long mStartTime;
+
+ public long mTimePeriodStartClock;
+ public long mTimePeriodStartRealtime;
+ public long mTimePeriodEndRealtime;
+ public long mTimePeriodStartUptime;
+ public long mTimePeriodEndUptime;
+ String mRuntime;
+ boolean mRunning;
+
+ public final SparseMappingTable mTableData = new SparseMappingTable();
+
+ int[] mAddLongTable;
+ int mAddLongTableSize;
+
+ public final long[] mSysMemUsageArgs = new long[SYS_MEM_USAGE_COUNT];
+ public final SysMemUsageTable mSysMemUsage = new SysMemUsageTable(mTableData);
+
+ // For writing parcels.
+ ArrayMap<String, Integer> mCommonStringToIndex;
+
+ // For reading parcels.
+ ArrayList<String> mIndexToCommonString;
+
+ public ProcessStats(boolean running) {
+ mRunning = running;
+ reset();
+ }
+
+ public ProcessStats(Parcel in) {
+ reset();
+ readFromParcel(in);
+ }
+
+ public void add(ProcessStats other) {
+ ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = other.mPackages.getMap();
+ for (int ip=0; ip<pkgMap.size(); ip++) {
+ final String pkgName = pkgMap.keyAt(ip);
+ final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip);
+ for (int iu=0; iu<uids.size(); iu++) {
+ final int uid = uids.keyAt(iu);
+ final SparseArray<PackageState> versions = uids.valueAt(iu);
+ for (int iv=0; iv<versions.size(); iv++) {
+ final int vers = versions.keyAt(iv);
+ final PackageState otherState = versions.valueAt(iv);
+ final int NPROCS = otherState.mProcesses.size();
+ final int NSRVS = otherState.mServices.size();
+ for (int iproc=0; iproc<NPROCS; iproc++) {
+ ProcessState otherProc = otherState.mProcesses.valueAt(iproc);
+ if (otherProc.getCommonProcess() != otherProc) {
+ if (DEBUG) Slog.d(TAG, "Adding pkg " + pkgName + " uid " + uid
+ + " vers " + vers + " proc " + otherProc.getName());
+ ProcessState thisProc = getProcessStateLocked(pkgName, uid, vers,
+ otherProc.getName());
+ if (thisProc.getCommonProcess() == thisProc) {
+ if (DEBUG) Slog.d(TAG, "Existing process is single-package, splitting");
+ thisProc.setMultiPackage(true);
+ long now = SystemClock.uptimeMillis();
+ final PackageState pkgState = getPackageStateLocked(pkgName, uid,
+ vers);
+ thisProc = thisProc.clone(now);
+ pkgState.mProcesses.put(thisProc.getName(), thisProc);
+ }
+ thisProc.add(otherProc);
+ }
+ }
+ for (int isvc=0; isvc<NSRVS; isvc++) {
+ ServiceState otherSvc = otherState.mServices.valueAt(isvc);
+ if (DEBUG) Slog.d(TAG, "Adding pkg " + pkgName + " uid " + uid
+ + " service " + otherSvc.getName());
+ ServiceState thisSvc = getServiceStateLocked(pkgName, uid, vers,
+ otherSvc.getProcessName(), otherSvc.getName());
+ thisSvc.add(otherSvc);
+ }
+ }
+ }
+ }
+
+ ArrayMap<String, SparseArray<ProcessState>> procMap = other.mProcesses.getMap();
+ for (int ip=0; ip<procMap.size(); ip++) {
+ SparseArray<ProcessState> uids = procMap.valueAt(ip);
+ for (int iu=0; iu<uids.size(); iu++) {
+ int uid = uids.keyAt(iu);
+ ProcessState otherProc = uids.valueAt(iu);
+ final String name = otherProc.getName();
+ final String pkg = otherProc.getPackage();
+ final int vers = otherProc.getVersion();
+ ProcessState thisProc = mProcesses.get(name, uid);
+ if (DEBUG) Slog.d(TAG, "Adding uid " + uid + " proc " + name);
+ if (thisProc == null) {
+ if (DEBUG) Slog.d(TAG, "Creating new process!");
+ thisProc = new ProcessState(this, pkg, uid, vers, name);
+ mProcesses.put(name, uid, thisProc);
+ PackageState thisState = getPackageStateLocked(pkg, uid, vers);
+ if (!thisState.mProcesses.containsKey(name)) {
+ thisState.mProcesses.put(name, thisProc);
+ }
+ }
+ thisProc.add(otherProc);
+ }
+ }
+
+ for (int i=0; i<ADJ_COUNT; i++) {
+ if (DEBUG) Slog.d(TAG, "Total duration #" + i + " inc by "
+ + other.mMemFactorDurations[i] + " from "
+ + mMemFactorDurations[i]);
+ mMemFactorDurations[i] += other.mMemFactorDurations[i];
+ }
+
+ mSysMemUsage.mergeStats(other.mSysMemUsage);
+
+ if (other.mTimePeriodStartClock < mTimePeriodStartClock) {
+ mTimePeriodStartClock = other.mTimePeriodStartClock;
+ mTimePeriodStartClockStr = other.mTimePeriodStartClockStr;
+ }
+ mTimePeriodEndRealtime += other.mTimePeriodEndRealtime - other.mTimePeriodStartRealtime;
+ mTimePeriodEndUptime += other.mTimePeriodEndUptime - other.mTimePeriodStartUptime;
+ }
+
+ public void addSysMemUsage(long cachedMem, long freeMem, long zramMem, long kernelMem,
+ long nativeMem) {
+ if (mMemFactor != STATE_NOTHING) {
+ int state = mMemFactor * STATE_COUNT;
+ mSysMemUsageArgs[SYS_MEM_USAGE_SAMPLE_COUNT] = 1;
+ for (int i=0; i<3; i++) {
+ mSysMemUsageArgs[SYS_MEM_USAGE_CACHED_MINIMUM + i] = cachedMem;
+ mSysMemUsageArgs[SYS_MEM_USAGE_FREE_MINIMUM + i] = freeMem;
+ mSysMemUsageArgs[SYS_MEM_USAGE_ZRAM_MINIMUM + i] = zramMem;
+ mSysMemUsageArgs[SYS_MEM_USAGE_KERNEL_MINIMUM + i] = kernelMem;
+ mSysMemUsageArgs[SYS_MEM_USAGE_NATIVE_MINIMUM + i] = nativeMem;
+ }
+ mSysMemUsage.mergeStats(state, mSysMemUsageArgs, 0);
+ }
+ }
+
+ public static final Parcelable.Creator<ProcessStats> CREATOR
+ = new Parcelable.Creator<ProcessStats>() {
+ public ProcessStats createFromParcel(Parcel in) {
+ return new ProcessStats(in);
+ }
+
+ public ProcessStats[] newArray(int size) {
+ return new ProcessStats[size];
+ }
+ };
+
+ public void computeTotalMemoryUse(TotalMemoryUseCollection data, long now) {
+ data.totalTime = 0;
+ for (int i=0; i<STATE_COUNT; i++) {
+ data.processStateWeight[i] = 0;
+ data.processStatePss[i] = 0;
+ data.processStateTime[i] = 0;
+ data.processStateSamples[i] = 0;
+ }
+ for (int i=0; i<SYS_MEM_USAGE_COUNT; i++) {
+ data.sysMemUsage[i] = 0;
+ }
+ data.sysMemCachedWeight = 0;
+ data.sysMemFreeWeight = 0;
+ data.sysMemZRamWeight = 0;
+ data.sysMemKernelWeight = 0;
+ data.sysMemNativeWeight = 0;
+ data.sysMemSamples = 0;
+ final long[] totalMemUsage = mSysMemUsage.getTotalMemUsage();
+ for (int is=0; is<data.screenStates.length; is++) {
+ for (int im=0; im<data.memStates.length; im++) {
+ int memBucket = data.screenStates[is] + data.memStates[im];
+ int stateBucket = memBucket * STATE_COUNT;
+ long memTime = mMemFactorDurations[memBucket];
+ if (mMemFactor == memBucket) {
+ memTime += now - mStartTime;
+ }
+ data.totalTime += memTime;
+ final int sysKey = mSysMemUsage.getKey((byte)stateBucket);
+ long[] longs = totalMemUsage;
+ int idx = 0;
+ if (sysKey != SparseMappingTable.INVALID_KEY) {
+ final long[] tmpLongs = mSysMemUsage.getArrayForKey(sysKey);
+ final int tmpIndex = SparseMappingTable.getIndexFromKey(sysKey);
+ if (tmpLongs[tmpIndex+SYS_MEM_USAGE_SAMPLE_COUNT] >= 3) {
+ SysMemUsageTable.mergeSysMemUsage(data.sysMemUsage, 0, longs, idx);
+ longs = tmpLongs;
+ idx = tmpIndex;
+ }
+ }
+ data.sysMemCachedWeight += longs[idx+SYS_MEM_USAGE_CACHED_AVERAGE]
+ * (double)memTime;
+ data.sysMemFreeWeight += longs[idx+SYS_MEM_USAGE_FREE_AVERAGE]
+ * (double)memTime;
+ data.sysMemZRamWeight += longs[idx+SYS_MEM_USAGE_ZRAM_AVERAGE]
+ * (double)memTime;
+ data.sysMemKernelWeight += longs[idx+SYS_MEM_USAGE_KERNEL_AVERAGE]
+ * (double)memTime;
+ data.sysMemNativeWeight += longs[idx+SYS_MEM_USAGE_NATIVE_AVERAGE]
+ * (double)memTime;
+ data.sysMemSamples += longs[idx+SYS_MEM_USAGE_SAMPLE_COUNT];
+ }
+ }
+ ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+ for (int iproc=0; iproc<procMap.size(); iproc++) {
+ SparseArray<ProcessState> uids = procMap.valueAt(iproc);
+ for (int iu=0; iu<uids.size(); iu++) {
+ final ProcessState proc = uids.valueAt(iu);
+ proc.aggregatePss(data, now);
+ }
+ }
+ }
+
+ public void reset() {
+ if (DEBUG) Slog.d(TAG, "Resetting state of " + mTimePeriodStartClockStr);
+ resetCommon();
+ mPackages.getMap().clear();
+ mProcesses.getMap().clear();
+ mMemFactor = STATE_NOTHING;
+ mStartTime = 0;
+ if (DEBUG) Slog.d(TAG, "State reset; now " + mTimePeriodStartClockStr);
+ }
+
+ public void resetSafely() {
+ if (DEBUG) Slog.d(TAG, "Safely resetting state of " + mTimePeriodStartClockStr);
+ resetCommon();
+
+ // First initialize use count of all common processes.
+ final long now = SystemClock.uptimeMillis();
+ final ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+ for (int ip=procMap.size()-1; ip>=0; ip--) {
+ final SparseArray<ProcessState> uids = procMap.valueAt(ip);
+ for (int iu=uids.size()-1; iu>=0; iu--) {
+ uids.valueAt(iu).tmpNumInUse = 0;
+ }
+ }
+
+ // Next reset or prune all per-package processes, and for the ones that are reset
+ // track this back to the common processes.
+ final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
+ for (int ip=pkgMap.size()-1; ip>=0; ip--) {
+ final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip);
+ for (int iu=uids.size()-1; iu>=0; iu--) {
+ final SparseArray<PackageState> vpkgs = uids.valueAt(iu);
+ for (int iv=vpkgs.size()-1; iv>=0; iv--) {
+ final PackageState pkgState = vpkgs.valueAt(iv);
+ for (int iproc=pkgState.mProcesses.size()-1; iproc>=0; iproc--) {
+ final ProcessState ps = pkgState.mProcesses.valueAt(iproc);
+ if (ps.isInUse()) {
+ ps.resetSafely(now);
+ ps.getCommonProcess().tmpNumInUse++;
+ ps.getCommonProcess().tmpFoundSubProc = ps;
+ } else {
+ pkgState.mProcesses.valueAt(iproc).makeDead();
+ pkgState.mProcesses.removeAt(iproc);
+ }
+ }
+ for (int isvc=pkgState.mServices.size()-1; isvc>=0; isvc--) {
+ final ServiceState ss = pkgState.mServices.valueAt(isvc);
+ if (ss.isInUse()) {
+ ss.resetSafely(now);
+ } else {
+ pkgState.mServices.removeAt(isvc);
+ }
+ }
+ if (pkgState.mProcesses.size() <= 0 && pkgState.mServices.size() <= 0) {
+ vpkgs.removeAt(iv);
+ }
+ }
+ if (vpkgs.size() <= 0) {
+ uids.removeAt(iu);
+ }
+ }
+ if (uids.size() <= 0) {
+ pkgMap.removeAt(ip);
+ }
+ }
+
+ // Finally prune out any common processes that are no longer in use.
+ for (int ip=procMap.size()-1; ip>=0; ip--) {
+ final SparseArray<ProcessState> uids = procMap.valueAt(ip);
+ for (int iu=uids.size()-1; iu>=0; iu--) {
+ ProcessState ps = uids.valueAt(iu);
+ if (ps.isInUse() || ps.tmpNumInUse > 0) {
+ // If this is a process for multiple packages, we could at this point
+ // be back down to one package. In that case, we want to revert back
+ // to a single shared ProcessState. We can do this by converting the
+ // current package-specific ProcessState up to the shared ProcessState,
+ // throwing away the current one we have here (because nobody else is
+ // using it).
+ if (!ps.isActive() && ps.isMultiPackage() && ps.tmpNumInUse == 1) {
+ // Here we go...
+ ps = ps.tmpFoundSubProc;
+ ps.makeStandalone();
+ uids.setValueAt(iu, ps);
+ } else {
+ ps.resetSafely(now);
+ }
+ } else {
+ ps.makeDead();
+ uids.removeAt(iu);
+ }
+ }
+ if (uids.size() <= 0) {
+ procMap.removeAt(ip);
+ }
+ }
+
+ mStartTime = now;
+ if (DEBUG) Slog.d(TAG, "State reset; now " + mTimePeriodStartClockStr);
+ }
+
+ private void resetCommon() {
+ mTimePeriodStartClock = System.currentTimeMillis();
+ buildTimePeriodStartClockStr();
+ mTimePeriodStartRealtime = mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
+ mTimePeriodStartUptime = mTimePeriodEndUptime = SystemClock.uptimeMillis();
+ mTableData.reset();
+ Arrays.fill(mMemFactorDurations, 0);
+ mSysMemUsage.resetTable();
+ mStartTime = 0;
+ mReadError = null;
+ mFlags = 0;
+ evaluateSystemProperties(true);
+ }
+
+ public boolean evaluateSystemProperties(boolean update) {
+ boolean changed = false;
+ String runtime = SystemProperties.get("persist.sys.dalvik.vm.lib.2",
+ VMRuntime.getRuntime().vmLibrary());
+ if (!Objects.equals(runtime, mRuntime)) {
+ changed = true;
+ if (update) {
+ mRuntime = runtime;
+ }
+ }
+ return changed;
+ }
+
+ private void buildTimePeriodStartClockStr() {
+ mTimePeriodStartClockStr = DateFormat.format("yyyy-MM-dd-HH-mm-ss",
+ mTimePeriodStartClock).toString();
+ }
+
+ static final int[] BAD_TABLE = new int[0];
+
+ private void writeCompactedLongArray(Parcel out, long[] array, int num) {
+ for (int i=0; i<num; i++) {
+ long val = array[i];
+ if (val < 0) {
+ Slog.w(TAG, "Time val negative: " + val);
+ val = 0;
+ }
+ if (val <= Integer.MAX_VALUE) {
+ out.writeInt((int)val);
+ } else {
+ int top = ~((int)((val>>32)&0x7fffffff));
+ int bottom = (int)(val&0xfffffff);
+ out.writeInt(top);
+ out.writeInt(bottom);
+ }
+ }
+ }
+
+ private void readCompactedLongArray(Parcel in, int version, long[] array, int num) {
+ if (version <= 10) {
+ in.readLongArray(array);
+ return;
+ }
+ final int alen = array.length;
+ if (num > alen) {
+ throw new RuntimeException("bad array lengths: got " + num + " array is " + alen);
+ }
+ int i;
+ for (i=0; i<num; i++) {
+ int val = in.readInt();
+ if (val >= 0) {
+ array[i] = val;
+ } else {
+ int bottom = in.readInt();
+ array[i] = (((long)~val)<<32) | bottom;
+ }
+ }
+ while (i < alen) {
+ array[i] = 0;
+ i++;
+ }
+ }
+
+ private void writeCommonString(Parcel out, String name) {
+ Integer index = mCommonStringToIndex.get(name);
+ if (index != null) {
+ out.writeInt(index);
+ return;
+ }
+ index = mCommonStringToIndex.size();
+ mCommonStringToIndex.put(name, index);
+ out.writeInt(~index);
+ out.writeString(name);
+ }
+
+ private String readCommonString(Parcel in, int version) {
+ if (version <= 9) {
+ return in.readString();
+ }
+ int index = in.readInt();
+ if (index >= 0) {
+ return mIndexToCommonString.get(index);
+ }
+ index = ~index;
+ String name = in.readString();
+ while (mIndexToCommonString.size() <= index) {
+ mIndexToCommonString.add(null);
+ }
+ mIndexToCommonString.set(index, name);
+ return name;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ writeToParcel(out, SystemClock.uptimeMillis(), flags);
+ }
+
+ /** @hide */
+ public void writeToParcel(Parcel out, long now, int flags) {
+ out.writeInt(MAGIC);
+ out.writeInt(PARCEL_VERSION);
+ out.writeInt(STATE_COUNT);
+ out.writeInt(ADJ_COUNT);
+ out.writeInt(PSS_COUNT);
+ out.writeInt(SYS_MEM_USAGE_COUNT);
+ out.writeInt(SparseMappingTable.ARRAY_SIZE);
+
+ mCommonStringToIndex = new ArrayMap<String, Integer>(mProcesses.size());
+
+ // First commit all running times.
+ ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+ final int NPROC = procMap.size();
+ for (int ip=0; ip<NPROC; ip++) {
+ SparseArray<ProcessState> uids = procMap.valueAt(ip);
+ final int NUID = uids.size();
+ for (int iu=0; iu<NUID; iu++) {
+ uids.valueAt(iu).commitStateTime(now);
+ }
+ }
+ final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
+ final int NPKG = pkgMap.size();
+ for (int ip=0; ip<NPKG; ip++) {
+ final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip);
+ final int NUID = uids.size();
+ for (int iu=0; iu<NUID; iu++) {
+ final SparseArray<PackageState> vpkgs = uids.valueAt(iu);
+ final int NVERS = vpkgs.size();
+ for (int iv=0; iv<NVERS; iv++) {
+ PackageState pkgState = vpkgs.valueAt(iv);
+ final int NPROCS = pkgState.mProcesses.size();
+ for (int iproc=0; iproc<NPROCS; iproc++) {
+ ProcessState proc = pkgState.mProcesses.valueAt(iproc);
+ if (proc.getCommonProcess() != proc) {
+ proc.commitStateTime(now);
+ }
+ }
+ final int NSRVS = pkgState.mServices.size();
+ for (int isvc=0; isvc<NSRVS; isvc++) {
+ pkgState.mServices.valueAt(isvc).commitStateTime(now);
+ }
+ }
+ }
+ }
+
+ out.writeLong(mTimePeriodStartClock);
+ out.writeLong(mTimePeriodStartRealtime);
+ out.writeLong(mTimePeriodEndRealtime);
+ out.writeLong(mTimePeriodStartUptime);
+ out.writeLong(mTimePeriodEndUptime);
+ out.writeString(mRuntime);
+ out.writeInt(mFlags);
+
+ mTableData.writeToParcel(out);
+
+ if (mMemFactor != STATE_NOTHING) {
+ mMemFactorDurations[mMemFactor] += now - mStartTime;
+ mStartTime = now;
+ }
+ writeCompactedLongArray(out, mMemFactorDurations, mMemFactorDurations.length);
+
+ mSysMemUsage.writeToParcel(out);
+
+ out.writeInt(NPROC);
+ for (int ip=0; ip<NPROC; ip++) {
+ writeCommonString(out, procMap.keyAt(ip));
+ final SparseArray<ProcessState> uids = procMap.valueAt(ip);
+ final int NUID = uids.size();
+ out.writeInt(NUID);
+ for (int iu=0; iu<NUID; iu++) {
+ out.writeInt(uids.keyAt(iu));
+ final ProcessState proc = uids.valueAt(iu);
+ writeCommonString(out, proc.getPackage());
+ out.writeInt(proc.getVersion());
+ proc.writeToParcel(out, now);
+ }
+ }
+ out.writeInt(NPKG);
+ for (int ip=0; ip<NPKG; ip++) {
+ writeCommonString(out, pkgMap.keyAt(ip));
+ final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip);
+ final int NUID = uids.size();
+ out.writeInt(NUID);
+ for (int iu=0; iu<NUID; iu++) {
+ out.writeInt(uids.keyAt(iu));
+ final SparseArray<PackageState> vpkgs = uids.valueAt(iu);
+ final int NVERS = vpkgs.size();
+ out.writeInt(NVERS);
+ for (int iv=0; iv<NVERS; iv++) {
+ out.writeInt(vpkgs.keyAt(iv));
+ final PackageState pkgState = vpkgs.valueAt(iv);
+ final int NPROCS = pkgState.mProcesses.size();
+ out.writeInt(NPROCS);
+ for (int iproc=0; iproc<NPROCS; iproc++) {
+ writeCommonString(out, pkgState.mProcesses.keyAt(iproc));
+ final ProcessState proc = pkgState.mProcesses.valueAt(iproc);
+ if (proc.getCommonProcess() == proc) {
+ // This is the same as the common process we wrote above.
+ out.writeInt(0);
+ } else {
+ // There is separate data for this package's process.
+ out.writeInt(1);
+ proc.writeToParcel(out, now);
+ }
+ }
+ final int NSRVS = pkgState.mServices.size();
+ out.writeInt(NSRVS);
+ for (int isvc=0; isvc<NSRVS; isvc++) {
+ out.writeString(pkgState.mServices.keyAt(isvc));
+ final ServiceState svc = pkgState.mServices.valueAt(isvc);
+ writeCommonString(out, svc.getProcessName());
+ svc.writeToParcel(out, now);
+ }
+ }
+ }
+ }
+
+ mCommonStringToIndex = null;
+ }
+
+ private boolean readCheckedInt(Parcel in, int val, String what) {
+ int got;
+ if ((got=in.readInt()) != val) {
+ mReadError = "bad " + what + ": " + got;
+ return false;
+ }
+ return true;
+ }
+
+ static byte[] readFully(InputStream stream, int[] outLen) throws IOException {
+ int pos = 0;
+ final int initialAvail = stream.available();
+ byte[] data = new byte[initialAvail > 0 ? (initialAvail+1) : 16384];
+ while (true) {
+ int amt = stream.read(data, pos, data.length-pos);
+ if (DEBUG_PARCEL) Slog.i("foo", "Read " + amt + " bytes at " + pos
+ + " of avail " + data.length);
+ if (amt < 0) {
+ if (DEBUG_PARCEL) Slog.i("foo", "**** FINISHED READING: pos=" + pos
+ + " len=" + data.length);
+ outLen[0] = pos;
+ return data;
+ }
+ pos += amt;
+ if (pos >= data.length) {
+ byte[] newData = new byte[pos+16384];
+ if (DEBUG_PARCEL) Slog.i(TAG, "Copying " + pos + " bytes to new array len "
+ + newData.length);
+ System.arraycopy(data, 0, newData, 0, pos);
+ data = newData;
+ }
+ }
+ }
+
+ public void read(InputStream stream) {
+ try {
+ int[] len = new int[1];
+ byte[] raw = readFully(stream, len);
+ Parcel in = Parcel.obtain();
+ in.unmarshall(raw, 0, len[0]);
+ in.setDataPosition(0);
+ stream.close();
+
+ readFromParcel(in);
+ } catch (IOException e) {
+ mReadError = "caught exception: " + e;
+ }
+ }
+
+ public void readFromParcel(Parcel in) {
+ final boolean hadData = mPackages.getMap().size() > 0
+ || mProcesses.getMap().size() > 0;
+ if (hadData) {
+ resetSafely();
+ }
+
+ if (!readCheckedInt(in, MAGIC, "magic number")) {
+ return;
+ }
+ int version = in.readInt();
+ if (version != PARCEL_VERSION) {
+ mReadError = "bad version: " + version;
+ return;
+ }
+ if (!readCheckedInt(in, STATE_COUNT, "state count")) {
+ return;
+ }
+ if (!readCheckedInt(in, ADJ_COUNT, "adj count")) {
+ return;
+ }
+ if (!readCheckedInt(in, PSS_COUNT, "pss count")) {
+ return;
+ }
+ if (!readCheckedInt(in, SYS_MEM_USAGE_COUNT, "sys mem usage count")) {
+ return;
+ }
+ if (!readCheckedInt(in, SparseMappingTable.ARRAY_SIZE, "longs size")) {
+ return;
+ }
+
+ mIndexToCommonString = new ArrayList<String>();
+
+ mTimePeriodStartClock = in.readLong();
+ buildTimePeriodStartClockStr();
+ mTimePeriodStartRealtime = in.readLong();
+ mTimePeriodEndRealtime = in.readLong();
+ mTimePeriodStartUptime = in.readLong();
+ mTimePeriodEndUptime = in.readLong();
+ mRuntime = in.readString();
+ mFlags = in.readInt();
+ mTableData.readFromParcel(in);
+ readCompactedLongArray(in, version, mMemFactorDurations, mMemFactorDurations.length);
+ if (!mSysMemUsage.readFromParcel(in)) {
+ return;
+ }
+
+ int NPROC = in.readInt();
+ if (NPROC < 0) {
+ mReadError = "bad process count: " + NPROC;
+ return;
+ }
+ while (NPROC > 0) {
+ NPROC--;
+ final String procName = readCommonString(in, version);
+ if (procName == null) {
+ mReadError = "bad process name";
+ return;
+ }
+ int NUID = in.readInt();
+ if (NUID < 0) {
+ mReadError = "bad uid count: " + NUID;
+ return;
+ }
+ while (NUID > 0) {
+ NUID--;
+ final int uid = in.readInt();
+ if (uid < 0) {
+ mReadError = "bad uid: " + uid;
+ return;
+ }
+ final String pkgName = readCommonString(in, version);
+ if (pkgName == null) {
+ mReadError = "bad process package name";
+ return;
+ }
+ final int vers = in.readInt();
+ ProcessState proc = hadData ? mProcesses.get(procName, uid) : null;
+ if (proc != null) {
+ if (!proc.readFromParcel(in, false)) {
+ return;
+ }
+ } else {
+ proc = new ProcessState(this, pkgName, uid, vers, procName);
+ if (!proc.readFromParcel(in, true)) {
+ return;
+ }
+ }
+ if (DEBUG_PARCEL) Slog.d(TAG, "Adding process: " + procName + " " + uid
+ + " " + proc);
+ mProcesses.put(procName, uid, proc);
+ }
+ }
+
+ if (DEBUG_PARCEL) Slog.d(TAG, "Read " + mProcesses.getMap().size() + " processes");
+
+ int NPKG = in.readInt();
+ if (NPKG < 0) {
+ mReadError = "bad package count: " + NPKG;
+ return;
+ }
+ while (NPKG > 0) {
+ NPKG--;
+ final String pkgName = readCommonString(in, version);
+ if (pkgName == null) {
+ mReadError = "bad package name";
+ return;
+ }
+ int NUID = in.readInt();
+ if (NUID < 0) {
+ mReadError = "bad uid count: " + NUID;
+ return;
+ }
+ while (NUID > 0) {
+ NUID--;
+ final int uid = in.readInt();
+ if (uid < 0) {
+ mReadError = "bad uid: " + uid;
+ return;
+ }
+ int NVERS = in.readInt();
+ if (NVERS < 0) {
+ mReadError = "bad versions count: " + NVERS;
+ return;
+ }
+ while (NVERS > 0) {
+ NVERS--;
+ final int vers = in.readInt();
+ PackageState pkgState = new PackageState(pkgName, uid);
+ SparseArray<PackageState> vpkg = mPackages.get(pkgName, uid);
+ if (vpkg == null) {
+ vpkg = new SparseArray<PackageState>();
+ mPackages.put(pkgName, uid, vpkg);
+ }
+ vpkg.put(vers, pkgState);
+ int NPROCS = in.readInt();
+ if (NPROCS < 0) {
+ mReadError = "bad package process count: " + NPROCS;
+ return;
+ }
+ while (NPROCS > 0) {
+ NPROCS--;
+ String procName = readCommonString(in, version);
+ if (procName == null) {
+ mReadError = "bad package process name";
+ return;
+ }
+ int hasProc = in.readInt();
+ if (DEBUG_PARCEL) Slog.d(TAG, "Reading package " + pkgName + " " + uid
+ + " process " + procName + " hasProc=" + hasProc);
+ ProcessState commonProc = mProcesses.get(procName, uid);
+ if (DEBUG_PARCEL) Slog.d(TAG, "Got common proc " + procName + " " + uid
+ + ": " + commonProc);
+ if (commonProc == null) {
+ mReadError = "no common proc: " + procName;
+ return;
+ }
+ if (hasProc != 0) {
+ // The process for this package is unique to the package; we
+ // need to load it. We don't need to do anything about it if
+ // it is not unique because if someone later looks for it
+ // they will find and use it from the global procs.
+ ProcessState proc = hadData ? pkgState.mProcesses.get(procName) : null;
+ if (proc != null) {
+ if (!proc.readFromParcel(in, false)) {
+ return;
+ }
+ } else {
+ proc = new ProcessState(commonProc, pkgName, uid, vers, procName,
+ 0);
+ if (!proc.readFromParcel(in, true)) {
+ return;
+ }
+ }
+ if (DEBUG_PARCEL) Slog.d(TAG, "Adding package " + pkgName + " process: "
+ + procName + " " + uid + " " + proc);
+ pkgState.mProcesses.put(procName, proc);
+ } else {
+ if (DEBUG_PARCEL) Slog.d(TAG, "Adding package " + pkgName + " process: "
+ + procName + " " + uid + " " + commonProc);
+ pkgState.mProcesses.put(procName, commonProc);
+ }
+ }
+ int NSRVS = in.readInt();
+ if (NSRVS < 0) {
+ mReadError = "bad package service count: " + NSRVS;
+ return;
+ }
+ while (NSRVS > 0) {
+ NSRVS--;
+ String serviceName = in.readString();
+ if (serviceName == null) {
+ mReadError = "bad package service name";
+ return;
+ }
+ String processName = version > 9 ? readCommonString(in, version) : null;
+ ServiceState serv = hadData ? pkgState.mServices.get(serviceName) : null;
+ if (serv == null) {
+ serv = new ServiceState(this, pkgName, serviceName, processName, null);
+ }
+ if (!serv.readFromParcel(in)) {
+ return;
+ }
+ if (DEBUG_PARCEL) Slog.d(TAG, "Adding package " + pkgName + " service: "
+ + serviceName + " " + uid + " " + serv);
+ pkgState.mServices.put(serviceName, serv);
+ }
+ }
+ }
+ }
+
+ mIndexToCommonString = null;
+
+ if (DEBUG_PARCEL) Slog.d(TAG, "Successfully read procstats!");
+ }
+
+ public PackageState getPackageStateLocked(String packageName, int uid, int vers) {
+ SparseArray<PackageState> vpkg = mPackages.get(packageName, uid);
+ if (vpkg == null) {
+ vpkg = new SparseArray<PackageState>();
+ mPackages.put(packageName, uid, vpkg);
+ }
+ PackageState as = vpkg.get(vers);
+ if (as != null) {
+ return as;
+ }
+ as = new PackageState(packageName, uid);
+ vpkg.put(vers, as);
+ return as;
+ }
+
+ public ProcessState getProcessStateLocked(String packageName, int uid, int vers,
+ String processName) {
+ final PackageState pkgState = getPackageStateLocked(packageName, uid, vers);
+ ProcessState ps = pkgState.mProcesses.get(processName);
+ if (ps != null) {
+ return ps;
+ }
+ ProcessState commonProc = mProcesses.get(processName, uid);
+ if (commonProc == null) {
+ commonProc = new ProcessState(this, packageName, uid, vers, processName);
+ mProcesses.put(processName, uid, commonProc);
+ if (DEBUG) Slog.d(TAG, "GETPROC created new common " + commonProc);
+ }
+ if (!commonProc.isMultiPackage()) {
+ if (packageName.equals(commonProc.getPackage()) && vers == commonProc.getVersion()) {
+ // This common process is not in use by multiple packages, and
+ // is for the calling package, so we can just use it directly.
+ ps = commonProc;
+ if (DEBUG) Slog.d(TAG, "GETPROC also using for pkg " + commonProc);
+ } else {
+ if (DEBUG) Slog.d(TAG, "GETPROC need to split common proc!");
+ // This common process has not been in use by multiple packages,
+ // but it was created for a different package than the caller.
+ // We need to convert it to a multi-package process.
+ commonProc.setMultiPackage(true);
+ // To do this, we need to make two new process states, one a copy
+ // of the current state for the process under the original package
+ // name, and the second a free new process state for it as the
+ // new package name.
+ long now = SystemClock.uptimeMillis();
+ // First let's make a copy of the current process state and put
+ // that under the now unique state for its original package name.
+ final PackageState commonPkgState = getPackageStateLocked(commonProc.getPackage(),
+ uid, commonProc.getVersion());
+ if (commonPkgState != null) {
+ ProcessState cloned = commonProc.clone(now);
+ if (DEBUG) Slog.d(TAG, "GETPROC setting clone to pkg " + commonProc.getPackage()
+ + ": " + cloned);
+ commonPkgState.mProcesses.put(commonProc.getName(), cloned);
+ // If this has active services, we need to update their process pointer
+ // to point to the new package-specific process state.
+ for (int i=commonPkgState.mServices.size()-1; i>=0; i--) {
+ ServiceState ss = commonPkgState.mServices.valueAt(i);
+ if (ss.getProcess() == commonProc) {
+ if (DEBUG) Slog.d(TAG, "GETPROC switching service to cloned: " + ss);
+ ss.setProcess(cloned);
+ } else if (DEBUG) {
+ Slog.d(TAG, "GETPROC leaving proc of " + ss);
+ }
+ }
+ } else {
+ Slog.w(TAG, "Cloning proc state: no package state " + commonProc.getPackage()
+ + "/" + uid + " for proc " + commonProc.getName());
+ }
+ // And now make a fresh new process state for the new package name.
+ ps = new ProcessState(commonProc, packageName, uid, vers, processName, now);
+ if (DEBUG) Slog.d(TAG, "GETPROC created new pkg " + ps);
+ }
+ } else {
+ // The common process is for multiple packages, we need to create a
+ // separate object for the per-package data.
+ ps = new ProcessState(commonProc, packageName, uid, vers, processName,
+ SystemClock.uptimeMillis());
+ if (DEBUG) Slog.d(TAG, "GETPROC created new pkg " + ps);
+ }
+ pkgState.mProcesses.put(processName, ps);
+ if (DEBUG) Slog.d(TAG, "GETPROC adding new pkg " + ps);
+ return ps;
+ }
+
+ public ServiceState getServiceStateLocked(String packageName, int uid, int vers,
+ String processName, String className) {
+ final ProcessStats.PackageState as = getPackageStateLocked(packageName, uid, vers);
+ ServiceState ss = as.mServices.get(className);
+ if (ss != null) {
+ if (DEBUG) Slog.d(TAG, "GETSVC: returning existing " + ss);
+ return ss;
+ }
+ final ProcessState ps = processName != null
+ ? getProcessStateLocked(packageName, uid, vers, processName) : null;
+ ss = new ServiceState(this, packageName, className, processName, ps);
+ as.mServices.put(className, ss);
+ if (DEBUG) Slog.d(TAG, "GETSVC: creating " + ss + " in " + ps);
+ return ss;
+ }
+
+ public void dumpLocked(PrintWriter pw, String reqPackage, long now, boolean dumpSummary,
+ boolean dumpAll, boolean activeOnly) {
+ long totalTime = DumpUtils.dumpSingleTime(null, null, mMemFactorDurations, mMemFactor,
+ mStartTime, now);
+ boolean sepNeeded = false;
+ if (mSysMemUsage.getKeyCount() > 0) {
+ pw.println("System memory usage:");
+ mSysMemUsage.dump(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ);
+ sepNeeded = true;
+ }
+ ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
+ boolean printedHeader = false;
+ for (int ip=0; ip<pkgMap.size(); ip++) {
+ final String pkgName = pkgMap.keyAt(ip);
+ final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip);
+ for (int iu=0; iu<uids.size(); iu++) {
+ final int uid = uids.keyAt(iu);
+ final SparseArray<PackageState> vpkgs = uids.valueAt(iu);
+ for (int iv=0; iv<vpkgs.size(); iv++) {
+ final int vers = vpkgs.keyAt(iv);
+ final PackageState pkgState = vpkgs.valueAt(iv);
+ final int NPROCS = pkgState.mProcesses.size();
+ final int NSRVS = pkgState.mServices.size();
+ final boolean pkgMatch = reqPackage == null || reqPackage.equals(pkgName);
+ if (!pkgMatch) {
+ boolean procMatch = false;
+ for (int iproc=0; iproc<NPROCS; iproc++) {
+ ProcessState proc = pkgState.mProcesses.valueAt(iproc);
+ if (reqPackage.equals(proc.getName())) {
+ procMatch = true;
+ break;
+ }
+ }
+ if (!procMatch) {
+ continue;
+ }
+ }
+ if (NPROCS > 0 || NSRVS > 0) {
+ if (!printedHeader) {
+ if (sepNeeded) pw.println();
+ pw.println("Per-Package Stats:");
+ printedHeader = true;
+ sepNeeded = true;
+ }
+ pw.print(" * "); pw.print(pkgName); pw.print(" / ");
+ UserHandle.formatUid(pw, uid); pw.print(" / v");
+ pw.print(vers); pw.println(":");
+ }
+ if (!dumpSummary || dumpAll) {
+ for (int iproc=0; iproc<NPROCS; iproc++) {
+ ProcessState proc = pkgState.mProcesses.valueAt(iproc);
+ if (!pkgMatch && !reqPackage.equals(proc.getName())) {
+ continue;
+ }
+ if (activeOnly && !proc.isInUse()) {
+ pw.print(" (Not active: ");
+ pw.print(pkgState.mProcesses.keyAt(iproc)); pw.println(")");
+ continue;
+ }
+ pw.print(" Process ");
+ pw.print(pkgState.mProcesses.keyAt(iproc));
+ if (proc.getCommonProcess().isMultiPackage()) {
+ pw.print(" (multi, ");
+ } else {
+ pw.print(" (unique, ");
+ }
+ pw.print(proc.getDurationsBucketCount());
+ pw.print(" entries)");
+ pw.println(":");
+ proc.dumpProcessState(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ,
+ ALL_PROC_STATES, now);
+ proc.dumpPss(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ,
+ ALL_PROC_STATES);
+ proc.dumpInternalLocked(pw, " ", dumpAll);
+ }
+ } else {
+ ArrayList<ProcessState> procs = new ArrayList<ProcessState>();
+ for (int iproc=0; iproc<NPROCS; iproc++) {
+ ProcessState proc = pkgState.mProcesses.valueAt(iproc);
+ if (!pkgMatch && !reqPackage.equals(proc.getName())) {
+ continue;
+ }
+ if (activeOnly && !proc.isInUse()) {
+ continue;
+ }
+ procs.add(proc);
+ }
+ DumpUtils.dumpProcessSummaryLocked(pw, " ", procs,
+ ALL_SCREEN_ADJ, ALL_MEM_ADJ, NON_CACHED_PROC_STATES,
+ now, totalTime);
+ }
+ for (int isvc=0; isvc<NSRVS; isvc++) {
+ ServiceState svc = pkgState.mServices.valueAt(isvc);
+ if (!pkgMatch && !reqPackage.equals(svc.getProcessName())) {
+ continue;
+ }
+ if (activeOnly && !svc.isInUse()) {
+ pw.print(" (Not active: ");
+ pw.print(pkgState.mServices.keyAt(isvc)); pw.println(")");
+ continue;
+ }
+ if (dumpAll) {
+ pw.print(" Service ");
+ } else {
+ pw.print(" * ");
+ }
+ pw.print(pkgState.mServices.keyAt(isvc));
+ pw.println(":");
+ pw.print(" Process: "); pw.println(svc.getProcessName());
+ svc.dumpStats(pw, " ", " ", " ",
+ now, totalTime, dumpSummary, dumpAll);
+ }
+ }
+ }
+ }
+
+ ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+ printedHeader = false;
+ int numShownProcs = 0, numTotalProcs = 0;
+ for (int ip=0; ip<procMap.size(); ip++) {
+ String procName = procMap.keyAt(ip);
+ SparseArray<ProcessState> uids = procMap.valueAt(ip);
+ for (int iu=0; iu<uids.size(); iu++) {
+ int uid = uids.keyAt(iu);
+ numTotalProcs++;
+ final ProcessState proc = uids.valueAt(iu);
+ if (proc.hasAnyData()) {
+ continue;
+ }
+ if (!proc.isMultiPackage()) {
+ continue;
+ }
+ if (reqPackage != null && !reqPackage.equals(procName)
+ && !reqPackage.equals(proc.getPackage())) {
+ continue;
+ }
+ numShownProcs++;
+ if (sepNeeded) {
+ pw.println();
+ }
+ sepNeeded = true;
+ if (!printedHeader) {
+ pw.println("Multi-Package Common Processes:");
+ printedHeader = true;
+ }
+ if (activeOnly && !proc.isInUse()) {
+ pw.print(" (Not active: "); pw.print(procName); pw.println(")");
+ continue;
+ }
+ pw.print(" * "); pw.print(procName); pw.print(" / ");
+ UserHandle.formatUid(pw, uid);
+ pw.print(" ("); pw.print(proc.getDurationsBucketCount());
+ pw.print(" entries)"); pw.println(":");
+ proc.dumpProcessState(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ,
+ ALL_PROC_STATES, now);
+ proc.dumpPss(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ, ALL_PROC_STATES);
+ proc.dumpInternalLocked(pw, " ", dumpAll);
+ }
+ }
+ if (dumpAll) {
+ pw.println();
+ pw.print(" Total procs: "); pw.print(numShownProcs);
+ pw.print(" shown of "); pw.print(numTotalProcs); pw.println(" total");
+ }
+
+ if (sepNeeded) {
+ pw.println();
+ }
+ if (dumpSummary) {
+ pw.println("Summary:");
+ dumpSummaryLocked(pw, reqPackage, now, activeOnly);
+ } else {
+ dumpTotalsLocked(pw, now);
+ }
+
+ if (dumpAll) {
+ pw.println();
+ pw.println("Internal state:");
+ /*
+ pw.print(" Num long arrays: "); pw.println(mLongs.size());
+ pw.print(" Next long entry: "); pw.println(mNextLong);
+ */
+ pw.print(" mRunning="); pw.println(mRunning);
+ }
+ }
+
+ public void dumpSummaryLocked(PrintWriter pw, String reqPackage, long now, boolean activeOnly) {
+ long totalTime = DumpUtils.dumpSingleTime(null, null, mMemFactorDurations, mMemFactor,
+ mStartTime, now);
+ dumpFilteredSummaryLocked(pw, null, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ,
+ ALL_PROC_STATES, NON_CACHED_PROC_STATES, now, totalTime, reqPackage, activeOnly);
+ pw.println();
+ dumpTotalsLocked(pw, now);
+ }
+
+ long printMemoryCategory(PrintWriter pw, String prefix, String label, double memWeight,
+ long totalTime, long curTotalMem, int samples) {
+ if (memWeight != 0) {
+ long mem = (long)(memWeight * 1024 / totalTime);
+ pw.print(prefix);
+ pw.print(label);
+ pw.print(": ");
+ DebugUtils.printSizeValue(pw, mem);
+ pw.print(" (");
+ pw.print(samples);
+ pw.print(" samples)");
+ pw.println();
+ return curTotalMem + mem;
+ }
+ return curTotalMem;
+ }
+
+ void dumpTotalsLocked(PrintWriter pw, long now) {
+ pw.println("Run time Stats:");
+ DumpUtils.dumpSingleTime(pw, " ", mMemFactorDurations, mMemFactor, mStartTime, now);
+ pw.println();
+ pw.println("Memory usage:");
+ TotalMemoryUseCollection totalMem = new TotalMemoryUseCollection(ALL_SCREEN_ADJ,
+ ALL_MEM_ADJ);
+ computeTotalMemoryUse(totalMem, now);
+ long totalPss = 0;
+ totalPss = printMemoryCategory(pw, " ", "Kernel ", totalMem.sysMemKernelWeight,
+ totalMem.totalTime, totalPss, totalMem.sysMemSamples);
+ totalPss = printMemoryCategory(pw, " ", "Native ", totalMem.sysMemNativeWeight,
+ totalMem.totalTime, totalPss, totalMem.sysMemSamples);
+ for (int i=0; i<STATE_COUNT; i++) {
+ // Skip restarting service state -- that is not actually a running process.
+ if (i != STATE_SERVICE_RESTARTING) {
+ totalPss = printMemoryCategory(pw, " ", DumpUtils.STATE_NAMES[i],
+ totalMem.processStateWeight[i], totalMem.totalTime, totalPss,
+ totalMem.processStateSamples[i]);
+ }
+ }
+ totalPss = printMemoryCategory(pw, " ", "Cached ", totalMem.sysMemCachedWeight,
+ totalMem.totalTime, totalPss, totalMem.sysMemSamples);
+ totalPss = printMemoryCategory(pw, " ", "Free ", totalMem.sysMemFreeWeight,
+ totalMem.totalTime, totalPss, totalMem.sysMemSamples);
+ totalPss = printMemoryCategory(pw, " ", "Z-Ram ", totalMem.sysMemZRamWeight,
+ totalMem.totalTime, totalPss, totalMem.sysMemSamples);
+ pw.print(" TOTAL : ");
+ DebugUtils.printSizeValue(pw, totalPss);
+ pw.println();
+ printMemoryCategory(pw, " ", DumpUtils.STATE_NAMES[STATE_SERVICE_RESTARTING],
+ totalMem.processStateWeight[STATE_SERVICE_RESTARTING], totalMem.totalTime, totalPss,
+ totalMem.processStateSamples[STATE_SERVICE_RESTARTING]);
+ pw.println();
+ pw.print(" Start time: ");
+ pw.print(DateFormat.format("yyyy-MM-dd HH:mm:ss", mTimePeriodStartClock));
+ pw.println();
+ pw.print(" Total elapsed time: ");
+ TimeUtils.formatDuration(
+ (mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime)
+ - mTimePeriodStartRealtime, pw);
+ boolean partial = true;
+ if ((mFlags&FLAG_SHUTDOWN) != 0) {
+ pw.print(" (shutdown)");
+ partial = false;
+ }
+ if ((mFlags&FLAG_SYSPROPS) != 0) {
+ pw.print(" (sysprops)");
+ partial = false;
+ }
+ if ((mFlags&FLAG_COMPLETE) != 0) {
+ pw.print(" (complete)");
+ partial = false;
+ }
+ if (partial) {
+ pw.print(" (partial)");
+ }
+ pw.print(' ');
+ pw.print(mRuntime);
+ pw.println();
+ }
+
+ void dumpFilteredSummaryLocked(PrintWriter pw, String header, String prefix,
+ int[] screenStates, int[] memStates, int[] procStates,
+ int[] sortProcStates, long now, long totalTime, String reqPackage, boolean activeOnly) {
+ ArrayList<ProcessState> procs = collectProcessesLocked(screenStates, memStates,
+ procStates, sortProcStates, now, reqPackage, activeOnly);
+ if (procs.size() > 0) {
+ if (header != null) {
+ pw.println();
+ pw.println(header);
+ }
+ DumpUtils.dumpProcessSummaryLocked(pw, prefix, procs, screenStates, memStates,
+ sortProcStates, now, totalTime);
+ }
+ }
+
+ public ArrayList<ProcessState> collectProcessesLocked(int[] screenStates, int[] memStates,
+ int[] procStates, int sortProcStates[], long now, String reqPackage,
+ boolean activeOnly) {
+ final ArraySet<ProcessState> foundProcs = new ArraySet<ProcessState>();
+ final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
+ for (int ip=0; ip<pkgMap.size(); ip++) {
+ final String pkgName = pkgMap.keyAt(ip);
+ final SparseArray<SparseArray<PackageState>> procs = pkgMap.valueAt(ip);
+ for (int iu=0; iu<procs.size(); iu++) {
+ final SparseArray<PackageState> vpkgs = procs.valueAt(iu);
+ final int NVERS = vpkgs.size();
+ for (int iv=0; iv<NVERS; iv++) {
+ final PackageState state = vpkgs.valueAt(iv);
+ final int NPROCS = state.mProcesses.size();
+ final boolean pkgMatch = reqPackage == null || reqPackage.equals(pkgName);
+ for (int iproc=0; iproc<NPROCS; iproc++) {
+ final ProcessState proc = state.mProcesses.valueAt(iproc);
+ if (!pkgMatch && !reqPackage.equals(proc.getName())) {
+ continue;
+ }
+ if (activeOnly && !proc.isInUse()) {
+ continue;
+ }
+ foundProcs.add(proc.getCommonProcess());
+ }
+ }
+ }
+ }
+ ArrayList<ProcessState> outProcs = new ArrayList<ProcessState>(foundProcs.size());
+ for (int i=0; i<foundProcs.size(); i++) {
+ ProcessState proc = foundProcs.valueAt(i);
+ if (proc.computeProcessTimeLocked(screenStates, memStates, procStates, now) > 0) {
+ outProcs.add(proc);
+ if (procStates != sortProcStates) {
+ proc.computeProcessTimeLocked(screenStates, memStates, sortProcStates, now);
+ }
+ }
+ }
+ Collections.sort(outProcs, ProcessState.COMPARATOR);
+ return outProcs;
+ }
+
+ public void dumpCheckinLocked(PrintWriter pw, String reqPackage) {
+ final long now = SystemClock.uptimeMillis();
+ final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
+ pw.println("vers,5");
+ pw.print("period,"); pw.print(mTimePeriodStartClockStr);
+ pw.print(","); pw.print(mTimePeriodStartRealtime); pw.print(",");
+ pw.print(mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime);
+ boolean partial = true;
+ if ((mFlags&FLAG_SHUTDOWN) != 0) {
+ pw.print(",shutdown");
+ partial = false;
+ }
+ if ((mFlags&FLAG_SYSPROPS) != 0) {
+ pw.print(",sysprops");
+ partial = false;
+ }
+ if ((mFlags&FLAG_COMPLETE) != 0) {
+ pw.print(",complete");
+ partial = false;
+ }
+ if (partial) {
+ pw.print(",partial");
+ }
+ pw.println();
+ pw.print("config,"); pw.println(mRuntime);
+ for (int ip=0; ip<pkgMap.size(); ip++) {
+ final String pkgName = pkgMap.keyAt(ip);
+ if (reqPackage != null && !reqPackage.equals(pkgName)) {
+ continue;
+ }
+ final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip);
+ for (int iu=0; iu<uids.size(); iu++) {
+ final int uid = uids.keyAt(iu);
+ final SparseArray<PackageState> vpkgs = uids.valueAt(iu);
+ for (int iv=0; iv<vpkgs.size(); iv++) {
+ final int vers = vpkgs.keyAt(iv);
+ final PackageState pkgState = vpkgs.valueAt(iv);
+ final int NPROCS = pkgState.mProcesses.size();
+ final int NSRVS = pkgState.mServices.size();
+ for (int iproc=0; iproc<NPROCS; iproc++) {
+ ProcessState proc = pkgState.mProcesses.valueAt(iproc);
+ proc.dumpPackageProcCheckin(pw, pkgName, uid, vers,
+ pkgState.mProcesses.keyAt(iproc), now);
+ }
+ for (int isvc=0; isvc<NSRVS; isvc++) {
+ final String serviceName = DumpUtils.collapseString(pkgName,
+ pkgState.mServices.keyAt(isvc));
+ final ServiceState svc = pkgState.mServices.valueAt(isvc);
+ svc.dumpTimesCheckin(pw, pkgName, uid, vers, serviceName, now);
+ }
+ }
+ }
+ }
+
+ ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+ for (int ip=0; ip<procMap.size(); ip++) {
+ String procName = procMap.keyAt(ip);
+ SparseArray<ProcessState> uids = procMap.valueAt(ip);
+ for (int iu=0; iu<uids.size(); iu++) {
+ final int uid = uids.keyAt(iu);
+ final ProcessState procState = uids.valueAt(iu);
+ procState.dumpProcCheckin(pw, procName, uid, now);
+ }
+ }
+ pw.print("total");
+ DumpUtils.dumpAdjTimesCheckin(pw, ",", mMemFactorDurations, mMemFactor, mStartTime, now);
+ pw.println();
+ final int sysMemUsageCount = mSysMemUsage.getKeyCount();
+ if (sysMemUsageCount > 0) {
+ pw.print("sysmemusage");
+ for (int i=0; i<sysMemUsageCount; i++) {
+ final int key = mSysMemUsage.getKeyAt(i);
+ final int type = SparseMappingTable.getIdFromKey(key);
+ pw.print(",");
+ DumpUtils.printProcStateTag(pw, type);
+ for (int j=SYS_MEM_USAGE_SAMPLE_COUNT; j<SYS_MEM_USAGE_COUNT; j++) {
+ if (j > SYS_MEM_USAGE_CACHED_MINIMUM) {
+ pw.print(":");
+ }
+ pw.print(mSysMemUsage.getValue(key, j));
+ }
+ }
+ }
+ pw.println();
+ TotalMemoryUseCollection totalMem = new TotalMemoryUseCollection(ALL_SCREEN_ADJ,
+ ALL_MEM_ADJ);
+ computeTotalMemoryUse(totalMem, now);
+ pw.print("weights,");
+ pw.print(totalMem.totalTime);
+ pw.print(",");
+ pw.print(totalMem.sysMemCachedWeight);
+ pw.print(":");
+ pw.print(totalMem.sysMemSamples);
+ pw.print(",");
+ pw.print(totalMem.sysMemFreeWeight);
+ pw.print(":");
+ pw.print(totalMem.sysMemSamples);
+ pw.print(",");
+ pw.print(totalMem.sysMemZRamWeight);
+ pw.print(":");
+ pw.print(totalMem.sysMemSamples);
+ pw.print(",");
+ pw.print(totalMem.sysMemKernelWeight);
+ pw.print(":");
+ pw.print(totalMem.sysMemSamples);
+ pw.print(",");
+ pw.print(totalMem.sysMemNativeWeight);
+ pw.print(":");
+ pw.print(totalMem.sysMemSamples);
+ for (int i=0; i<STATE_COUNT; i++) {
+ pw.print(",");
+ pw.print(totalMem.processStateWeight[i]);
+ pw.print(":");
+ pw.print(totalMem.processStateSamples[i]);
+ }
+ pw.println();
+ }
+
+
+ final public static class ProcessStateHolder {
+ public final int appVersion;
+ public ProcessState state;
+
+ public ProcessStateHolder(int _appVersion) {
+ appVersion = _appVersion;
+ }
+ }
+
+ public static final class PackageState {
+ public final ArrayMap<String, ProcessState> mProcesses
+ = new ArrayMap<String, ProcessState>();
+ public final ArrayMap<String, ServiceState> mServices
+ = new ArrayMap<String, ServiceState>();
+ public final String mPackageName;
+ public final int mUid;
+
+ public PackageState(String packageName, int uid) {
+ mUid = uid;
+ mPackageName = packageName;
+ }
+ }
+
+ public static final class ProcessDataCollection {
+ final int[] screenStates;
+ final int[] memStates;
+ final int[] procStates;
+
+ public long totalTime;
+ public long numPss;
+ public long minPss;
+ public long avgPss;
+ public long maxPss;
+ public long minUss;
+ public long avgUss;
+ public long maxUss;
+
+ public ProcessDataCollection(int[] _screenStates, int[] _memStates, int[] _procStates) {
+ screenStates = _screenStates;
+ memStates = _memStates;
+ procStates = _procStates;
+ }
+
+ void print(PrintWriter pw, long overallTime, boolean full) {
+ if (totalTime > overallTime) {
+ pw.print("*");
+ }
+ DumpUtils.printPercent(pw, (double) totalTime / (double) overallTime);
+ if (numPss > 0) {
+ pw.print(" (");
+ DebugUtils.printSizeValue(pw, minPss * 1024);
+ pw.print("-");
+ DebugUtils.printSizeValue(pw, avgPss * 1024);
+ pw.print("-");
+ DebugUtils.printSizeValue(pw, maxPss * 1024);
+ pw.print("/");
+ DebugUtils.printSizeValue(pw, minUss * 1024);
+ pw.print("-");
+ DebugUtils.printSizeValue(pw, avgUss * 1024);
+ pw.print("-");
+ DebugUtils.printSizeValue(pw, maxUss * 1024);
+ if (full) {
+ pw.print(" over ");
+ pw.print(numPss);
+ }
+ pw.print(")");
+ }
+ }
+ }
+
+ public static class TotalMemoryUseCollection {
+ final int[] screenStates;
+ final int[] memStates;
+
+ public TotalMemoryUseCollection(int[] _screenStates, int[] _memStates) {
+ screenStates = _screenStates;
+ memStates = _memStates;
+ }
+
+ public long totalTime;
+ public long[] processStatePss = new long[STATE_COUNT];
+ public double[] processStateWeight = new double[STATE_COUNT];
+ public long[] processStateTime = new long[STATE_COUNT];
+ public int[] processStateSamples = new int[STATE_COUNT];
+ public long[] sysMemUsage = new long[SYS_MEM_USAGE_COUNT];
+ public double sysMemCachedWeight;
+ public double sysMemFreeWeight;
+ public double sysMemZRamWeight;
+ public double sysMemKernelWeight;
+ public double sysMemNativeWeight;
+ public int sysMemSamples;
+ }
+
+}
diff --git a/core/java/com/android/internal/app/procstats/PssTable.java b/core/java/com/android/internal/app/procstats/PssTable.java
new file mode 100644
index 0000000..b6df983
--- /dev/null
+++ b/core/java/com/android/internal/app/procstats/PssTable.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.app.procstats;
+
+import static com.android.internal.app.procstats.ProcessStats.PSS_SAMPLE_COUNT;
+import static com.android.internal.app.procstats.ProcessStats.PSS_MINIMUM;
+import static com.android.internal.app.procstats.ProcessStats.PSS_AVERAGE;
+import static com.android.internal.app.procstats.ProcessStats.PSS_MAXIMUM;
+import static com.android.internal.app.procstats.ProcessStats.PSS_USS_MINIMUM;
+import static com.android.internal.app.procstats.ProcessStats.PSS_USS_AVERAGE;
+import static com.android.internal.app.procstats.ProcessStats.PSS_USS_MAXIMUM;
+import static com.android.internal.app.procstats.ProcessStats.PSS_COUNT;
+
+/**
+ * Class to accumulate PSS data.
+ */
+public class PssTable extends SparseMappingTable.Table {
+ /**
+ * Construct the PssTable with 'tableData' as backing store
+ * for the longs data.
+ */
+ public PssTable(SparseMappingTable tableData) {
+ super(tableData);
+ }
+
+ /**
+ * Merge the the values from the other table into this one.
+ */
+ public void mergeStats(PssTable that) {
+ final int N = that.getKeyCount();
+ for (int i=0; i<N; i++) {
+ final int key = that.getKeyAt(i);
+ final int state = SparseMappingTable.getIdFromKey(key);
+ mergeStats(state, (int)that.getValue(key, PSS_SAMPLE_COUNT),
+ that.getValue(key, PSS_MINIMUM),
+ that.getValue(key, PSS_AVERAGE),
+ that.getValue(key, PSS_MAXIMUM),
+ that.getValue(key, PSS_USS_MINIMUM),
+ that.getValue(key, PSS_USS_AVERAGE),
+ that.getValue(key, PSS_USS_MAXIMUM));
+ }
+ }
+
+ /**
+ * Merge the supplied PSS data in. The new min pss will be the minimum of the existing
+ * one and the new one, the average will now incorporate the new average, etc.
+ */
+ public void mergeStats(int state, int inCount, long minPss, long avgPss, long maxPss,
+ long minUss, long avgUss, long maxUss) {
+ final int key = getOrAddKey((byte)state, PSS_COUNT);
+ final long count = getValue(key, PSS_SAMPLE_COUNT);
+ if (count == 0) {
+ setValue(key, PSS_SAMPLE_COUNT, inCount);
+ setValue(key, PSS_MINIMUM, minPss);
+ setValue(key, PSS_AVERAGE, avgPss);
+ setValue(key, PSS_MAXIMUM, maxPss);
+ setValue(key, PSS_USS_MINIMUM, minUss);
+ setValue(key, PSS_USS_AVERAGE, avgUss);
+ setValue(key, PSS_USS_MAXIMUM, maxUss);
+ } else {
+ setValue(key, PSS_SAMPLE_COUNT, count + inCount);
+
+ long val;
+
+ val = getValue(key, PSS_MINIMUM);
+ if (val > minPss) {
+ setValue(key, PSS_MINIMUM, minPss);
+ }
+
+ val = getValue(key, PSS_AVERAGE);
+ setValue(key, PSS_AVERAGE,
+ (long)(((val*(double)count)+(avgPss*(double)inCount)) / (count+inCount)));
+
+ val = getValue(key, PSS_MAXIMUM);
+ if (val < maxPss) {
+ setValue(key, PSS_MAXIMUM, maxPss);
+ }
+
+ val = getValue(key, PSS_USS_MINIMUM);
+ if (val > minUss) {
+ setValue(key, PSS_USS_MINIMUM, minUss);
+ }
+
+ val = getValue(key, PSS_USS_AVERAGE);
+ setValue(key, PSS_AVERAGE,
+ (long)(((val*(double)count)+(avgUss*(double)inCount)) / (count+inCount)));
+
+ val = getValue(key, PSS_USS_MAXIMUM);
+ if (val < maxUss) {
+ setValue(key, PSS_USS_MAXIMUM, maxUss);
+ }
+ }
+ }
+}
diff --git a/core/java/com/android/internal/app/procstats/ServiceState.java b/core/java/com/android/internal/app/procstats/ServiceState.java
new file mode 100644
index 0000000..2e11c43
--- /dev/null
+++ b/core/java/com/android/internal/app/procstats/ServiceState.java
@@ -0,0 +1,502 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.app.procstats;
+
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.text.format.DateFormat;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.DebugUtils;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TimeUtils;
+
+import com.android.internal.app.procstats.ProcessStats;
+import static com.android.internal.app.procstats.ProcessStats.STATE_NOTHING;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Objects;
+
+public final class ServiceState {
+ private static final String TAG = "ProcessStats";
+ private static final boolean DEBUG = false;
+
+ public static final int SERVICE_RUN = 0;
+ public static final int SERVICE_STARTED = 1;
+ public static final int SERVICE_BOUND = 2;
+ public static final int SERVICE_EXEC = 3;
+ public static final int SERVICE_COUNT = 4;
+
+ private final String mPackage;
+ private final String mProcessName;
+ private final String mName;
+ private final DurationsTable mDurations;
+
+ private ProcessState mProc;
+ private Object mOwner;
+
+ private int mRunCount;
+ private int mRunState = STATE_NOTHING;
+ private long mRunStartTime;
+
+ private boolean mStarted;
+ private boolean mRestarting;
+ private int mStartedCount;
+ private int mStartedState = STATE_NOTHING;
+ private long mStartedStartTime;
+
+ private int mBoundCount;
+ private int mBoundState = STATE_NOTHING;
+ private long mBoundStartTime;
+
+ private int mExecCount;
+ private int mExecState = STATE_NOTHING;
+ private long mExecStartTime;
+
+ public ServiceState(ProcessStats processStats, String pkg, String name,
+ String processName, ProcessState proc) {
+ mPackage = pkg;
+ mName = name;
+ mProcessName = processName;
+ mProc = proc;
+ mDurations = new DurationsTable(processStats.mTableData);
+ }
+
+ public String getPackage() {
+ return mPackage;
+ }
+
+ public String getProcessName() {
+ return mProcessName;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public ProcessState getProcess() {
+ return mProc;
+ }
+
+ public void setProcess(ProcessState proc) {
+ mProc = proc;
+ }
+
+ public void setMemFactor(int memFactor, long now) {
+ if (isRestarting()) {
+ setRestarting(true, memFactor, now);
+ } else if (isInUse()) {
+ if (mStartedState != ProcessStats.STATE_NOTHING) {
+ setStarted(true, memFactor, now);
+ }
+ if (mBoundState != ProcessStats.STATE_NOTHING) {
+ setBound(true, memFactor, now);
+ }
+ if (mExecState != ProcessStats.STATE_NOTHING) {
+ setExecuting(true, memFactor, now);
+ }
+ }
+ }
+
+ public void applyNewOwner(Object newOwner) {
+ if (mOwner != newOwner) {
+ if (mOwner == null) {
+ mOwner = newOwner;
+ mProc.incActiveServices(mName);
+ } else {
+ // There was already an old owner, reset this object for its
+ // new owner.
+ mOwner = newOwner;
+ if (mStarted || mBoundState != STATE_NOTHING || mExecState != STATE_NOTHING) {
+ long now = SystemClock.uptimeMillis();
+ if (mStarted) {
+ if (DEBUG) Slog.d(TAG, "Service has new owner " + newOwner
+ + " from " + mOwner + " while started: pkg="
+ + mPackage + " service=" + mName + " proc=" + mProc);
+ setStarted(false, 0, now);
+ }
+ if (mBoundState != STATE_NOTHING) {
+ if (DEBUG) Slog.d(TAG, "Service has new owner " + newOwner
+ + " from " + mOwner + " while bound: pkg="
+ + mPackage + " service=" + mName + " proc=" + mProc);
+ setBound(false, 0, now);
+ }
+ if (mExecState != STATE_NOTHING) {
+ if (DEBUG) Slog.d(TAG, "Service has new owner " + newOwner
+ + " from " + mOwner + " while executing: pkg="
+ + mPackage + " service=" + mName + " proc=" + mProc);
+ setExecuting(false, 0, now);
+ }
+ }
+ }
+ }
+ }
+
+ public void clearCurrentOwner(Object owner, boolean silently) {
+ if (mOwner == owner) {
+ mProc.decActiveServices(mName);
+ if (mStarted || mBoundState != STATE_NOTHING || mExecState != STATE_NOTHING) {
+ long now = SystemClock.uptimeMillis();
+ if (mStarted) {
+ if (!silently) {
+ Slog.wtfStack(TAG, "Service owner " + owner
+ + " cleared while started: pkg=" + mPackage + " service="
+ + mName + " proc=" + mProc);
+ }
+ setStarted(false, 0, now);
+ }
+ if (mBoundState != STATE_NOTHING) {
+ if (!silently) {
+ Slog.wtfStack(TAG, "Service owner " + owner
+ + " cleared while bound: pkg=" + mPackage + " service="
+ + mName + " proc=" + mProc);
+ }
+ setBound(false, 0, now);
+ }
+ if (mExecState != STATE_NOTHING) {
+ if (!silently) {
+ Slog.wtfStack(TAG, "Service owner " + owner
+ + " cleared while exec: pkg=" + mPackage + " service="
+ + mName + " proc=" + mProc);
+ }
+ setExecuting(false, 0, now);
+ }
+ }
+ mOwner = null;
+ }
+ }
+
+ public boolean isInUse() {
+ return mOwner != null || mRestarting;
+ }
+
+ public boolean isRestarting() {
+ return mRestarting;
+ }
+
+ public void add(ServiceState other) {
+ mDurations.addDurations(other.mDurations);
+ mRunCount += other.mRunCount;
+ mStartedCount += other.mStartedCount;
+ mBoundCount += other.mBoundCount;
+ mExecCount += other.mExecCount;
+ }
+
+ public void resetSafely(long now) {
+ mDurations.resetTable();
+ mRunCount = mRunState != STATE_NOTHING ? 1 : 0;
+ mStartedCount = mStartedState != STATE_NOTHING ? 1 : 0;
+ mBoundCount = mBoundState != STATE_NOTHING ? 1 : 0;
+ mExecCount = mExecState != STATE_NOTHING ? 1 : 0;
+ mRunStartTime = mStartedStartTime = mBoundStartTime = mExecStartTime = now;
+ }
+
+ public void writeToParcel(Parcel out, long now) {
+ mDurations.writeToParcel(out);
+ out.writeInt(mRunCount);
+ out.writeInt(mStartedCount);
+ out.writeInt(mBoundCount);
+ out.writeInt(mExecCount);
+ }
+
+ public boolean readFromParcel(Parcel in) {
+ if (!mDurations.readFromParcel(in)) {
+ return false;
+ }
+ mRunCount = in.readInt();
+ mStartedCount = in.readInt();
+ mBoundCount = in.readInt();
+ mExecCount = in.readInt();
+ return true;
+ }
+
+ public void commitStateTime(long now) {
+ if (mRunState != STATE_NOTHING) {
+ mDurations.addDuration(SERVICE_RUN + (mRunState*SERVICE_COUNT),
+ now - mRunStartTime);
+ mRunStartTime = now;
+ }
+ if (mStartedState != STATE_NOTHING) {
+ mDurations.addDuration(SERVICE_STARTED + (mStartedState*SERVICE_COUNT),
+ now - mStartedStartTime);
+ mStartedStartTime = now;
+ }
+ if (mBoundState != STATE_NOTHING) {
+ mDurations.addDuration(SERVICE_BOUND + (mBoundState*SERVICE_COUNT),
+ now - mBoundStartTime);
+ mBoundStartTime = now;
+ }
+ if (mExecState != STATE_NOTHING) {
+ mDurations.addDuration(SERVICE_EXEC + (mExecState*SERVICE_COUNT),
+ now - mExecStartTime);
+ mExecStartTime = now;
+ }
+ }
+
+ private void updateRunning(int memFactor, long now) {
+ final int state = (mStartedState != STATE_NOTHING || mBoundState != STATE_NOTHING
+ || mExecState != STATE_NOTHING) ? memFactor : STATE_NOTHING;
+ if (mRunState != state) {
+ if (mRunState != STATE_NOTHING) {
+ mDurations.addDuration(SERVICE_RUN + (mRunState*SERVICE_COUNT),
+ now - mRunStartTime);
+ } else if (state != STATE_NOTHING) {
+ mRunCount++;
+ }
+ mRunState = state;
+ mRunStartTime = now;
+ }
+ }
+
+ public void setStarted(boolean started, int memFactor, long now) {
+ if (mOwner == null) {
+ Slog.wtf(TAG, "Starting service " + this + " without owner");
+ }
+ mStarted = started;
+ updateStartedState(memFactor, now);
+ }
+
+ public void setRestarting(boolean restarting, int memFactor, long now) {
+ mRestarting = restarting;
+ updateStartedState(memFactor, now);
+ }
+
+ public void updateStartedState(int memFactor, long now) {
+ final boolean wasStarted = mStartedState != STATE_NOTHING;
+ final boolean started = mStarted || mRestarting;
+ final int state = started ? memFactor : STATE_NOTHING;
+ if (mStartedState != state) {
+ if (mStartedState != STATE_NOTHING) {
+ mDurations.addDuration(SERVICE_STARTED + (mStartedState*SERVICE_COUNT),
+ now - mStartedStartTime);
+ } else if (started) {
+ mStartedCount++;
+ }
+ mStartedState = state;
+ mStartedStartTime = now;
+ mProc = mProc.pullFixedProc(mPackage);
+ if (wasStarted != started) {
+ if (started) {
+ mProc.incStartedServices(memFactor, now, mName);
+ } else {
+ mProc.decStartedServices(memFactor, now, mName);
+ }
+ }
+ updateRunning(memFactor, now);
+ }
+ }
+
+ public void setBound(boolean bound, int memFactor, long now) {
+ if (mOwner == null) {
+ Slog.wtf(TAG, "Binding service " + this + " without owner");
+ }
+ final int state = bound ? memFactor : STATE_NOTHING;
+ if (mBoundState != state) {
+ if (mBoundState != STATE_NOTHING) {
+ mDurations.addDuration(SERVICE_BOUND + (mBoundState*SERVICE_COUNT),
+ now - mBoundStartTime);
+ } else if (bound) {
+ mBoundCount++;
+ }
+ mBoundState = state;
+ mBoundStartTime = now;
+ updateRunning(memFactor, now);
+ }
+ }
+
+ public void setExecuting(boolean executing, int memFactor, long now) {
+ if (mOwner == null) {
+ Slog.wtf(TAG, "Executing service " + this + " without owner");
+ }
+ final int state = executing ? memFactor : STATE_NOTHING;
+ if (mExecState != state) {
+ if (mExecState != STATE_NOTHING) {
+ mDurations.addDuration(SERVICE_EXEC + (mExecState*SERVICE_COUNT),
+ now - mExecStartTime);
+ } else if (executing) {
+ mExecCount++;
+ }
+ mExecState = state;
+ mExecStartTime = now;
+ updateRunning(memFactor, now);
+ }
+ }
+
+ public long getDuration(int opType, int curState, long startTime, int memFactor,
+ long now) {
+ int state = opType + (memFactor*SERVICE_COUNT);
+ long time = mDurations.getValueForId((byte)state);
+ if (curState == memFactor) {
+ time += now - startTime;
+ }
+ return time;
+ }
+
+ public void dumpStats(PrintWriter pw, String prefix, String prefixInner, String headerPrefix,
+ long now, long totalTime, boolean dumpSummary, boolean dumpAll) {
+ dumpStats(pw, prefix, prefixInner, headerPrefix, "Running",
+ mRunCount, ServiceState.SERVICE_RUN, mRunState,
+ mRunStartTime, now, totalTime, !dumpSummary || dumpAll);
+ dumpStats(pw, prefix, prefixInner, headerPrefix, "Started",
+ mStartedCount, ServiceState.SERVICE_STARTED, mStartedState,
+ mStartedStartTime, now, totalTime, !dumpSummary || dumpAll);
+ dumpStats(pw, prefix, prefixInner, headerPrefix, "Bound",
+ mBoundCount, ServiceState.SERVICE_BOUND, mBoundState,
+ mBoundStartTime, now, totalTime, !dumpSummary || dumpAll);
+ dumpStats(pw, prefix, prefixInner, headerPrefix, "Executing",
+ mExecCount, ServiceState.SERVICE_EXEC, mExecState,
+ mExecStartTime, now, totalTime, !dumpSummary || dumpAll);
+ if (dumpAll) {
+ if (mOwner != null) {
+ pw.print(" mOwner="); pw.println(mOwner);
+ }
+ if (mStarted || mRestarting) {
+ pw.print(" mStarted="); pw.print(mStarted);
+ pw.print(" mRestarting="); pw.println(mRestarting);
+ }
+ }
+ }
+
+ private void dumpStats(PrintWriter pw, String prefix, String prefixInner,
+ String headerPrefix, String header,
+ int count, int serviceType, int state, long startTime, long now, long totalTime,
+ boolean dumpAll) {
+ if (count != 0) {
+ if (dumpAll) {
+ pw.print(prefix); pw.print(header);
+ pw.print(" op count "); pw.print(count); pw.println(":");
+ dumpTime(pw, prefixInner, serviceType, state, startTime, now);
+ } else {
+ long myTime = dumpTime(null, null, serviceType, state, startTime, now);
+ pw.print(prefix); pw.print(headerPrefix); pw.print(header);
+ pw.print(" count "); pw.print(count);
+ pw.print(" / time ");
+ DumpUtils.printPercent(pw, (double)myTime/(double)totalTime);
+ pw.println();
+ }
+ }
+ }
+
+ public long dumpTime(PrintWriter pw, String prefix,
+ int serviceType, int curState, long curStartTime, long now) {
+ long totalTime = 0;
+ int printedScreen = -1;
+ for (int iscreen=0; iscreen<ProcessStats.ADJ_COUNT; iscreen+=ProcessStats.ADJ_SCREEN_MOD) {
+ int printedMem = -1;
+ for (int imem=0; imem<ProcessStats.ADJ_MEM_FACTOR_COUNT; imem++) {
+ int state = imem+iscreen;
+ long time = getDuration(serviceType, curState, curStartTime, state, now);
+ String running = "";
+ if (curState == state && pw != null) {
+ running = " (running)";
+ }
+ if (time != 0) {
+ if (pw != null) {
+ pw.print(prefix);
+ DumpUtils.printScreenLabel(pw, printedScreen != iscreen
+ ? iscreen : STATE_NOTHING);
+ printedScreen = iscreen;
+ DumpUtils.printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING,
+ (char)0);
+ printedMem = imem;
+ pw.print(": ");
+ TimeUtils.formatDuration(time, pw); pw.println(running);
+ }
+ totalTime += time;
+ }
+ }
+ }
+ if (totalTime != 0 && pw != null) {
+ pw.print(prefix);
+ pw.print(" TOTAL: ");
+ TimeUtils.formatDuration(totalTime, pw);
+ pw.println();
+ }
+ return totalTime;
+ }
+
+ public void dumpTimesCheckin(PrintWriter pw, String pkgName, int uid, int vers,
+ String serviceName, long now) {
+ dumpTimeCheckin(pw, "pkgsvc-run", pkgName, uid, vers, serviceName,
+ ServiceState.SERVICE_RUN, mRunCount, mRunState, mRunStartTime, now);
+ dumpTimeCheckin(pw, "pkgsvc-start", pkgName, uid, vers, serviceName,
+ ServiceState.SERVICE_STARTED, mStartedCount, mStartedState, mStartedStartTime, now);
+ dumpTimeCheckin(pw, "pkgsvc-bound", pkgName, uid, vers, serviceName,
+ ServiceState.SERVICE_BOUND, mBoundCount, mBoundState, mBoundStartTime, now);
+ dumpTimeCheckin(pw, "pkgsvc-exec", pkgName, uid, vers, serviceName,
+ ServiceState.SERVICE_EXEC, mExecCount, mExecState, mExecStartTime, now);
+ }
+
+ private void dumpTimeCheckin(PrintWriter pw, String label, String packageName,
+ int uid, int vers, String serviceName, int serviceType, int opCount,
+ int curState, long curStartTime, long now) {
+ if (opCount <= 0) {
+ return;
+ }
+ pw.print(label);
+ pw.print(",");
+ pw.print(packageName);
+ pw.print(",");
+ pw.print(uid);
+ pw.print(",");
+ pw.print(vers);
+ pw.print(",");
+ pw.print(serviceName);
+ pw.print(",");
+ pw.print(opCount);
+ boolean didCurState = false;
+ final int N = mDurations.getKeyCount();
+ for (int i=0; i<N; i++) {
+ final int key = mDurations.getKeyAt(i);
+ long time = mDurations.getValue(key);
+ int type = SparseMappingTable.getIdFromKey(key);
+ int memFactor = type / ServiceState.SERVICE_COUNT;
+ type %= ServiceState.SERVICE_COUNT;
+ if (type != serviceType) {
+ continue;
+ }
+ if (curState == memFactor) {
+ didCurState = true;
+ time += now - curStartTime;
+ }
+ DumpUtils.printAdjTagAndValue(pw, memFactor, time);
+ }
+ if (!didCurState && curState != STATE_NOTHING) {
+ DumpUtils.printAdjTagAndValue(pw, curState, now - curStartTime);
+ }
+ pw.println();
+ }
+
+
+ public String toString() {
+ return "ServiceState{" + Integer.toHexString(System.identityHashCode(this))
+ + " " + mName + " pkg=" + mPackage + " proc="
+ + Integer.toHexString(System.identityHashCode(this)) + "}";
+ }
+}
diff --git a/core/java/com/android/internal/app/procstats/SparseMappingTable.java b/core/java/com/android/internal/app/procstats/SparseMappingTable.java
new file mode 100644
index 0000000..7252276
--- /dev/null
+++ b/core/java/com/android/internal/app/procstats/SparseMappingTable.java
@@ -0,0 +1,631 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.app.procstats;
+
+import android.os.Build;
+import android.os.Parcel;
+import android.util.Slog;
+import libcore.util.EmptyArray;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import com.android.internal.util.GrowingArrayUtils;
+
+/**
+ * Class that contains a set of tables mapping byte ids to long values.
+ *
+ * This class is used to store the ProcessStats data. This data happens to be
+ * a set of very sparse tables, that is mostly append or overwrite, with infrequent
+ * resets of the data.
+ *
+ * Data is stored as a list of large long[] arrays containing the actual values. There are a
+ * set of Table objects that each contain a small array mapping the byte IDs to a position
+ * in the larger arrays.
+ *
+ * The data itself is either a single long value or a range of long values which are always
+ * stored continguously in one of the long arrays. When the caller allocates a slot with
+ * getOrAddKey, an int key is returned. That key can be re-retreived with getKey without
+ * allocating the value. The data can then be set or retrieved with that key.
+ */
+public class SparseMappingTable {
+ private static final String TAG = "SparseMappingTable";
+
+ // How big each array is.
+ public static final int ARRAY_SIZE = 4096;
+
+ public static final int INVALID_KEY = 0xffffffff;
+
+ // Where the "type"/"state" part of the data appears in an offset integer.
+ private static final int ID_SHIFT = 0;
+ private static final int ID_MASK = 0xff;
+ // Where the "which array" part of the data appears in an offset integer.
+ private static final int ARRAY_SHIFT = 8;
+ private static final int ARRAY_MASK = 0xff;
+ // Where the "index into array" part of the data appears in an offset integer.
+ private static final int INDEX_SHIFT = 16;
+ private static final int INDEX_MASK = 0xffff;
+
+ private int mSequence;
+ private int mNextIndex;
+ private final ArrayList<long[]> mLongs = new ArrayList<long[]>();
+
+ /**
+ * A table of data as stored in a SparseMappingTable.
+ */
+ public static class Table {
+ // When mSequence is this this our data better be empty
+ private static final int UNINITIALIZED_SEQUENCE = -1;
+
+ private SparseMappingTable mParent;
+ private int mSequence = UNINITIALIZED_SEQUENCE;
+ private int[] mTable;
+ private int mSize;
+
+ public Table(SparseMappingTable parent) {
+ mParent = parent;
+ mSequence = parent.mSequence;
+ }
+
+ /**
+ * Pulls the data from 'copyFrom' and stores it in our own longs table.
+ *
+ * @param copyFrom The Table to copy from
+ * @param valueCount The number of values to copy for each key
+ */
+ public void copyFrom(Table copyFrom, int valueCount) {
+ mTable = null;
+ mSize = 0;
+
+ final int N = copyFrom.getKeyCount();
+ for (int i=0; i<N; i++) {
+ final int theirKey = copyFrom.getKeyAt(i);
+ final long[] theirLongs = copyFrom.mParent.mLongs.get(getArrayFromKey(theirKey));
+
+ final byte id = SparseMappingTable.getIdFromKey(theirKey);
+
+ final int myKey = this.getOrAddKey((byte)id, valueCount);
+ final long[] myLongs = mParent.mLongs.get(getArrayFromKey(myKey));
+
+ System.arraycopy(theirLongs, getIndexFromKey(theirKey),
+ myLongs, getIndexFromKey(myKey), valueCount);
+ }
+ }
+
+ /**
+ * Allocates data in the buffer, and stores that key in the mapping for this
+ * table.
+ *
+ * @param id The id of the item (will be used in making the key)
+ * @param count The number of bytes to allocate. Must be less than
+ * SparseMappingTable.ARRAY_SIZE.
+ *
+ * @return The 'key' for this data value, which contains both the id itself
+ * and the location in the long arrays that the data is actually stored
+ * but should be considered opaque to the caller.
+ */
+ public int getOrAddKey(byte id, int count) {
+ // This is the only place we add data to mParent.mLongs, so this is the time
+ // to update our sequence to match there.
+ if (mSequence == UNINITIALIZED_SEQUENCE) {
+ mSequence = mParent.mSequence;
+ }
+
+ assertConsistency();
+
+ final int idx = binarySearch(id);
+ if (idx >= 0) {
+ // Found
+ return mTable[idx];
+ } else {
+ // Not found. Need to allocate it.
+
+ // Get an array with enough space to store 'count' values.
+ final ArrayList<long[]> list = mParent.mLongs;
+ int whichArray = list.size()-1;
+ long[] array = list.get(whichArray);
+ if (mParent.mNextIndex + count > array.length) {
+ // if it won't fit then make a new array.
+ array = new long[ARRAY_SIZE];
+ list.add(array);
+ whichArray++;
+ mParent.mNextIndex = 0;
+ }
+
+ // The key is a combination of whichArray, which index in that array, and
+ // the table value itself, which will be used for lookup
+ final int key = (whichArray << ARRAY_SHIFT)
+ | (mParent.mNextIndex << INDEX_SHIFT)
+ | (((int)id) << ID_SHIFT);
+
+ mParent.mNextIndex += count;
+
+ // Store the key in the sparse lookup table for this Table object.
+ mTable = GrowingArrayUtils.insert(mTable != null ? mTable : EmptyArray.INT,
+ mSize, ~idx, key);
+ mSize++;
+
+ return key;
+ }
+ }
+
+ /**
+ * Looks up a key in the table.
+ *
+ * @return The key from this table or INVALID_KEY if the id is not found.
+ */
+ public int getKey(byte id) {
+ assertConsistency();
+
+ final int idx = binarySearch(id);
+ if (idx >= 0) {
+ return mTable[idx];
+ } else {
+ return INVALID_KEY;
+ }
+ }
+
+ /**
+ * Get the value for the given key and offset from that key.
+ *
+ * @param key A key as obtained from getKey or getOrAddKey.
+ * @param value The value to set.
+ */
+ public long getValue(int key) {
+ return getValue(key, 0);
+ }
+
+ /**
+ * Get the value for the given key and offset from that key.
+ *
+ * @param key A key as obtained from getKey or getOrAddKey.
+ * @param index The offset from that key. Must be less than the count
+ * provided to getOrAddKey when the space was allocated.
+ * @param value The value to set.
+ *
+ * @return the value, or 0 in case of an error
+ */
+ public long getValue(int key, int index) {
+ assertConsistency();
+
+ try {
+ final long[] array = mParent.mLongs.get(getArrayFromKey(key));
+ return array[getIndexFromKey(key) + index];
+ } catch (IndexOutOfBoundsException ex) {
+ logOrThrow("key=0x" + Integer.toHexString(key)
+ + " index=" + index + " -- " + dumpInternalState(), ex);
+ return 0;
+ }
+ }
+
+ /**
+ * Set the value for the given id at offset 0 from that id.
+ * If the id is not found, return 0 instead.
+ *
+ * @param id The id of the item.
+ */
+ public long getValueForId(byte id) {
+ return getValueForId(id, 0);
+ }
+
+ /**
+ * Set the value for the given id and index offset from that id.
+ * If the id is not found, return 0 instead.
+ *
+ * @param id The id of the item.
+ * @param index The offset from that key. Must be less than the count
+ * provided to getOrAddKey when the space was allocated.
+ */
+ public long getValueForId(byte id, int index) {
+ assertConsistency();
+
+ final int idx = binarySearch(id);
+ if (idx >= 0) {
+ final int key = mTable[idx];
+ try {
+ final long[] array = mParent.mLongs.get(getArrayFromKey(key));
+ return array[getIndexFromKey(key) + index];
+ } catch (IndexOutOfBoundsException ex) {
+ logOrThrow("id=0x" + Integer.toHexString(id) + " idx=" + idx
+ + " key=0x" + Integer.toHexString(key) + " index=" + index
+ + " -- " + dumpInternalState(), ex);
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Return the raw storage long[] for the given key.
+ */
+ public long[] getArrayForKey(int key) {
+ assertConsistency();
+
+ return mParent.mLongs.get(getArrayFromKey(key));
+ }
+
+ /**
+ * Set the value for the given key and offset from that key.
+ *
+ * @param key A key as obtained from getKey or getOrAddKey.
+ * @param value The value to set.
+ */
+ public void setValue(int key, long value) {
+ setValue(key, 0, value);
+ }
+
+ /**
+ * Set the value for the given key and offset from that key.
+ *
+ * @param key A key as obtained from getKey or getOrAddKey.
+ * @param index The offset from that key. Must be less than the count
+ * provided to getOrAddKey when the space was allocated.
+ * @param value The value to set.
+ */
+ public void setValue(int key, int index, long value) {
+ assertConsistency();
+
+ if (value < 0) {
+ logOrThrow("can't store negative values"
+ + " key=0x" + Integer.toHexString(key)
+ + " index=" + index + " value=" + value
+ + " -- " + dumpInternalState());
+ return;
+ }
+
+ try {
+ final long[] array = mParent.mLongs.get(getArrayFromKey(key));
+ array[getIndexFromKey(key) + index] = value;
+ } catch (IndexOutOfBoundsException ex) {
+ logOrThrow("key=0x" + Integer.toHexString(key)
+ + " index=" + index + " value=" + value
+ + " -- " + dumpInternalState(), ex);
+ return;
+ }
+ }
+
+ /**
+ * Clear out the table, and reset the sequence numbers so future writes
+ * without allocations will assert.
+ */
+ public void resetTable() {
+ // Clear out our table.
+ mTable = null;
+ mSize = 0;
+
+ // Reset our sequence number. This will make all read/write calls
+ // start to fail, and then when we re-allocate it will be re-synced
+ // to that of mParent.
+ mSequence = UNINITIALIZED_SEQUENCE;
+ }
+
+ /**
+ * Write the keys stored in the table to the parcel. The parent must
+ * be separately written. Does not save the actual data.
+ */
+ public void writeToParcel(Parcel out) {
+ out.writeInt(mSequence);
+ out.writeInt(mSize);
+ for (int i=0; i<mSize; i++) {
+ out.writeInt(mTable[i]);
+ }
+ }
+
+ /**
+ * Read the keys from the parcel. The parent (with its long array) must
+ * have been previously initialized.
+ */
+ public boolean readFromParcel(Parcel in) {
+ // Read the state
+ mSequence = in.readInt();
+ mSize = in.readInt();
+ if (mSize != 0) {
+ mTable = new int[mSize];
+ for (int i=0; i<mSize; i++) {
+ mTable[i] = in.readInt();
+ }
+ } else {
+ mTable = null;
+ }
+
+ // Make sure we're all healthy
+ if (validateKeys(true)) {
+ return true;
+ } else {
+ // Clear it out
+ mSize = 0;
+ mTable = null;
+ return false;
+ }
+ }
+
+ /**
+ * Return the number of keys that have been added to this Table.
+ */
+ public int getKeyCount() {
+ return mSize;
+ }
+
+ /**
+ * Get the key at the given index in our table.
+ */
+ public int getKeyAt(int i) {
+ return mTable[i];
+ }
+
+ /**
+ * Throw an exception if one of a variety of internal consistency checks fails.
+ */
+ private void assertConsistency() {
+ // Assert that our sequewnce number has been initialized. If it hasn't
+ // that means someone tried to read or write data without allocating it
+ // since we were created or reset.
+ if (mSequence == UNINITIALIZED_SEQUENCE) {
+ logOrThrow("mSequence == UNINITIALIZED_SEQUENCE in"
+ + " SparseMappingTable.Table. -- "
+ + dumpInternalState());
+ return;
+ }
+
+ // Assert that our sequence number matches mParent's. If it isn't that means
+ // we have been reset and our
+ if (mSequence != mParent.mSequence) {
+ if (mSequence < mParent.mSequence) {
+ logOrThrow("Sequence mismatch. SparseMappingTable.resetTable()"
+ + " called but not Table.resetTable() -- "
+ + dumpInternalState());
+ return;
+ } else if (mSequence > mParent.mSequence) {
+ logOrThrow("Sequence mismatch. Table.resetTable()"
+ + " called but not SparseMappingTable.resetTable() -- "
+ + dumpInternalState());
+ return;
+ }
+ }
+ }
+
+ /**
+ * Finds the 'id' inside the array of length size (physical size of the array
+ * is not used).
+ *
+ * @return The index of the array, or the bitwise not (~index) of where it
+ * would go if you wanted to insert 'id' into the array.
+ */
+ private int binarySearch(byte id) {
+ int lo = 0;
+ int hi = mSize - 1;
+
+ while (lo <= hi) {
+ int mid = (lo + hi) >>> 1;
+ byte midId = (byte)((mTable[mid] >> ID_SHIFT) & ID_MASK);
+
+ if (midId < id) {
+ lo = mid + 1;
+ } else if (midId > id) {
+ hi = mid - 1;
+ } else {
+ return mid; // id found
+ }
+ }
+ return ~lo; // id not present
+ }
+
+ /**
+ * Check that all the keys are valid locations in the long arrays.
+ *
+ * If any aren't, log it and return false. Else return true.
+ */
+ private boolean validateKeys(boolean log) {
+ ArrayList<long[]> longs = mParent.mLongs;
+ final int longsSize = longs.size();
+
+ final int N = mSize;
+ for (int i=0; i<N; i++) {
+ final int key = mTable[i];
+ final int arrayIndex = getArrayFromKey(key);
+ final int index = getIndexFromKey(key);
+ if (arrayIndex >= longsSize || index >= longs.get(arrayIndex).length) {
+ if (log) {
+ Slog.w(TAG, "Invalid stats at index " + i + " -- " + dumpInternalState());
+ }
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public String dumpInternalState() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("SparseMappingTable.Table{mSequence=");
+ sb.append(mSequence);
+ sb.append(" mParent.mSequence=");
+ sb.append(mParent.mSequence);
+ sb.append(" mParent.mLongs.size()=");
+ sb.append(mParent.mLongs.size());
+ sb.append(" mSize=");
+ sb.append(mSize);
+ sb.append(" mTable=");
+ if (mTable == null) {
+ sb.append("null");
+ } else {
+ final int N = mTable.length;
+ sb.append('[');
+ for (int i=0; i<N; i++) {
+ final int key = mTable[i];
+ sb.append("0x");
+ sb.append(Integer.toHexString((key >> ID_SHIFT) & ID_MASK));
+ sb.append("/0x");
+ sb.append(Integer.toHexString((key >> ARRAY_SHIFT) & ARRAY_MASK));
+ sb.append("/0x");
+ sb.append(Integer.toHexString((key >> INDEX_SHIFT) & INDEX_MASK));
+ if (i != N-1) {
+ sb.append(", ");
+ }
+ }
+ sb.append(']');
+ }
+ sb.append(" clazz=");
+ sb.append(getClass().getName());
+ sb.append('}');
+
+ return sb.toString();
+ }
+ }
+
+ /**
+ * Wipe out all the data.
+ */
+ public void reset() {
+ // Clear out mLongs, and prime it with a new array of data
+ mLongs.clear();
+ mLongs.add(new long[ARRAY_SIZE]);
+ mNextIndex = 0;
+
+ // Increment out sequence counter, because all of the tables will
+ // now be out of sync with the data.
+ mSequence++;
+ }
+
+ /**
+ * Write the data arrays to the parcel.
+ */
+ public void writeToParcel(Parcel out) {
+ out.writeInt(mSequence);
+ out.writeInt(mNextIndex);
+ final int N = mLongs.size();
+ out.writeInt(N);
+ for (int i=0; i<N-1; i++) {
+ final long[] array = mLongs.get(i);
+ out.writeInt(array.length);
+ writeCompactedLongArray(out, array, array.length);
+ }
+ // save less for the last one. upon re-loading they'll just start a new array.
+ final long[] lastLongs = mLongs.get(N-1);
+ out.writeInt(mNextIndex);
+ writeCompactedLongArray(out, lastLongs, mNextIndex);
+ }
+
+ /**
+ * Read the data arrays from the parcel.
+ */
+ public void readFromParcel(Parcel in) {
+ mSequence = in.readInt();
+ mNextIndex = in.readInt();
+
+ mLongs.clear();
+ final int N = in.readInt();
+ for (int i=0; i<N; i++) {
+ final int size = in.readInt();
+ final long[] array = new long[size];
+ readCompactedLongArray(in, array, size);
+ mLongs.add(array);
+ }
+ }
+
+ /**
+ * Write the long array to the parcel in a compacted form. Does not allow negative
+ * values in the array.
+ */
+ private static void writeCompactedLongArray(Parcel out, long[] array, int num) {
+ for (int i=0; i<num; i++) {
+ long val = array[i];
+ if (val < 0) {
+ Slog.w(TAG, "Time val negative: " + val);
+ val = 0;
+ }
+ if (val <= Integer.MAX_VALUE) {
+ out.writeInt((int)val);
+ } else {
+ int top = ~((int)((val>>32)&0x7fffffff));
+ int bottom = (int)(val&0xfffffff);
+ out.writeInt(top);
+ out.writeInt(bottom);
+ }
+ }
+ }
+
+ /**
+ * Read the compacted array into the long[].
+ */
+ private static void readCompactedLongArray(Parcel in, long[] array, int num) {
+ final int alen = array.length;
+ if (num > alen) {
+ logOrThrow("bad array lengths: got " + num + " array is " + alen);
+ return;
+ }
+ int i;
+ for (i=0; i<num; i++) {
+ int val = in.readInt();
+ if (val >= 0) {
+ array[i] = val;
+ } else {
+ int bottom = in.readInt();
+ array[i] = (((long)~val)<<32) | bottom;
+ }
+ }
+ while (i < alen) {
+ array[i] = 0;
+ i++;
+ }
+ }
+
+ /**
+ * Extract the id from a key.
+ */
+ public static byte getIdFromKey(int key) {
+ return (byte)((key >> ID_SHIFT) & ID_MASK);
+ }
+
+ /**
+ * Gets the index of the array in the list of arrays.
+ *
+ * Not to be confused with getIndexFromKey.
+ */
+ public static int getArrayFromKey(int key) {
+ return (key >> ARRAY_SHIFT) & ARRAY_MASK;
+ }
+
+ /**
+ * Gets the index of a value in a long[].
+ *
+ * Not to be confused with getArrayFromKey.
+ */
+ public static int getIndexFromKey(int key) {
+ return (key >> INDEX_SHIFT) & INDEX_MASK;
+ }
+
+ /**
+ * Do a Slog.wtf or throw an exception (thereby crashing the system process if
+ * this is a debug build.)
+ */
+ private static void logOrThrow(String message) {
+ logOrThrow(message, new RuntimeException("Stack trace"));
+ }
+
+ /**
+ * Do a Slog.wtf or throw an exception (thereby crashing the system process if
+ * this is an eng build.)
+ */
+ private static void logOrThrow(String message, Throwable th) {
+ Slog.wtf(TAG, message, th);
+ if (Build.TYPE.equals("eng")) {
+ throw new RuntimeException(message, th);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/app/procstats/SysMemUsageTable.java b/core/java/com/android/internal/app/procstats/SysMemUsageTable.java
new file mode 100644
index 0000000..e71bc55
--- /dev/null
+++ b/core/java/com/android/internal/app/procstats/SysMemUsageTable.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.app.procstats;
+
+import android.util.DebugUtils;
+
+import static com.android.internal.app.procstats.ProcessStats.STATE_COUNT;
+import static com.android.internal.app.procstats.ProcessStats.STATE_NOTHING;
+import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_SAMPLE_COUNT;
+import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_CACHED_MINIMUM;
+import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_CACHED_AVERAGE;
+import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_CACHED_MAXIMUM;
+import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_FREE_MINIMUM;
+import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_FREE_AVERAGE;
+import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_FREE_MAXIMUM;
+import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_ZRAM_MINIMUM;
+import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_ZRAM_AVERAGE;
+import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_ZRAM_MAXIMUM;
+import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_KERNEL_MINIMUM;
+import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_KERNEL_AVERAGE;
+import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_KERNEL_MAXIMUM;
+import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_NATIVE_MINIMUM;
+import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_NATIVE_AVERAGE;
+import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_NATIVE_MAXIMUM;
+import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_COUNT;
+
+import java.io.PrintWriter;
+
+
+/**
+ * Class to accumulate system mem usage data.
+ */
+public class SysMemUsageTable extends SparseMappingTable.Table {
+ /**
+ * Construct the SysMemUsageTable with 'tableData' as backing store
+ * for the longs data.
+ */
+ public SysMemUsageTable(SparseMappingTable tableData) {
+ super(tableData);
+ }
+
+ /**
+ * Merge the stats given into our own values.
+ *
+ * @param that SysMemUsageTable to copy from.
+ */
+ public void mergeStats(SysMemUsageTable that) {
+ final int N = that.getKeyCount();
+ for (int i=0; i<N; i++) {
+ final int key = that.getKeyAt(i);
+
+ final int state = SparseMappingTable.getIdFromKey(key);
+ final long[] addData = that.getArrayForKey(key);
+ final int addOff = SparseMappingTable.getIndexFromKey(key);
+
+ mergeStats(state, addData, addOff);
+ }
+ }
+
+ /**
+ * Merge the stats given into our own values.
+ *
+ * @param state The state
+ * @param addData The data array to copy
+ * @param addOff The index in addOff to start copying from
+ */
+ public void mergeStats(int state, long[] addData, int addOff) {
+ final int key = getOrAddKey((byte)state, SYS_MEM_USAGE_COUNT);
+
+ final long[] dstData = getArrayForKey(key);
+ final int dstOff = SparseMappingTable.getIndexFromKey(key);
+
+ SysMemUsageTable.mergeSysMemUsage(dstData, dstOff, addData, addOff);
+ }
+
+ /**
+ * Return a long[] containing the merge of all of the usage in this table.
+ */
+ public long[] getTotalMemUsage() {
+ long[] total = new long[SYS_MEM_USAGE_COUNT];
+ final int N = getKeyCount();
+ for (int i=0; i<N; i++) {
+ final int key = getKeyAt(i);
+
+ final long[] addData = getArrayForKey(key);
+ final int addOff = SparseMappingTable.getIndexFromKey(key);
+
+ SysMemUsageTable.mergeSysMemUsage(total, 0, addData, addOff);
+ }
+ return total;
+ }
+
+ /**
+ * Merge the stats from one raw long[] into another.
+ *
+ * @param dstData The destination array
+ * @param dstOff The index in the destination array to start from
+ * @param addData The source array
+ * @param addOff The index in the source array to start from
+ */
+ public static void mergeSysMemUsage(long[] dstData, int dstOff,
+ long[] addData, int addOff) {
+ final long dstCount = dstData[dstOff+SYS_MEM_USAGE_SAMPLE_COUNT];
+ final long addCount = addData[addOff+SYS_MEM_USAGE_SAMPLE_COUNT];
+ if (dstCount == 0) {
+ dstData[dstOff+SYS_MEM_USAGE_SAMPLE_COUNT] = addCount;
+ for (int i=SYS_MEM_USAGE_CACHED_MINIMUM; i<SYS_MEM_USAGE_COUNT; i++) {
+ dstData[dstOff+i] = addData[addOff+i];
+ }
+ } else if (addCount > 0) {
+ dstData[dstOff+SYS_MEM_USAGE_SAMPLE_COUNT] = dstCount + addCount;
+ for (int i=SYS_MEM_USAGE_CACHED_MINIMUM; i<SYS_MEM_USAGE_COUNT; i+=3) {
+ if (dstData[dstOff+i] > addData[addOff+i]) {
+ dstData[dstOff+i] = addData[addOff+i];
+ }
+ dstData[dstOff+i+1] = (long)(
+ ((dstData[dstOff+i+1]*(double)dstCount)
+ + (addData[addOff+i+1]*(double)addCount))
+ / (dstCount+addCount) );
+ if (dstData[dstOff+i+2] < addData[addOff+i+2]) {
+ dstData[dstOff+i+2] = addData[addOff+i+2];
+ }
+ }
+ }
+ }
+
+
+ public void dump(PrintWriter pw, String prefix, int[] screenStates, int[] memStates) {
+ int printedScreen = -1;
+ for (int is=0; is<screenStates.length; is++) {
+ int printedMem = -1;
+ for (int im=0; im<memStates.length; im++) {
+ final int iscreen = screenStates[is];
+ final int imem = memStates[im];
+ final int bucket = ((iscreen + imem) * STATE_COUNT);
+ long count = getValueForId((byte)bucket, SYS_MEM_USAGE_SAMPLE_COUNT);
+ if (count > 0) {
+ pw.print(prefix);
+ if (screenStates.length > 1) {
+ DumpUtils.printScreenLabel(pw, printedScreen != iscreen
+ ? iscreen : STATE_NOTHING);
+ printedScreen = iscreen;
+ }
+ if (memStates.length > 1) {
+ DumpUtils.printMemLabel(pw,
+ printedMem != imem ? imem : STATE_NOTHING, '\0');
+ printedMem = imem;
+ }
+ pw.print(": ");
+ pw.print(count);
+ pw.println(" samples:");
+ dumpCategory(pw, prefix, " Cached", bucket, SYS_MEM_USAGE_CACHED_MINIMUM);
+ dumpCategory(pw, prefix, " Free", bucket, SYS_MEM_USAGE_FREE_MINIMUM);
+ dumpCategory(pw, prefix, " ZRam", bucket, SYS_MEM_USAGE_ZRAM_MINIMUM);
+ dumpCategory(pw, prefix, " Kernel", bucket, SYS_MEM_USAGE_KERNEL_MINIMUM);
+ dumpCategory(pw, prefix, " Native", bucket, SYS_MEM_USAGE_NATIVE_MINIMUM);
+ }
+ }
+ }
+ }
+
+ private void dumpCategory(PrintWriter pw, String prefix, String label, int bucket, int index) {
+ pw.print(prefix); pw.print(label);
+ pw.print(": ");
+ DebugUtils.printSizeValue(pw, getValueForId((byte)bucket, index) * 1024);
+ pw.print(" min, ");
+ DebugUtils.printSizeValue(pw, getValueForId((byte)bucket, index + 1) * 1024);
+ pw.print(" avg, ");
+ DebugUtils.printSizeValue(pw, getValueForId((byte)bucket, index+2) * 1024);
+ pw.println(" max");
+ }
+
+}
+
+
diff --git a/core/java/com/android/internal/app/procstats/package.html b/core/java/com/android/internal/app/procstats/package.html
new file mode 100644
index 0000000..db6f78b
--- /dev/null
+++ b/core/java/com/android/internal/app/procstats/package.html
@@ -0,0 +1,3 @@
+<body>
+{@hide}
+</body>
\ No newline at end of file
diff --git a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
index 5a195cb..4260e50 100644
--- a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
+++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
@@ -33,8 +33,8 @@
//
// for AppWidgetHost
//
- int[] startListening(IAppWidgetHost host, String callingPackage, int hostId,
- out List<RemoteViews> updatedViews);
+ ParceledListSlice startListening(IAppWidgetHost host, String callingPackage, int hostId,
+ in int[] appWidgetIds, out int[] updatedIds);
void stopListening(String callingPackage, int hostId);
int allocateAppWidgetId(String callingPackage, int hostId);
void deleteAppWidgetId(String callingPackage, int appWidgetId);
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index 10027b6..5e8f4a2 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -27,7 +27,6 @@
import android.content.pm.PackageInfo;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
-import android.os.SELinux;
import android.system.ErrnoException;
import android.system.Os;
import android.system.StructStat;
diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java
index c0dce22..7bd64e5 100644
--- a/core/java/com/android/internal/content/PackageMonitor.java
+++ b/core/java/com/android/internal/content/PackageMonitor.java
@@ -26,6 +26,7 @@
import android.os.UserHandle;
import android.util.Slog;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.Preconditions;
import java.util.HashSet;
@@ -72,15 +73,17 @@
public void register(Context context, Looper thread, UserHandle user,
boolean externalStorage) {
+ register(context, user, externalStorage,
+ (thread == null) ? BackgroundThread.getHandler() : new Handler(thread));
+ }
+
+ public void register(Context context, UserHandle user,
+ boolean externalStorage, Handler handler) {
if (mRegisteredContext != null) {
throw new IllegalStateException("Already registered");
}
mRegisteredContext = context;
- if (thread == null) {
- mRegisteredHandler = BackgroundThread.getHandler();
- } else {
- mRegisteredHandler = new Handler(thread);
- }
+ mRegisteredHandler = Preconditions.checkNotNull(handler);
if (user != null) {
context.registerReceiverAsUser(this, user, sPackageFilt, null, mRegisteredHandler);
context.registerReceiverAsUser(this, user, sNonDataFilt, null, mRegisteredHandler);
diff --git a/core/java/com/android/internal/os/BackgroundThread.java b/core/java/com/android/internal/os/BackgroundThread.java
index d6f7b20..cffba01 100644
--- a/core/java/com/android/internal/os/BackgroundThread.java
+++ b/core/java/com/android/internal/os/BackgroundThread.java
@@ -18,6 +18,7 @@
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.Trace;
/**
* Shared singleton background thread for each process.
@@ -34,6 +35,7 @@
if (sInstance == null) {
sInstance = new BackgroundThread();
sInstance.start();
+ sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_ACTIVITY_MANAGER);
sHandler = new Handler(sInstance.getLooper());
}
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index b0ef012..bbefcb5 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -4012,6 +4012,7 @@
if (DEBUG_HISTORY) Slog.v(TAG, "BLE scan started for: "
+ Integer.toHexString(mHistoryCur.states2));
addHistoryRecordLocked(elapsedRealtime, uptime);
+ mBluetoothScanTimer.startRunningLocked(elapsedRealtime);
}
mBluetoothScanNesting++;
getUidStatsLocked(uid).noteBluetoothScanStartedLocked(elapsedRealtime);
@@ -4034,6 +4035,7 @@
if (DEBUG_HISTORY) Slog.v(TAG, "BLE scan stopped for: "
+ Integer.toHexString(mHistoryCur.states2));
addHistoryRecordLocked(elapsedRealtime, uptime);
+ mBluetoothScanTimer.stopRunningLocked(elapsedRealtime);
}
getUidStatsLocked(uid).noteBluetoothScanStoppedLocked(elapsedRealtime);
}
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index 14ebe22..79138b7 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -85,6 +85,7 @@
public static final String POWER_WIFI_CONTROLLER_IDLE = "wifi.controller.idle";
public static final String POWER_WIFI_CONTROLLER_RX = "wifi.controller.rx";
public static final String POWER_WIFI_CONTROLLER_TX = "wifi.controller.tx";
+ public static final String POWER_WIFI_CONTROLLER_TX_LEVELS = "wifi.controller.tx_levels";
public static final String POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE = "wifi.controller.voltage";
public static final String POWER_BLUETOOTH_CONTROLLER_IDLE = "bluetooth.controller.idle";
@@ -287,9 +288,15 @@
};
for (int i = 0; i < configResIds.length; i++) {
+ String key = configResIdKeys[i];
+ // if we already have some of these parameters in power_profile.xml, ignore the
+ // value in config.xml
+ if ((sPowerMap.containsKey(key) && (Double) sPowerMap.get(key) > 0)) {
+ continue;
+ }
int value = resources.getInteger(configResIds[i]);
if (value > 0) {
- sPowerMap.put(configResIdKeys[i], (double) value);
+ sPowerMap.put(key, (double) value);
}
}
}
diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
index 6931193..738aaca 100644
--- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java
+++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
@@ -16,6 +16,8 @@
package com.android.internal.policy;
+import static android.view.WindowCallbacks.RESIZE_MODE_FREEFORM;
+
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
@@ -69,6 +71,7 @@
private ColorDrawable mNavigationBarColor;
private boolean mOldFullscreen;
private boolean mFullscreen;
+ private final int mResizeMode;
private final Rect mOldSystemInsets = new Rect();
private final Rect mOldStableInsets = new Rect();
private final Rect mSystemInsets = new Rect();
@@ -77,7 +80,7 @@
public BackdropFrameRenderer(DecorView decorView, ThreadedRenderer renderer, Rect initialBounds,
Drawable resizingBackgroundDrawable, Drawable captionBackgroundDrawable,
Drawable userCaptionBackgroundDrawable, int statusBarColor, int navigationBarColor,
- boolean fullscreen, Rect systemInsets, Rect stableInsets) {
+ boolean fullscreen, Rect systemInsets, Rect stableInsets, int resizeMode) {
setName("ResizeFrame");
mRenderer = renderer;
@@ -98,6 +101,7 @@
mStableInsets.set(stableInsets);
mOldSystemInsets.set(systemInsets);
mOldStableInsets.set(stableInsets);
+ mResizeMode = resizeMode;
synchronized (this) {
redrawLocked(initialBounds, fullscreen, mSystemInsets, mStableInsets);
}
@@ -266,11 +270,16 @@
mLastXOffset = xOffset;
mLastYOffset = yOffset;
- mRenderer.setContentDrawBounds(
- mLastXOffset,
- mLastYOffset,
- mLastXOffset + mLastContentWidth,
- mLastYOffset + mLastCaptionHeight + mLastContentHeight);
+ // Only clip the content to the bounds if we are not fullscreen. In the other case, we
+ // actually need to draw outside these.
+ if (mResizeMode == RESIZE_MODE_FREEFORM) {
+ mRenderer.setContentDrawBounds(
+ mLastXOffset,
+ mLastYOffset,
+ mLastXOffset + mLastContentWidth,
+ mLastYOffset + mLastCaptionHeight + mLastContentHeight);
+ }
+
// If this was the first call and redrawLocked got already called prior
// to us, we should re-issue a redrawLocked now.
return firstCall
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index bdf2a21..ea0fbda 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -39,8 +39,11 @@
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.RemoteException;
@@ -49,6 +52,7 @@
import android.util.TypedValue;
import android.view.ActionMode;
import android.view.ContextThemeWrapper;
+import android.view.DisplayListCanvas;
import android.view.Gravity;
import android.view.InputQueue;
import android.view.KeyEvent;
@@ -88,6 +92,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -178,6 +183,7 @@
private boolean mLastHasBottomStableInset = false;
private boolean mLastHasRightStableInset = false;
private int mLastWindowFlags = 0;
+ private boolean mLastShouldAlwaysConsumeNavBar = false;
private int mRootScrollY = 0;
@@ -203,6 +209,7 @@
private Drawable mResizingBackgroundDrawable;
private Drawable mCaptionBackgroundDrawable;
private Drawable mUserCaptionBackgroundDrawable;
+ private Drawable mOriginalBackgroundDrawable;
private float mAvailableWidth;
@@ -211,6 +218,11 @@
private boolean mApplyFloatingVerticalInsets = false;
private boolean mApplyFloatingHorizontalInsets = false;
+ private int mResizeMode = RESIZE_MODE_INVALID;
+ private final int mResizeShadowSize;
+ private final Paint mVerticalResizeShadowPaint = new Paint();
+ private final Paint mHorizontalResizeShadowPaint = new Paint();
+
DecorView(Context context, int featureId, PhoneWindow window,
WindowManager.LayoutParams params) {
super(context);
@@ -233,6 +245,10 @@
setWindow(window);
updateLogTag(params);
+
+ mResizeShadowSize = context.getResources().getDimensionPixelSize(
+ R.dimen.resize_shadow_size);
+ initResizingPaints();
}
void setBackgroundFallback(int resId) {
@@ -699,6 +715,10 @@
// our shadow elevation.
updateElevation();
mAllowUpdateElevation = true;
+
+ if (changed && mResizeMode == RESIZE_MODE_DOCKED_DIVIDER) {
+ getViewRootImpl().requestInvalidateRootRenderNode();
+ }
}
@Override
@@ -869,6 +889,11 @@
mBackgroundPadding.setEmpty();
}
drawableChanged();
+
+ // Make sure we don't reset to the old drawable when finishing resizing.
+ if (mResizeMode != RESIZE_MODE_INVALID) {
+ mOriginalBackgroundDrawable = null;
+ }
}
}
@@ -978,6 +1003,7 @@
boolean hasRightStableInset = insets.getStableInsetRight() != 0;
disallowAnimate |= (hasRightStableInset != mLastHasRightStableInset);
mLastHasRightStableInset = hasRightStableInset;
+ mLastShouldAlwaysConsumeNavBar = insets.shouldAlwaysConsumeNavBar();
}
boolean navBarToRightEdge = isNavBarToRightEdge(mLastBottomInset, mLastRightInset);
@@ -998,12 +1024,11 @@
// When we expand the window with FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, we still need
// to ensure that the rest of the view hierarchy doesn't notice it, unless they've
// explicitly asked for it.
-
boolean consumingNavBar =
(attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
&& (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0
&& (sysUiVisibility & SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0
- || (insets != null && insets.shouldAlwaysConsumeNavBar());
+ || mLastShouldAlwaysConsumeNavBar;
// If we didn't request fullscreen layout, but we still got it because of the
// mForceWindowDrawsStatusBarBackground flag, also consume top inset.
@@ -1907,7 +1932,7 @@
@Override
public void onWindowDragResizeStart(Rect initialBounds, boolean fullscreen, Rect systemInsets,
- Rect stableInsets) {
+ Rect stableInsets, int resizeMode) {
if (mWindow.isDestroyed()) {
// If the owner's window is gone, we should not be able to come here anymore.
releaseThreadedRenderer();
@@ -1923,7 +1948,7 @@
initialBounds, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
getCurrentColor(mNavigationColorViewState), fullscreen, systemInsets,
- stableInsets);
+ stableInsets, resizeMode);
// Get rid of the shadow while we are resizing. Shadow drawing takes considerable time.
// If we want to get the shadow shown while resizing, we would need to elevate a new
@@ -1931,13 +1956,24 @@
updateElevation();
updateColorViews(null /* insets */, false);
+
+ mOriginalBackgroundDrawable = getBackground();
+ setBackgroundDrawable(null);
}
+ mResizeMode = resizeMode;
+ getViewRootImpl().requestInvalidateRootRenderNode();
}
@Override
public void onWindowDragResizeEnd() {
releaseThreadedRenderer();
updateColorViews(null /* insets */, false);
+ mResizeMode = RESIZE_MODE_INVALID;
+ getViewRootImpl().requestInvalidateRootRenderNode();
+ if (mOriginalBackgroundDrawable != null) {
+ setBackgroundDrawable(mOriginalBackgroundDrawable);
+ mOriginalBackgroundDrawable = null;
+ }
}
@Override
@@ -1960,6 +1996,41 @@
}
}
+ @Override
+ public void onPostDraw(DisplayListCanvas canvas) {
+ drawResizingShadowIfNeeded(canvas);
+ }
+
+ private void initResizingPaints() {
+ final int startColor = mContext.getResources().getColor(
+ R.color.resize_shadow_start_color, null);
+ final int endColor = mContext.getResources().getColor(
+ R.color.resize_shadow_end_color, null);
+ final int middleColor = (startColor + endColor) / 2;
+ mHorizontalResizeShadowPaint.setShader(new LinearGradient(
+ 0, 0, 0, mResizeShadowSize, new int[] { startColor, middleColor, endColor },
+ new float[] { 0f, 0.3f, 1f }, Shader.TileMode.CLAMP));
+ mVerticalResizeShadowPaint.setShader(new LinearGradient(
+ 0, 0, mResizeShadowSize, 0, new int[] { startColor, middleColor, endColor },
+ new float[] { 0f, 0.3f, 1f }, Shader.TileMode.CLAMP));
+ }
+
+ private void drawResizingShadowIfNeeded(DisplayListCanvas canvas) {
+ if (mResizeMode != RESIZE_MODE_DOCKED_DIVIDER || mWindow.mIsFloating
+ || mWindow.isTranslucent()
+ || (mWindow.getAttributes().flags & FLAG_SHOW_WALLPAPER) != 0) {
+ return;
+ }
+ canvas.save();
+ canvas.translate(0, getHeight() - mFrameOffsets.bottom);
+ canvas.drawRect(0, 0, getWidth(), mResizeShadowSize, mHorizontalResizeShadowPaint);
+ canvas.restore();
+ canvas.save();
+ canvas.translate(getWidth() - mFrameOffsets.right, 0);
+ canvas.drawRect(0, 0, mResizeShadowSize, getHeight(), mVerticalResizeShadowPaint);
+ canvas.restore();
+ }
+
/** Release the renderer thread which is usually done when the user stops resizing. */
private void releaseThreadedRenderer() {
if (mResizingBackgroundDrawable != null && mLastBackgroundDrawableCb != null) {
diff --git a/core/java/com/android/internal/policy/IKeyguardService.aidl b/core/java/com/android/internal/policy/IKeyguardService.aidl
index e330de2..171a264 100644
--- a/core/java/com/android/internal/policy/IKeyguardService.aidl
+++ b/core/java/com/android/internal/policy/IKeyguardService.aidl
@@ -50,9 +50,12 @@
* Called when the device has finished going to sleep.
*
* @param why {@link #OFF_BECAUSE_OF_USER}, {@link #OFF_BECAUSE_OF_ADMIN},
- * or {@link #OFF_BECAUSE_OF_TIMEOUT}.
+ * or {@link #OFF_BECAUSE_OF_TIMEOUT}.
+ * @param cameraGestureTriggered whether the camera gesture was triggered between
+ * {@link #onStartedGoingToSleep} and this method; if it's been
+ * triggered, we shouldn't lock the device.
*/
- void onFinishedGoingToSleep(int reason);
+ void onFinishedGoingToSleep(int reason, boolean cameraGestureTriggered);
/**
* Called when the device has started waking up.
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 7637eec..0f257d7 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -24,6 +24,7 @@
import android.app.SearchManager;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.view.ContextThemeWrapper;
import android.view.Gravity;
import android.view.IRotationWatcher.Stub;
@@ -157,6 +158,7 @@
InputQueue.Callback mTakeInputQueueCallback;
boolean mIsFloating;
+ private boolean mIsTranslucent;
private LayoutInflater mLayoutInflater;
@@ -490,6 +492,10 @@
return mIsFloating;
}
+ public boolean isTranslucent() {
+ return mIsTranslucent;
+ }
+
/**
* Return a LayoutInflater instance that can be used to inflate XML view layout
* resources for use in this Window.
@@ -509,6 +515,11 @@
mDecorContentParent.setWindowTitle(title);
}
mTitle = title;
+ WindowManager.LayoutParams params = getAttributes();
+ if (!TextUtils.equals(title, params.getTitle())) {
+ params.setTitle(title);
+ dispatchWindowAttributesChanged(getAttributes());
+ }
}
@Override
@@ -675,7 +686,7 @@
}
@Override
- public void onMultiWindowChanged() {
+ public void onMultiWindowModeChanged() {
if (mDecor != null) {
mDecor.onConfigurationChanged(getContext().getResources().getConfiguration());
}
@@ -2400,6 +2411,8 @@
requestFeature(FEATURE_ACTIVITY_TRANSITIONS);
}
+ mIsTranslucent = a.getBoolean(R.styleable.Window_windowIsTranslucent, false);
+
final Context context = getContext();
final int targetSdk = context.getApplicationInfo().targetSdkVersion;
final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB;
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index b243cac..9e5c238 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -88,6 +88,11 @@
*/
void appTransitionStarting(long statusBarAnimationsStartTime, long statusBarAnimationsDuration);
+ /**
+ * Notifies the status bar that an app transition is done.
+ */
+ void appTransitionFinished();
+
void showAssistDisclosure();
void startAssist(in Bundle args);
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index ee73b90..bed5a2e 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -238,6 +238,14 @@
return total;
}
+ public static int[] convertToIntArray(List<Integer> list) {
+ int[] array = new int[list.size()];
+ for (int i = 0; i < list.size(); i++) {
+ array[i] = list.get(i);
+ }
+ return array;
+ }
+
/**
* Adds value to given array if not already present, providing set-like
* behavior.
diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java
index bd0e6ce..d8be9fd 100644
--- a/core/java/com/android/internal/util/AsyncChannel.java
+++ b/core/java/com/android/internal/util/AsyncChannel.java
@@ -402,7 +402,7 @@
// Initialize destination fields
mDstMessenger = dstMessenger;
-
+ linkToDeathMonitor();
if (DBG) log("connected srcHandler to the dstMessenger X");
}
@@ -844,22 +844,30 @@
msg.arg1 = status;
msg.obj = this;
msg.replyTo = mDstMessenger;
+ if (!linkToDeathMonitor()) {
+ // Override status to indicate failure
+ msg.arg1 = STATUS_BINDING_UNSUCCESSFUL;
+ }
- /*
- * Link to death only when bindService isn't used.
- */
- if (mConnection == null) {
+ mSrcHandler.sendMessage(msg);
+ }
+
+ /**
+ * Link to death monitor for destination messenger. Returns true if successfully binded to
+ * destination messenger; false otherwise.
+ */
+ private boolean linkToDeathMonitor() {
+ // Link to death only when bindService isn't used and not already linked.
+ if (mConnection == null && mDeathMonitor == null) {
mDeathMonitor = new DeathMonitor();
try {
mDstMessenger.getBinder().linkToDeath(mDeathMonitor, 0);
} catch (RemoteException e) {
mDeathMonitor = null;
- // Override status to indicate failure
- msg.arg1 = STATUS_BINDING_UNSUCCESSFUL;
+ return false;
}
}
-
- mSrcHandler.sendMessage(msg);
+ return true;
}
/**
diff --git a/core/java/com/android/internal/util/FastXmlSerializer.java b/core/java/com/android/internal/util/FastXmlSerializer.java
index 7a04080..3c1d2d6 100644
--- a/core/java/com/android/internal/util/FastXmlSerializer.java
+++ b/core/java/com/android/internal/util/FastXmlSerializer.java
@@ -28,6 +28,7 @@
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;
+import java.nio.charset.CodingErrorAction;
import java.nio.charset.IllegalCharsetNameException;
import java.nio.charset.UnsupportedCharsetException;
@@ -38,14 +39,14 @@
*/
public class FastXmlSerializer implements XmlSerializer {
private static final String ESCAPE_TABLE[] = new String[] {
- null, null, null, null, null, null, null, null, // 0-7
- null, null, null, null, null, null, null, null, // 8-15
- null, null, null, null, null, null, null, null, // 16-23
- null, null, null, null, null, null, null, null, // 24-31
- null, null, """, null, null, null, "&", null, // 32-39
- null, null, null, null, null, null, null, null, // 40-47
- null, null, null, null, null, null, null, null, // 48-55
- null, null, null, null, "<", null, ">", null, // 56-63
+ "�", "", "", "", "", "", "", "", // 0-7
+ "", "	", " ", "", "", " ", "", "", // 8-15
+ "", "", "", "", "", "", "", "", // 16-23
+ "", "", "", "", "", "", "", "", // 24-31
+ null, null, """, null, null, null, "&", null, // 32-39
+ null, null, null, null, null, null, null, null, // 40-47
+ null, null, null, null, null, null, null, null, // 48-55
+ null, null, null, null, "<", null, ">", null, // 56-63
};
private static final int BUFFER_LEN = 8192;
@@ -310,7 +311,9 @@
throw new IllegalArgumentException();
if (true) {
try {
- mCharset = Charset.forName(encoding).newEncoder();
+ mCharset = Charset.forName(encoding).newEncoder()
+ .onMalformedInput(CodingErrorAction.REPLACE)
+ .onUnmappableCharacter(CodingErrorAction.REPLACE);
} catch (IllegalCharsetNameException e) {
throw (UnsupportedEncodingException) (new UnsupportedEncodingException(
encoding).initCause(e));
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
index 53cb56e..1ead5b3 100644
--- a/core/java/com/android/internal/util/Preconditions.java
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -35,6 +35,20 @@
}
/**
+ * Ensures that an expression checking an argument is true.
+ *
+ * @param expression the expression to check
+ * @param errorMessage the exception message to use if the check fails; will
+ * be converted to a string using {@link String#valueOf(Object)}
+ * @throws IllegalArgumentException if {@code expression} is false
+ */
+ public static void checkArgument(boolean expression, final Object errorMessage) {
+ if (!expression) {
+ throw new IllegalArgumentException(String.valueOf(errorMessage));
+ }
+ }
+
+ /**
* Ensures that an string reference passed as a parameter to the calling
* method is not empty.
*
@@ -42,7 +56,7 @@
* @return the string reference that was validated
* @throws IllegalArgumentException if {@code string} is empty
*/
- public static @NonNull String checkStringNotEmpty(final String string) {
+ public static @NonNull <T extends CharSequence> T checkStringNotEmpty(final T string) {
if (TextUtils.isEmpty(string)) {
throw new IllegalArgumentException();
}
@@ -59,7 +73,7 @@
* @return the string reference that was validated
* @throws IllegalArgumentException if {@code string} is empty
*/
- public static @NonNull String checkStringNotEmpty(final String string,
+ public static @NonNull <T extends CharSequence> T checkStringNotEmpty(final T string,
final Object errorMessage) {
if (TextUtils.isEmpty(string)) {
throw new IllegalArgumentException(String.valueOf(errorMessage));
@@ -127,13 +141,17 @@
/**
* Check the requested flags, throwing if any requested flags are outside
* the allowed set.
+ *
+ * @return the validated requested flags.
*/
- public static void checkFlagsArgument(final int requestedFlags, final int allowedFlags) {
+ public static int checkFlagsArgument(final int requestedFlags, final int allowedFlags) {
if ((requestedFlags & allowedFlags) != requestedFlags) {
throw new IllegalArgumentException("Requested flags 0x"
+ Integer.toHexString(requestedFlags) + ", but only 0x"
+ Integer.toHexString(allowedFlags) + " are allowed");
}
+
+ return requestedFlags;
}
/**
@@ -156,6 +174,37 @@
/**
* Ensures that that the argument numeric value is non-negative.
*
+ * @param value a numeric int value
+ *
+ * @return the validated numeric value
+ * @throws IllegalArgumentException if {@code value} was negative
+ */
+ public static @IntRange(from = 0) int checkArgumentNonnegative(final int value) {
+ if (value < 0) {
+ throw new IllegalArgumentException();
+ }
+
+ return value;
+ }
+
+ /**
+ * Ensures that that the argument numeric value is non-negative.
+ *
+ * @param value a numeric long value
+ * @return the validated numeric value
+ * @throws IllegalArgumentException if {@code value} was negative
+ */
+ public static long checkArgumentNonnegative(final long value) {
+ if (value < 0) {
+ throw new IllegalArgumentException();
+ }
+
+ return value;
+ }
+
+ /**
+ * Ensures that that the argument numeric value is non-negative.
+ *
* @param value a numeric long value
* @param errorMessage the exception message to use if the check fails
* @return the validated numeric value
diff --git a/core/java/com/android/internal/util/ProgressReporter.java b/core/java/com/android/internal/util/ProgressReporter.java
new file mode 100644
index 0000000..796f8ac
--- /dev/null
+++ b/core/java/com/android/internal/util/ProgressReporter.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IProgressListener;
+import android.os.RemoteException;
+import android.util.MathUtils;
+
+/**
+ * Tracks and reports progress of a single task to a {@link IProgressListener}.
+ * The reported progress of a task ranges from 0-100, but the task can be
+ * segmented into smaller pieces using {@link #startSegment(int)} and
+ * {@link #endSegment(int[])}, and segments can be nested.
+ * <p>
+ * Here's an example in action; when finished the overall task progress will be
+ * at 60.
+ *
+ * <pre>
+ * prog.setProgress(20);
+ * {
+ * final int restore = prog.startSegment(40);
+ * for (int i = 0; i < N; i++) {
+ * prog.setProgress(i, N);
+ * ...
+ * }
+ * prog.endSegment(restore);
+ * }
+ * </pre>
+ *
+ * This class is not thread safe.
+ *
+ * @hide
+ */
+public class ProgressReporter {
+ public static final ProgressReporter NO_OP = new ProgressReporter(0, null);
+
+ private final int mId;
+ private final IProgressListener mListener;
+
+ private Bundle mExtras = new Bundle();
+
+ private int mProgress = 0;
+
+ /**
+ * Current segment range: first element is starting progress of this
+ * segment, second element is length of segment.
+ */
+ private int[] mSegmentRange = new int[] { 0, 100 };
+
+ /**
+ * Create a new task with the given identifier whose progress will be
+ * reported to the given listener.
+ */
+ public ProgressReporter(int id, @Nullable IProgressListener listener) {
+ mId = id;
+ mListener = listener;
+ }
+
+ /**
+ * Set the progress of the currently active segment.
+ *
+ * @param progress Segment progress between 0-100.
+ */
+ public void setProgress(int progress) {
+ setProgress(progress, 100, null);
+ }
+
+ /**
+ * Set the progress of the currently active segment.
+ *
+ * @param progress Segment progress between 0-100.
+ */
+ public void setProgress(int progress, @Nullable CharSequence title) {
+ setProgress(progress, 100, title);
+ }
+
+ /**
+ * Set the fractional progress of the currently active segment.
+ */
+ public void setProgress(int n, int m) {
+ setProgress(n, m, null);
+ }
+
+ /**
+ * Set the fractional progress of the currently active segment.
+ */
+ public void setProgress(int n, int m, @Nullable CharSequence title) {
+ mProgress = mSegmentRange[0]
+ + MathUtils.constrain((n * mSegmentRange[1]) / m, 0, mSegmentRange[1]);
+ if (title != null) {
+ mExtras.putCharSequence(Intent.EXTRA_TITLE, title);
+ }
+ notifyProgress(mId, mProgress, mExtras);
+ }
+
+ /**
+ * Start a new inner segment that will contribute the given range towards
+ * the currently active segment. You must pass the returned value to
+ * {@link #endSegment(int[])} when finished.
+ */
+ public int[] startSegment(int size) {
+ final int[] lastRange = mSegmentRange;
+ mSegmentRange = new int[] { mProgress, (size * mSegmentRange[1] / 100) };
+ return lastRange;
+ }
+
+ /**
+ * End the current segment.
+ */
+ public void endSegment(int[] lastRange) {
+ mProgress = mSegmentRange[0] + mSegmentRange[1];
+ mSegmentRange = lastRange;
+ }
+
+ int getProgress() {
+ return mProgress;
+ }
+
+ int[] getSegmentRange() {
+ return mSegmentRange;
+ }
+
+ /**
+ * Report this entire task as being finished.
+ */
+ public void finish() {
+ notifyFinished(mId, null);
+ }
+
+ private void notifyProgress(int id, int progress, Bundle extras) {
+ if (mListener != null) {
+ try {
+ mListener.onProgress(id, progress, extras);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ public void notifyFinished(int id, Bundle extras) {
+ if (mListener != null) {
+ try {
+ mListener.onFinished(id, extras);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+}
diff --git a/core/java/com/android/internal/util/ScreenShapeHelper.java b/core/java/com/android/internal/util/ScreenShapeHelper.java
index 4a196f8..5f390be 100644
--- a/core/java/com/android/internal/util/ScreenShapeHelper.java
+++ b/core/java/com/android/internal/util/ScreenShapeHelper.java
@@ -1,27 +1,20 @@
package com.android.internal.util;
import android.content.res.Resources;
-import android.content.res.TypedArray;
import android.os.Build;
import android.os.SystemProperties;
-import android.util.DisplayMetrics;
-import android.util.TypedValue;
import android.view.ViewRootImpl;
-import com.android.internal.R;
-
/**
* @hide
*/
public class ScreenShapeHelper {
- private static final boolean IS_EMULATOR = Build.HARDWARE.contains("goldfish");
-
/**
* Return the bottom pixel window outset of a window given its style attributes.
* @return An outset dimension in pixels or 0 if no outset should be applied.
*/
public static int getWindowOutsetBottomPx(Resources resources) {
- if (IS_EMULATOR) {
+ if (Build.IS_EMULATOR) {
return SystemProperties.getInt(ViewRootImpl.PROPERTY_EMULATOR_WIN_OUTSET_BOTTOM_PX, 0);
} else {
return resources.getInteger(com.android.internal.R.integer.config_windowOutsetBottom);
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index 02b9db3..3a4afad 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -27,13 +27,12 @@
import android.os.RemoteException;
import android.util.Log;
import android.view.KeyEvent;
-import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
-
-import java.lang.ref.WeakReference;
+import android.view.inputmethod.InputConnectionInspector;
+import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
public abstract class IInputConnectionWrapper extends IInputContext.Stub {
static final String TAG = "IInputConnectionWrapper";
@@ -61,10 +60,11 @@
private static final int DO_PERFORM_PRIVATE_COMMAND = 120;
private static final int DO_CLEAR_META_KEY_STATES = 130;
private static final int DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO = 140;
- private static final int DO_REPORT_FINISH = 150;
+ private static final int DO_CLOSE_CONNECTION = 150;
- @NonNull
- private final WeakReference<InputConnection> mInputConnection;
+ @GuardedBy("mLock")
+ @Nullable
+ private InputConnection mInputConnection;
private Looper mMainLooper;
private Handler mH;
@@ -90,15 +90,17 @@
}
}
- public IInputConnectionWrapper(Looper mainLooper, InputConnection conn) {
- mInputConnection = new WeakReference<>(conn);
+ public IInputConnectionWrapper(Looper mainLooper, @NonNull InputConnection inputConnection) {
+ mInputConnection = inputConnection;
mMainLooper = mainLooper;
mH = new MyHandler(mMainLooper);
}
@Nullable
public InputConnection getInputConnection() {
- return mInputConnection.get();
+ synchronized (mLock) {
+ return mInputConnection;
+ }
}
protected boolean isFinished() {
@@ -219,8 +221,8 @@
seq, callback));
}
- public void reportFinish() {
- dispatchMessage(obtainMessage(DO_REPORT_FINISH));
+ public void closeConnection() {
+ dispatchMessage(obtainMessage(DO_CLOSE_CONNECTION));
}
void dispatchMessage(Message msg) {
@@ -241,7 +243,7 @@
case DO_GET_TEXT_AFTER_CURSOR: {
SomeArgs args = (SomeArgs)msg.obj;
try {
- InputConnection ic = mInputConnection.get();
+ InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "getTextAfterCursor on inactive InputConnection");
args.callback.setTextAfterCursor(null, args.seq);
@@ -257,7 +259,7 @@
case DO_GET_TEXT_BEFORE_CURSOR: {
SomeArgs args = (SomeArgs)msg.obj;
try {
- InputConnection ic = mInputConnection.get();
+ InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "getTextBeforeCursor on inactive InputConnection");
args.callback.setTextBeforeCursor(null, args.seq);
@@ -273,7 +275,7 @@
case DO_GET_SELECTED_TEXT: {
SomeArgs args = (SomeArgs)msg.obj;
try {
- InputConnection ic = mInputConnection.get();
+ InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "getSelectedText on inactive InputConnection");
args.callback.setSelectedText(null, args.seq);
@@ -289,7 +291,7 @@
case DO_GET_CURSOR_CAPS_MODE: {
SomeArgs args = (SomeArgs)msg.obj;
try {
- InputConnection ic = mInputConnection.get();
+ InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "getCursorCapsMode on inactive InputConnection");
args.callback.setCursorCapsMode(0, args.seq);
@@ -305,7 +307,7 @@
case DO_GET_EXTRACTED_TEXT: {
SomeArgs args = (SomeArgs)msg.obj;
try {
- InputConnection ic = mInputConnection.get();
+ InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "getExtractedText on inactive InputConnection");
args.callback.setExtractedText(null, args.seq);
@@ -319,7 +321,7 @@
return;
}
case DO_COMMIT_TEXT: {
- InputConnection ic = mInputConnection.get();
+ InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "commitText on inactive InputConnection");
return;
@@ -329,7 +331,7 @@
return;
}
case DO_SET_SELECTION: {
- InputConnection ic = mInputConnection.get();
+ InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "setSelection on inactive InputConnection");
return;
@@ -338,7 +340,7 @@
return;
}
case DO_PERFORM_EDITOR_ACTION: {
- InputConnection ic = mInputConnection.get();
+ InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "performEditorAction on inactive InputConnection");
return;
@@ -347,7 +349,7 @@
return;
}
case DO_PERFORM_CONTEXT_MENU_ACTION: {
- InputConnection ic = mInputConnection.get();
+ InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "performContextMenuAction on inactive InputConnection");
return;
@@ -356,7 +358,7 @@
return;
}
case DO_COMMIT_COMPLETION: {
- InputConnection ic = mInputConnection.get();
+ InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "commitCompletion on inactive InputConnection");
return;
@@ -365,7 +367,7 @@
return;
}
case DO_COMMIT_CORRECTION: {
- InputConnection ic = mInputConnection.get();
+ InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "commitCorrection on inactive InputConnection");
return;
@@ -374,7 +376,7 @@
return;
}
case DO_SET_COMPOSING_TEXT: {
- InputConnection ic = mInputConnection.get();
+ InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "setComposingText on inactive InputConnection");
return;
@@ -384,7 +386,7 @@
return;
}
case DO_SET_COMPOSING_REGION: {
- InputConnection ic = mInputConnection.get();
+ InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "setComposingRegion on inactive InputConnection");
return;
@@ -393,7 +395,7 @@
return;
}
case DO_FINISH_COMPOSING_TEXT: {
- InputConnection ic = mInputConnection.get();
+ InputConnection ic = getInputConnection();
// Note we do NOT check isActive() here, because this is safe
// for an IME to call at any time, and we need to allow it
// through to clean up our state after the IME has switched to
@@ -406,7 +408,7 @@
return;
}
case DO_SEND_KEY_EVENT: {
- InputConnection ic = mInputConnection.get();
+ InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "sendKeyEvent on inactive InputConnection");
return;
@@ -416,7 +418,7 @@
return;
}
case DO_CLEAR_META_KEY_STATES: {
- InputConnection ic = mInputConnection.get();
+ InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "clearMetaKeyStates on inactive InputConnection");
return;
@@ -425,7 +427,7 @@
return;
}
case DO_DELETE_SURROUNDING_TEXT: {
- InputConnection ic = mInputConnection.get();
+ InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "deleteSurroundingText on inactive InputConnection");
return;
@@ -434,7 +436,7 @@
return;
}
case DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS: {
- InputConnection ic = mInputConnection.get();
+ InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "deleteSurroundingTextInCodePoints on inactive InputConnection");
return;
@@ -443,7 +445,7 @@
return;
}
case DO_BEGIN_BATCH_EDIT: {
- InputConnection ic = mInputConnection.get();
+ InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "beginBatchEdit on inactive InputConnection");
return;
@@ -452,7 +454,7 @@
return;
}
case DO_END_BATCH_EDIT: {
- InputConnection ic = mInputConnection.get();
+ InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "endBatchEdit on inactive InputConnection");
return;
@@ -461,7 +463,7 @@
return;
}
case DO_REPORT_FULLSCREEN_MODE: {
- InputConnection ic = mInputConnection.get();
+ InputConnection ic = getInputConnection();
if (ic == null) {
Log.w(TAG, "reportFullscreenMode on inexistent InputConnection");
return;
@@ -472,7 +474,7 @@
return;
}
case DO_PERFORM_PRIVATE_COMMAND: {
- InputConnection ic = mInputConnection.get();
+ InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "performPrivateCommand on inactive InputConnection");
return;
@@ -485,7 +487,7 @@
case DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO: {
SomeArgs args = (SomeArgs)msg.obj;
try {
- InputConnection ic = mInputConnection.get();
+ InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "requestCursorAnchorInfo on inactive InputConnection");
args.callback.setRequestUpdateCursorAnchorInfoResult(false, args.seq);
@@ -498,16 +500,16 @@
}
return;
}
- case DO_REPORT_FINISH: {
+ case DO_CLOSE_CONNECTION: {
// Note that we do not need to worry about race condition here, because 1) mFinished
// is updated only inside this block, and 2) the code here is running on a Handler
- // hence we assume multiple DO_REPORT_FINISH messages will not be handled at the
+ // hence we assume multiple DO_CLOSE_CONNECTION messages will not be handled at the
// same time.
if (isFinished()) {
return;
}
try {
- InputConnection ic = mInputConnection.get();
+ InputConnection ic = getInputConnection();
// Note we do NOT check isActive() here, because this is safe
// for an IME to call at any time, and we need to allow it
// through to clean up our state after the IME has switched to
@@ -515,14 +517,14 @@
if (ic == null) {
return;
}
- ic.finishComposingText();
- // TODO: Make reportFinish() public method of InputConnection to remove this
- // check.
- if (ic instanceof BaseInputConnection) {
- ((BaseInputConnection) ic).reportFinish();
+ @MissingMethodFlags
+ final int missingMethods = InputConnectionInspector.getMissingMethodFlags(ic);
+ if ((missingMethods & MissingMethodFlags.CLOSE_CONNECTION) == 0) {
+ ic.closeConnection();
}
} finally {
synchronized (mLock) {
+ mInputConnection = null;
mFinished = true;
}
}
diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java
index 85b8606..f9884d8 100644
--- a/core/java/com/android/internal/view/InputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/InputConnectionWrapper.java
@@ -487,6 +487,10 @@
return null;
}
+ public void closeConnection() {
+ // Nothing should happen when called from input method.
+ }
+
private boolean isMethodMissing(@MissingMethodFlags final int methodFlag) {
return (mMissingMethods & methodFlag) == methodFlag;
}
diff --git a/core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java b/core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java
index 526e2ae..9e2cbdb 100644
--- a/core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java
+++ b/core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java
@@ -45,7 +45,8 @@
private static float[] createLUT(TimeInterpolator interpolator, long duration) {
long frameIntervalNanos = Choreographer.getInstance().getFrameIntervalNanos();
int animIntervalMs = (int) (frameIntervalNanos / TimeUtils.NANOS_PER_MS);
- int numAnimFrames = (int) Math.ceil(((double) duration) / animIntervalMs);
+ // We need 2 frame values as the minimal.
+ int numAnimFrames = Math.max(2, (int) Math.ceil(((double) duration) / animIntervalMs));
float values[] = new float[numAnimFrames];
float lastFrame = numAnimFrames - 1;
for (int i = 0; i < numAnimFrames; i++) {
diff --git a/core/java/com/android/internal/view/menu/ActionMenuItemView.java b/core/java/com/android/internal/view/menu/ActionMenuItemView.java
index bd97e5d..4738f5e 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuItemView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuItemView.java
@@ -346,16 +346,6 @@
}
return false;
}
-
- @Override
- protected boolean onForwardingStopped() {
- final ShowableListMenu popup = getPopup();
- if (popup != null) {
- popup.dismiss();
- return true;
- }
- return false;
- }
}
public static abstract class PopupCallback {
diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java
index 31b2f96..df57639 100644
--- a/core/java/com/android/internal/view/menu/MenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/MenuBuilder.java
@@ -65,7 +65,6 @@
private final Context mContext;
private final Resources mResources;
- private final boolean mShowCascadingMenus;
/**
* Whether the shortcuts should be qwerty-accessible. Use isQwertyMode()
@@ -188,9 +187,6 @@
public MenuBuilder(Context context) {
mContext = context;
mResources = context.getResources();
- mShowCascadingMenus = context.getResources().getBoolean(
- com.android.internal.R.bool.config_enableCascadingSubmenus);
-
mItems = new ArrayList<MenuItemImpl>();
mVisibleItems = new ArrayList<MenuItemImpl>();
@@ -915,10 +911,6 @@
close(true /* closeAllMenus */);
}
} else if (itemImpl.hasSubMenu() || providerHasSubMenu) {
- if (!mShowCascadingMenus) {
- close(false /* closeAllMenus */);
- }
-
if (!itemImpl.hasSubMenu()) {
itemImpl.setSubMenu(new SubMenuBuilder(getContext(), this, itemImpl));
}
diff --git a/core/java/com/android/internal/view/menu/StandardMenuPopup.java b/core/java/com/android/internal/view/menu/StandardMenuPopup.java
index 2cb224e..8ced36f 100644
--- a/core/java/com/android/internal/view/menu/StandardMenuPopup.java
+++ b/core/java/com/android/internal/view/menu/StandardMenuPopup.java
@@ -240,7 +240,10 @@
mTreeObserver = null;
}
mShownAnchorView.removeOnAttachStateChangeListener(mAttachStateChangeListener);
- mOnDismissListener.onDismiss();
+
+ if (mOnDismissListener != null) {
+ mOnDismissListener.onDismiss();
+ }
}
@Override
@@ -265,6 +268,13 @@
subPopup.setPresenterCallback(mPresenterCallback);
subPopup.setForceShowIcon(mAdapter.getForceShowIcon());
+ // Pass responsibility for handling onDismiss to the submenu.
+ subPopup.setOnDismissListener(mOnDismissListener);
+ mOnDismissListener = null;
+
+ // Close this menu popup to make room for the submenu popup.
+ dismiss();
+
// Show the new sub-menu popup at the same location as this popup.
if (subPopup.tryShow(mXOffset, mYOffset)) {
if (mPresenterCallback != null) {
diff --git a/core/java/com/android/internal/widget/DecorCaptionView.java b/core/java/com/android/internal/widget/DecorCaptionView.java
index 409a17f..e59d7ba 100644
--- a/core/java/com/android/internal/widget/DecorCaptionView.java
+++ b/core/java/com/android/internal/widget/DecorCaptionView.java
@@ -136,7 +136,7 @@
public void setPhoneWindow(PhoneWindow owner, boolean show) {
mOwner = owner;
mShow = show;
- mOverlayWithAppContent = owner.getOverlayDecorCaption();
+ mOverlayWithAppContent = owner.isOverlayWithDecorCaptionEnabled();
if (mOverlayWithAppContent) {
// The caption is covering the content, so we make its background transparent to make
// the content visible.
diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java
index a96b5a0..7f7528d 100644
--- a/core/java/com/android/internal/widget/EditableInputConnection.java
+++ b/core/java/com/android/internal/widget/EditableInputConnection.java
@@ -83,13 +83,9 @@
return false;
}
- /**
- * @hide
- */
@Override
- public void reportFinish() {
- super.reportFinish();
-
+ public void closeConnection() {
+ super.closeConnection();
synchronized(this) {
while (mBatchEditNesting > 0) {
endBatchEdit();
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index da223a6..1848fe8 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -1460,6 +1460,7 @@
super(Preconditions.checkNotNull(popup).mContext);
this.mPopup = popup;
setScrollBarDefaultDelayBeforeFade(ViewConfiguration.getScrollDefaultDelay() * 3);
+ setScrollIndicators(View.SCROLL_INDICATOR_TOP | View.SCROLL_INDICATOR_BOTTOM);
}
@Override
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 9d14478..3d892af 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1099,7 +1099,8 @@
|| mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
|| mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX
|| mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
- || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
+ || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX
+ || mode == DevicePolicyManager.PASSWORD_QUALITY_MANAGED;
return passwordEnabled && savedPasswordExists(userId);
}
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index c4347f8..25b487e 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -17,9 +17,14 @@
package com.android.internal.widget;
+import android.annotation.NonNull;
import android.content.Context;
import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -85,6 +90,10 @@
private final float mMinFlingVelocity;
private final OverScroller mScroller;
private final VelocityTracker mVelocityTracker;
+ private final Drawable mScrollIndicatorDrawable;
+ private final Drawable mFakeForeground;
+
+ private View mButtonBar;
private OnDismissedListener mOnDismissedListener;
private RunOnDismissedListener mRunOnDismissedListener;
@@ -106,6 +115,8 @@
}
};
+ private final int[] mTempOffset = new int[2];
+
public ResolverDrawerLayout(Context context) {
this(context, null);
}
@@ -127,6 +138,9 @@
mMaxCollapsedHeight);
a.recycle();
+ mScrollIndicatorDrawable = mContext.getDrawable(R.drawable.scroll_indicator_material);
+ mFakeForeground = new ColorDrawable(Color.TRANSPARENT);
+
mScroller = new OverScroller(context, AnimationUtils.loadInterpolator(context,
android.R.interpolator.decelerate_quint));
mVelocityTracker = VelocityTracker.obtain();
@@ -138,6 +152,13 @@
setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
}
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ mButtonBar = findViewById(R.id.button_bar);
+ }
+
public void setSmallCollapsed(boolean smallCollapsed) {
mSmallCollapsed = smallCollapsed;
requestLayout();
@@ -202,8 +223,7 @@
}
final boolean isCollapsedNew = mCollapseOffset != 0;
if (isCollapsedOld != isCollapsedNew) {
- notifyViewAccessibilityStateChangedIfNeeded(
- AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+ onCollapsedChanged(isCollapsedNew);
}
} else {
// Start out collapsed at first unless we restored state for otherwise
@@ -442,8 +462,7 @@
mTopOffset += dy;
final boolean isCollapsedNew = newPos != 0;
if (isCollapsedOld != isCollapsedNew) {
- notifyViewAccessibilityStateChangedIfNeeded(
- AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+ onCollapsedChanged(isCollapsedNew);
}
postInvalidateOnAnimation();
return dy;
@@ -451,6 +470,14 @@
return 0;
}
+ private void onCollapsedChanged(boolean isCollapsed) {
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+
+ // Set a fake foreground so that we receive onDrawForeground().
+ setForeground(isCollapsed ? mFakeForeground : null);
+ }
+
void dispatchOnDismissed() {
if (mOnDismissedListener != null) {
mOnDismissedListener.onDismissed();
@@ -709,6 +736,23 @@
}
@Override
+ public void onDrawForeground(Canvas canvas) {
+ if (isCollapsed() && mButtonBar != null) {
+ // Draw the scroll indicator directly above the button bar.
+ final int height = mScrollIndicatorDrawable.getIntrinsicHeight();
+ mButtonBar.getLocationInWindow(mTempOffset);
+ final int barTop = mTempOffset[1];
+ getLocationInWindow(mTempOffset);
+ final int myTop = mTempOffset[1];
+ final int top = (barTop - myTop) - height;
+ mScrollIndicatorDrawable.setBounds(0, top, getWidth(), top + height);
+ mScrollIndicatorDrawable.draw(canvas);
+ }
+
+ super.onDrawForeground(canvas);
+ }
+
+ @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int sourceWidth = MeasureSpec.getSize(widthMeasureSpec);
int widthSize = sourceWidth;
diff --git a/services/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
similarity index 94%
rename from services/core/java/com/android/server/SystemConfig.java
rename to core/java/com/android/server/SystemConfig.java
index 30e0ceb..07912cd 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -19,6 +19,7 @@
import static com.android.internal.util.ArrayUtils.appendInt;
import android.app.ActivityManager;
+import android.content.ComponentName;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageManager;
import android.os.Environment;
@@ -115,6 +116,9 @@
// These are the packages that should not run under system user
final ArraySet<String> mSystemUserBlacklistedApps = new ArraySet<>();
+ // These are the components that are enabled by default as VR mode listener services.
+ final ArraySet<ComponentName> mDefaultVrComponents = new ArraySet<>();
+
public static SystemConfig getInstance() {
synchronized (SystemConfig.class) {
if (sInstance == null) {
@@ -168,6 +172,10 @@
return mSystemUserBlacklistedApps;
}
+ public ArraySet<ComponentName> getDefaultVrComponents() {
+ return mDefaultVrComponents;
+ }
+
SystemConfig() {
// Read configuration from system
readPermissions(Environment.buildPath(
@@ -431,6 +439,19 @@
mSystemUserBlacklistedApps.add(pkgname);
}
XmlUtils.skipCurrentTag(parser);
+ } else if ("default-enabled-vr-app".equals(name) && allowAppConfigs) {
+ String pkgname = parser.getAttributeValue(null, "package");
+ String clsname = parser.getAttributeValue(null, "class");
+ if (pkgname == null) {
+ Slog.w(TAG, "<default-enabled-vr-app without package in " + permFile
+ + " at " + parser.getPositionDescription());
+ } else if (clsname == null) {
+ Slog.w(TAG, "<default-enabled-vr-app without class in " + permFile
+ + " at " + parser.getPositionDescription());
+ } else {
+ mDefaultVrComponents.add(new ComponentName(pkgname, clsname));
+ }
+ XmlUtils.skipCurrentTag(parser);
} else {
XmlUtils.skipCurrentTag(parser);
continue;
diff --git a/core/java/com/android/server/backup/ShortcutBackupHelper.java b/core/java/com/android/server/backup/ShortcutBackupHelper.java
new file mode 100644
index 0000000..0b3f2ae
--- /dev/null
+++ b/core/java/com/android/server/backup/ShortcutBackupHelper.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.backup;
+
+import android.app.backup.BlobBackupHelper;
+import android.content.Context;
+import android.content.pm.IShortcutService;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.Slog;
+
+public class ShortcutBackupHelper extends BlobBackupHelper {
+ private static final String TAG = "ShortcutBackupAgent";
+ private static final int BLOB_VERSION = 1;
+
+ private static final String KEY_USER_FILE = "shortcutuser.xml";
+
+ public ShortcutBackupHelper() {
+ super(BLOB_VERSION, KEY_USER_FILE);
+ }
+
+ private IShortcutService getShortcutService() {
+ return IShortcutService.Stub.asInterface(
+ ServiceManager.getService(Context.SHORTCUT_SERVICE));
+ }
+
+ @Override
+ protected byte[] getBackupPayload(String key) {
+ switch (key) {
+ case KEY_USER_FILE:
+ try {
+ return getShortcutService().getBackupPayload(UserHandle.USER_SYSTEM);
+ } catch (Exception e) {
+ Slog.wtf(TAG, "Backup failed", e);
+ }
+ break;
+ default:
+ Slog.w(TAG, "Unknown key: " + key);
+ }
+ return null;
+ }
+
+ @Override
+ protected void applyRestoredPayload(String key, byte[] payload) {
+ switch (key) {
+ case KEY_USER_FILE:
+ try {
+ getShortcutService().applyRestore(payload, UserHandle.USER_SYSTEM);
+ } catch (Exception e) {
+ Slog.wtf(TAG, "Restore failed", e);
+ }
+ break;
+ default:
+ Slog.w(TAG, "Unknown key: " + key);
+ }
+ }
+}
diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java
index 181ed51..2d12fcd 100644
--- a/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/core/java/com/android/server/backup/SystemBackupAgent.java
@@ -17,9 +17,9 @@
package com.android.server.backup;
import android.app.IWallpaperManager;
+import android.app.backup.BackupAgentHelper;
import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
-import android.app.backup.BackupAgentHelper;
import android.app.backup.FullBackup;
import android.app.backup.FullBackupDataOutput;
import android.app.backup.WallpaperBackupHelper;
@@ -48,6 +48,7 @@
private static final String NOTIFICATION_HELPER = "notifications";
private static final String PERMISSION_HELPER = "permissions";
private static final String USAGE_STATS_HELPER = "usage_stats";
+ private static final String SHORTCUT_MANAGER_HELPER = "shortcut_manager";
// These paths must match what the WallpaperManagerService uses. The leaf *_FILENAME
// are also used in the full-backup file format, so must not change unless steps are
@@ -100,6 +101,7 @@
addHelper(NOTIFICATION_HELPER, new NotificationBackupHelper(this));
addHelper(PERMISSION_HELPER, new PermissionBackupHelper());
addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(this));
+ addHelper(SHORTCUT_MANAGER_HELPER, new ShortcutBackupHelper());
super.onBackup(oldState, data, newState);
}
@@ -138,6 +140,7 @@
addHelper(NOTIFICATION_HELPER, new NotificationBackupHelper(this));
addHelper(PERMISSION_HELPER, new PermissionBackupHelper());
addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(this));
+ addHelper(SHORTCUT_MANAGER_HELPER, new ShortcutBackupHelper());
try {
super.onRestore(data, appVersionCode, newState);
diff --git a/core/java/com/android/server/net/NetlinkTracker.java b/core/java/com/android/server/net/NetlinkTracker.java
index d45982e..5b421d9 100644
--- a/core/java/com/android/server/net/NetlinkTracker.java
+++ b/core/java/com/android/server/net/NetlinkTracker.java
@@ -102,6 +102,19 @@
}
@Override
+ public void interfaceRemoved(String iface) {
+ maybeLog("interfaceRemoved", iface);
+ if (mInterfaceName.equals(iface)) {
+ // Our interface was removed. Clear our LinkProperties and tell our owner that they are
+ // now empty. Note that from the moment that the interface is removed, any further
+ // interface-specific messages (e.g., RTM_DELADDR) will not reach us, because the netd
+ // code that parses them will not be able to resolve the ifindex to an interface name.
+ clearLinkProperties();
+ mCallback.update();
+ }
+ }
+
+ @Override
public void addressUpdated(String iface, LinkAddress address) {
if (mInterfaceName.equals(iface)) {
maybeLog("addressUpdated", iface, address);
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index c685b0c..8a512a6 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -103,7 +103,6 @@
android_util_jar_StrictJarFile.cpp \
android_graphics_Canvas.cpp \
android_graphics_Picture.cpp \
- android/graphics/AvoidXfermode.cpp \
android/graphics/Bitmap.cpp \
android/graphics/BitmapFactory.cpp \
android/graphics/Camera.cpp \
@@ -193,9 +192,9 @@
$(TOP)/system/media/camera/include \
$(TOP)/system/netd/include \
external/pdfium/core/include/fpdfapi \
- external/pdfium/core/include/fpdfdoc \
external/pdfium/fpdfsdk/include \
external/pdfium/public \
+ external/pdfium \
external/skia/include/private \
external/skia/src/core \
external/skia/src/effects \
diff --git a/core/jni/android/graphics/AvoidXfermode.cpp b/core/jni/android/graphics/AvoidXfermode.cpp
deleted file mode 100644
index 9ca1f26..0000000
--- a/core/jni/android/graphics/AvoidXfermode.cpp
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "AvoidXfermode.h"
-#include "SkColorPriv.h"
-#include "SkReadBuffer.h"
-#include "SkWriteBuffer.h"
-#include "SkString.h"
-
-AvoidXfermode::AvoidXfermode(SkColor opColor, U8CPU tolerance, Mode mode) {
- if (tolerance > 255) {
- tolerance = 255;
- }
- fTolerance = SkToU8(tolerance);
- fOpColor = opColor;
- fDistMul = (256 << 14) / (tolerance + 1);
- fMode = mode;
-}
-
-SkFlattenable* AvoidXfermode::CreateProc(SkReadBuffer& buffer) {
- const SkColor color = buffer.readColor();
- const unsigned tolerance = buffer.readUInt();
- const unsigned mode = buffer.readUInt();
- return Create(color, tolerance, (Mode)mode);
-}
-
-void AvoidXfermode::flatten(SkWriteBuffer& buffer) const {
- buffer.writeColor(fOpColor);
- buffer.writeUInt(fTolerance);
- buffer.writeUInt(fMode);
-}
-
-// returns 0..31
-static unsigned color_dist16(uint16_t c, unsigned r, unsigned g, unsigned b) {
- SkASSERT(r <= SK_R16_MASK);
- SkASSERT(g <= SK_G16_MASK);
- SkASSERT(b <= SK_B16_MASK);
-
- unsigned dr = SkAbs32(SkGetPackedR16(c) - r);
- unsigned dg = SkAbs32(SkGetPackedG16(c) - g) >> (SK_G16_BITS - SK_R16_BITS);
- unsigned db = SkAbs32(SkGetPackedB16(c) - b);
-
- return SkMax32(dr, SkMax32(dg, db));
-}
-
-// returns 0..255
-static unsigned color_dist32(SkPMColor c, U8CPU r, U8CPU g, U8CPU b) {
- SkASSERT(r <= 0xFF);
- SkASSERT(g <= 0xFF);
- SkASSERT(b <= 0xFF);
-
- unsigned dr = SkAbs32(SkGetPackedR32(c) - r);
- unsigned dg = SkAbs32(SkGetPackedG32(c) - g);
- unsigned db = SkAbs32(SkGetPackedB32(c) - b);
-
- return SkMax32(dr, SkMax32(dg, db));
-}
-
-static int scale_dist_14(int dist, uint32_t mul, uint32_t sub) {
- int tmp = dist * mul - sub;
- int result = (tmp + (1 << 13)) >> 14;
-
- return result;
-}
-
-static inline unsigned Accurate255To256(unsigned x) {
- return x + (x >> 7);
-}
-
-void AvoidXfermode::xfer32(SkPMColor dst[], const SkPMColor src[], int count,
- const SkAlpha aa[]) const {
- unsigned opR = SkColorGetR(fOpColor);
- unsigned opG = SkColorGetG(fOpColor);
- unsigned opB = SkColorGetB(fOpColor);
- uint32_t mul = fDistMul;
- uint32_t sub = (fDistMul - (1 << 14)) << 8;
-
- int MAX, mask;
-
- if (kTargetColor_Mode == fMode) {
- mask = -1;
- MAX = 255;
- } else {
- mask = 0;
- MAX = 0;
- }
-
- for (int i = 0; i < count; i++) {
- int d = color_dist32(dst[i], opR, opG, opB);
- // now reverse d if we need to
- d = MAX + (d ^ mask) - mask;
- SkASSERT((unsigned)d <= 255);
- d = Accurate255To256(d);
-
- d = scale_dist_14(d, mul, sub);
- SkASSERT(d <= 256);
-
- if (d > 0) {
- if (aa) {
- d = SkAlphaMul(d, Accurate255To256(*aa++));
- if (0 == d) {
- continue;
- }
- }
- dst[i] = SkFourByteInterp256(src[i], dst[i], d);
- }
- }
-}
-
-static inline U16CPU SkBlend3216(SkPMColor src, U16CPU dst, unsigned scale) {
- SkASSERT(scale <= 32);
- scale <<= 3;
-
- return SkPackRGB16( SkAlphaBlend(SkPacked32ToR16(src), SkGetPackedR16(dst), scale),
- SkAlphaBlend(SkPacked32ToG16(src), SkGetPackedG16(dst), scale),
- SkAlphaBlend(SkPacked32ToB16(src), SkGetPackedB16(dst), scale));
-}
-
-void AvoidXfermode::xfer16(uint16_t dst[], const SkPMColor src[], int count,
- const SkAlpha aa[]) const {
- unsigned opR = SkColorGetR(fOpColor) >> (8 - SK_R16_BITS);
- unsigned opG = SkColorGetG(fOpColor) >> (8 - SK_G16_BITS);
- unsigned opB = SkColorGetB(fOpColor) >> (8 - SK_R16_BITS);
- uint32_t mul = fDistMul;
- uint32_t sub = (fDistMul - (1 << 14)) << SK_R16_BITS;
-
- int MAX, mask;
-
- if (kTargetColor_Mode == fMode) {
- mask = -1;
- MAX = 31;
- } else {
- mask = 0;
- MAX = 0;
- }
-
- for (int i = 0; i < count; i++) {
- int d = color_dist16(dst[i], opR, opG, opB);
- // now reverse d if we need to
- d = MAX + (d ^ mask) - mask;
- SkASSERT((unsigned)d <= 31);
- // convert from 0..31 to 0..32
- d += d >> 4;
- d = scale_dist_14(d, mul, sub);
- SkASSERT(d <= 32);
-
- if (d > 0) {
- if (aa) {
- d = SkAlphaMul(d, Accurate255To256(*aa++));
- if (0 == d) {
- continue;
- }
- }
- dst[i] = SkBlend3216(src[i], dst[i], d);
- }
- }
-}
-
-void AvoidXfermode::xferA8(SkAlpha dst[], const SkPMColor src[], int count,
- const SkAlpha aa[]) const {
-}
-
-#ifndef SK_IGNORE_TO_STRING
-void AvoidXfermode::toString(SkString* str) const {
- str->append("AvoidXfermode: opColor: ");
- str->appendHex(fOpColor);
- str->appendf("distMul: %d ", fDistMul);
-
- static const char* gModeStrings[] = { "Avoid", "Target" };
-
- str->appendf("mode: %s", gModeStrings[fMode]);
-}
-#endif
diff --git a/core/jni/android/graphics/AvoidXfermode.h b/core/jni/android/graphics/AvoidXfermode.h
deleted file mode 100644
index 8b7fb71..0000000
--- a/core/jni/android/graphics/AvoidXfermode.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef AvoidXfermode_DEFINED
-#define AvoidXfermode_DEFINED
-
-#include "SkColor.h"
-#include "SkTypes.h"
-#include "SkXfermode.h"
-
-/** \class AvoidXfermode
-
- This xfermode will draw the src everywhere except on top of the specified
- color.
-*/
-class AvoidXfermode : public SkXfermode {
-public:
- enum Mode {
- kAvoidColor_Mode, //!< draw everywhere except on the opColor
- kTargetColor_Mode //!< draw only on top of the opColor
- };
-
- /** This xfermode draws, or doesn't draw, based on the destination's
- distance from an op-color.
-
- There are two modes, and each mode interprets a tolerance value.
-
- Avoid: In this mode, drawing is allowed only on destination pixels that
- are different from the op-color.
- Tolerance near 0: avoid any colors even remotely similar to the op-color
- Tolerance near 255: avoid only colors nearly identical to the op-color
-
- Target: In this mode, drawing only occurs on destination pixels that
- are similar to the op-color
- Tolerance near 0: draw only on colors that are nearly identical to the op-color
- Tolerance near 255: draw on any colors even remotely similar to the op-color
- */
- static AvoidXfermode* Create(SkColor opColor, U8CPU tolerance, Mode mode) {
- return new AvoidXfermode(opColor, tolerance, mode);
- }
-
- // overrides from SkXfermode
- void xfer32(SkPMColor dst[], const SkPMColor src[], int count,
- const SkAlpha aa[]) const override;
- void xfer16(uint16_t dst[], const SkPMColor src[], int count,
- const SkAlpha aa[]) const override;
- void xferA8(SkAlpha dst[], const SkPMColor src[], int count,
- const SkAlpha aa[]) const override;
-
- SK_TO_STRING_OVERRIDE()
- SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(AvoidXfermode)
-
-protected:
- AvoidXfermode(SkColor opColor, U8CPU tolerance, Mode mode);
- void flatten(SkWriteBuffer&) const override;
-
-private:
- SkColor fOpColor;
- uint32_t fDistMul; // x.14 cached from fTolerance
- uint8_t fTolerance;
- Mode fMode;
-
- typedef SkXfermode INHERITED;
-};
-
-#endif
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 27b9830..22a81d4 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -251,13 +251,6 @@
void Bitmap::reconfigure(const SkImageInfo& info, size_t rowBytes,
SkColorTable* ctable) {
- {
- android::AutoMutex _lock(mLock);
- if (mPinnedRefCount) {
- ALOGW("Called reconfigure on a bitmap that is in use! This may"
- " cause graphical corruption!");
- }
- }
mPixelRef->reconfigure(info, rowBytes, ctable);
}
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 1ead618..528541d 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -723,6 +723,7 @@
// Make sure that the recycled bitmap has the correct alpha type.
mRecycledBitmap->setAlphaType(bitmap->alphaType());
+ bitmap->notifyPixelsChanged();
bitmap->lockPixels();
mNeedsCopy = false;
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 27d8fed..f46f45c 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -768,6 +768,22 @@
return false;
}
+ // Returns true if the given string is exact one pair of regional indicators.
+ static bool isFlag(const jchar* str, size_t length) {
+ const jchar RI_LEAD_SURROGATE = 0xD83C;
+ const jchar RI_TRAIL_SURROGATE_MIN = 0xDDE6;
+ const jchar RI_TRAIL_SURROGATE_MAX = 0xDDFF;
+
+ if (length != 4) {
+ return false;
+ }
+ if (str[0] != RI_LEAD_SURROGATE || str[2] != RI_LEAD_SURROGATE) {
+ return false;
+ }
+ return RI_TRAIL_SURROGATE_MIN <= str[1] && str[1] <= RI_TRAIL_SURROGATE_MAX &&
+ RI_TRAIL_SURROGATE_MIN <= str[3] && str[3] <= RI_TRAIL_SURROGATE_MAX;
+ }
+
static jboolean hasGlyph(JNIEnv *env, jclass, jlong paintHandle, jlong typefaceHandle,
jint bidiFlags, jstring string) {
const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
@@ -817,13 +833,32 @@
// in joining scripts, such as Arabic and Mongolian.
return false;
}
- return nGlyphs > 0 && !layoutContainsNotdef(layout);
+
+ if (nGlyphs == 0 || layoutContainsNotdef(layout)) {
+ return false; // The collection doesn't have a glyph.
+ }
+
+ if (nChars == 2 && isFlag(str.get(), str.size())) {
+ // Some font may have a special glyph for unsupported regional indicator pairs.
+ // To return false for this case, need to compare the glyph id with the one of ZZ
+ // since ZZ is reserved for unknown or invalid territory.
+ // U+1F1FF (REGIONAL INDICATOR SYMBOL LETTER Z) is \uD83C\uDDFF in UTF16.
+ static const jchar ZZ_FLAG_STR[] = { 0xD83C, 0xDDFF, 0xD83C, 0xDDFF };
+ Layout zzLayout;
+ MinikinUtils::doLayout(&zzLayout, paint, bidiFlags, typeface, ZZ_FLAG_STR, 0, 4, 4);
+ if (zzLayout.nGlyphs() != 1 || layoutContainsNotdef(zzLayout)) {
+ // The font collection doesn't have a glyph for unknown flag. Just return true.
+ return true;
+ }
+ return zzLayout.getGlyphId(0) != layout.getGlyphId(0);
+ }
+ return true;
}
static jfloat doRunAdvance(const Paint* paint, Typeface* typeface, const jchar buf[],
jint start, jint count, jint bufSize, jboolean isRtl, jint offset) {
int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
- if (offset == count) {
+ if (offset == start + count) {
return MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count,
bufSize, nullptr);
}
diff --git a/core/jni/android/graphics/SurfaceTexture.cpp b/core/jni/android/graphics/SurfaceTexture.cpp
index 2018e76..6e9830e 100644
--- a/core/jni/android/graphics/SurfaceTexture.cpp
+++ b/core/jni/android/graphics/SurfaceTexture.cpp
@@ -65,12 +65,12 @@
EGLContext ctx = eglGetCurrentContext();
if (dpy == EGL_NO_DISPLAY) {
- ALOGE("isProtectedSurface: invalid current EGLDisplay");
+ ALOGI("isProtectedSurface: invalid current EGLDisplay");
return false;
}
if (ctx == EGL_NO_CONTEXT) {
- ALOGE("isProtectedSurface: invalid current EGLContext");
+ ALOGI("isProtectedSurface: invalid current EGLContext");
return false;
}
diff --git a/core/jni/android/graphics/pdf/PdfEditor.cpp b/core/jni/android/graphics/pdf/PdfEditor.cpp
index 0177635..2c840bd 100644
--- a/core/jni/android/graphics/pdf/PdfEditor.cpp
+++ b/core/jni/android/graphics/pdf/PdfEditor.cpp
@@ -196,7 +196,7 @@
return;
}
- CFX_AffineMatrix matrix;
+ CFX_Matrix matrix;
SkMatrix* skTransform = reinterpret_cast<SkMatrix*>(transformPtr);
diff --git a/core/jni/android/graphics/pdf/PdfRenderer.cpp b/core/jni/android/graphics/pdf/PdfRenderer.cpp
index 6ddfacf..27f3493 100644
--- a/core/jni/android/graphics/pdf/PdfRenderer.cpp
+++ b/core/jni/android/graphics/pdf/PdfRenderer.cpp
@@ -205,11 +205,10 @@
clip.bottom = destBottom;
fxgeDevice->SetClip_Rect(&clip);
- CPDF_RenderContext* pageContext = new CPDF_RenderContext;
+ CPDF_RenderContext* pageContext = new CPDF_RenderContext(pPage);
pContext->m_pContext = pageContext;
- pageContext->Create(pPage);
- CFX_AffineMatrix matrix;
+ CFX_Matrix matrix;
if (!transform) {
pPage->GetDisplayMatrix(matrix, destLeft, destTop, destRight - destLeft,
destBottom - destTop, 0);
@@ -232,8 +231,8 @@
}
pageContext->AppendObjectList(pPage, &matrix);
- pContext->m_pRenderer = new CPDF_ProgressiveRenderer;
- pContext->m_pRenderer->Start(pageContext, fxgeDevice, renderOptions, NULL);
+ pContext->m_pRenderer = new CPDF_ProgressiveRenderer(pageContext, fxgeDevice, renderOptions);
+ pContext->m_pRenderer->Start(NULL);
fxgeDevice->RestoreState();
diff --git a/core/jni/android_graphics_drawable_VectorDrawable.cpp b/core/jni/android_graphics_drawable_VectorDrawable.cpp
index a2662f9..b04293e 100644
--- a/core/jni/android_graphics_drawable_VectorDrawable.cpp
+++ b/core/jni/android_graphics_drawable_VectorDrawable.cpp
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-#include "jni.h"
#include "GraphicsJNI.h"
+#include "jni.h"
#include "core_jni_helpers.h"
-#include "log/log.h"
+#include "PathParser.h"
#include "VectorDrawable.h"
#include <hwui/Paint.h>
@@ -27,43 +27,15 @@
using namespace uirenderer;
using namespace uirenderer::VectorDrawable;
+/**
+ * VectorDrawable's pre-draw construction.
+ */
static jlong createTree(JNIEnv*, jobject, jlong groupPtr) {
VectorDrawable::Group* rootGroup = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
VectorDrawable::Tree* tree = new VectorDrawable::Tree(rootGroup);
return reinterpret_cast<jlong>(tree);
}
-static void setTreeViewportSize(JNIEnv*, jobject, jlong treePtr,
- jfloat viewportWidth, jfloat viewportHeight) {
- VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr);
- tree->setViewportSize(viewportWidth, viewportHeight);
-}
-
-static jboolean setRootAlpha(JNIEnv*, jobject, jlong treePtr, jfloat alpha) {
- VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr);
- return tree->setRootAlpha(alpha);
-}
-
-static jfloat getRootAlpha(JNIEnv*, jobject, jlong treePtr) {
- VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr);
- return tree->getRootAlpha();
-}
-
-static void setAllowCaching(JNIEnv*, jobject, jlong treePtr, jboolean allowCaching) {
- VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr);
- tree->setAllowCaching(allowCaching);
-}
-
-static void draw(JNIEnv* env, jobject, jlong treePtr, jlong canvasPtr,
- jlong colorFilterPtr, jobject jrect, jboolean needsMirroring, jboolean canReuseCache) {
- VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr);
- Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
- SkRect rect;
- GraphicsJNI::jrect_to_rect(env, jrect, &rect);
- SkColorFilter* colorFilter = reinterpret_cast<SkColorFilter*>(colorFilterPtr);
- tree->draw(canvas, colorFilter, rect, needsMirroring, canReuseCache);
-}
-
static jlong createEmptyFullPath(JNIEnv*, jobject) {
VectorDrawable::FullPath* newPath = new VectorDrawable::FullPath();
return reinterpret_cast<jlong>(newPath);
@@ -76,46 +48,6 @@
return reinterpret_cast<jlong>(newPath);
}
-static void updateFullPathPropertiesAndStrokeStyles(JNIEnv*, jobject, jlong fullPathPtr,
- jfloat strokeWidth, jint strokeColor, jfloat strokeAlpha, jint fillColor, jfloat fillAlpha,
- jfloat trimPathStart, jfloat trimPathEnd, jfloat trimPathOffset, jfloat strokeMiterLimit,
- jint strokeLineCap, jint strokeLineJoin, jint fillType) {
- VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
- fullPath->updateProperties(strokeWidth, strokeColor, strokeAlpha, fillColor, fillAlpha,
- trimPathStart, trimPathEnd, trimPathOffset, strokeMiterLimit, strokeLineCap,
- strokeLineJoin, fillType);
-}
-
-static void updateFullPathFillGradient(JNIEnv*, jobject, jlong pathPtr, jlong fillGradientPtr) {
- VectorDrawable::FullPath* path = reinterpret_cast<VectorDrawable::FullPath*>(pathPtr);
- SkShader* fillShader = reinterpret_cast<SkShader*>(fillGradientPtr);
- path->setFillGradient(fillShader);
-}
-
-static void updateFullPathStrokeGradient(JNIEnv*, jobject, jlong pathPtr, jlong strokeGradientPtr) {
- VectorDrawable::FullPath* path = reinterpret_cast<VectorDrawable::FullPath*>(pathPtr);
- SkShader* strokeShader = reinterpret_cast<SkShader*>(strokeGradientPtr);
- path->setStrokeGradient(strokeShader);
-}
-
-static jboolean getFullPathProperties(JNIEnv* env, jobject, jlong fullPathPtr,
- jbyteArray outProperties, jint length) {
- VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
- int8_t pathProperties[length];
- bool success = fullPath->getProperties(pathProperties, length);
- env->SetByteArrayRegion(outProperties, 0, length, reinterpret_cast<int8_t*>(&pathProperties));
- return success;
-}
-
-static jboolean getGroupProperties(JNIEnv* env, jobject, jlong groupPtr,
- jfloatArray outProperties, jint length) {
- VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
- float groupProperties[length];
- bool success = group->getProperties(groupProperties, length);
- env->SetFloatArrayRegion(outProperties, 0, length, reinterpret_cast<float*>(&groupProperties));
- return success;
-}
-
static jlong createEmptyClipPath(JNIEnv*, jobject) {
VectorDrawable::ClipPath* newPath = new VectorDrawable::ClipPath();
return reinterpret_cast<jlong>(newPath);
@@ -146,180 +78,265 @@
env->ReleaseStringUTFChars(nameStr, nodeName);
}
-static void updateGroupProperties(JNIEnv*, jobject, jlong groupPtr, jfloat rotate, jfloat pivotX,
- jfloat pivotY, jfloat scaleX, jfloat scaleY, jfloat translateX, jfloat translateY) {
- VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
- group->updateLocalMatrix(rotate, pivotX, pivotY, scaleX, scaleY, translateX, translateY);
-}
-
static void addChild(JNIEnv*, jobject, jlong groupPtr, jlong childPtr) {
VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
VectorDrawable::Node* child = reinterpret_cast<VectorDrawable::Node*>(childPtr);
group->addChild(child);
}
+static void setAllowCaching(JNIEnv*, jobject, jlong treePtr, jboolean allowCaching) {
+ VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr);
+ tree->setAllowCaching(allowCaching);
+}
+
+/**
+ * Draw
+ */
+static void draw(JNIEnv* env, jobject, jlong treePtr, jlong canvasPtr,
+ jlong colorFilterPtr, jobject jrect, jboolean needsMirroring, jboolean canReuseCache) {
+ VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr);
+ Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
+ SkRect rect;
+ GraphicsJNI::jrect_to_rect(env, jrect, &rect);
+ SkColorFilter* colorFilter = reinterpret_cast<SkColorFilter*>(colorFilterPtr);
+ tree->draw(canvas, colorFilter, rect, needsMirroring, canReuseCache);
+}
+
+/**
+ * Setters and getters for updating staging properties that can happen both pre-draw and post draw.
+ */
+static void setTreeViewportSize(JNIEnv*, jobject, jlong treePtr,
+ jfloat viewportWidth, jfloat viewportHeight) {
+ VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr);
+ tree->mutateStagingProperties()->setViewportSize(viewportWidth, viewportHeight);
+}
+
+static jboolean setRootAlpha(JNIEnv*, jobject, jlong treePtr, jfloat alpha) {
+ VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr);
+ return tree->mutateStagingProperties()->setRootAlpha(alpha);
+}
+
+static jfloat getRootAlpha(JNIEnv*, jobject, jlong treePtr) {
+ VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr);
+ return tree->stagingProperties()->getRootAlpha();
+}
+
+static void updateFullPathPropertiesAndStrokeStyles(JNIEnv*, jobject, jlong fullPathPtr,
+ jfloat strokeWidth, jint strokeColor, jfloat strokeAlpha, jint fillColor, jfloat fillAlpha,
+ jfloat trimPathStart, jfloat trimPathEnd, jfloat trimPathOffset, jfloat strokeMiterLimit,
+ jint strokeLineCap, jint strokeLineJoin, jint fillType) {
+ VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+ fullPath->mutateStagingProperties()->updateProperties(strokeWidth, strokeColor, strokeAlpha,
+ fillColor, fillAlpha, trimPathStart, trimPathEnd, trimPathOffset, strokeMiterLimit,
+ strokeLineCap, strokeLineJoin, fillType);
+}
+
+static void updateFullPathFillGradient(JNIEnv*, jobject, jlong pathPtr, jlong fillGradientPtr) {
+ VectorDrawable::FullPath* path = reinterpret_cast<VectorDrawable::FullPath*>(pathPtr);
+ SkShader* fillShader = reinterpret_cast<SkShader*>(fillGradientPtr);
+ path->mutateStagingProperties()->setFillGradient(fillShader);
+}
+
+static void updateFullPathStrokeGradient(JNIEnv*, jobject, jlong pathPtr, jlong strokeGradientPtr) {
+ VectorDrawable::FullPath* path = reinterpret_cast<VectorDrawable::FullPath*>(pathPtr);
+ SkShader* strokeShader = reinterpret_cast<SkShader*>(strokeGradientPtr);
+ path->mutateStagingProperties()->setStrokeGradient(strokeShader);
+}
+
+static jboolean getFullPathProperties(JNIEnv* env, jobject, jlong fullPathPtr,
+ jbyteArray outProperties, jint length) {
+ VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+ int8_t pathProperties[length];
+ bool success = fullPath->stagingProperties()->copyProperties(pathProperties, length);
+ env->SetByteArrayRegion(outProperties, 0, length, reinterpret_cast<int8_t*>(&pathProperties));
+ return success;
+}
+
+static jboolean getGroupProperties(JNIEnv* env, jobject, jlong groupPtr,
+ jfloatArray outProperties, jint length) {
+ VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+ float groupProperties[length];
+ bool success = group->stagingProperties()->copyProperties(groupProperties, length);
+ env->SetFloatArrayRegion(outProperties, 0, length, reinterpret_cast<float*>(&groupProperties));
+ return success;
+}
+
+static void updateGroupProperties(JNIEnv*, jobject, jlong groupPtr, jfloat rotate, jfloat pivotX,
+ jfloat pivotY, jfloat scaleX, jfloat scaleY, jfloat translateX, jfloat translateY) {
+ VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+ group->mutateStagingProperties()->updateProperties(rotate, pivotX, pivotY, scaleX, scaleY,
+ translateX, translateY);
+}
+
static void setPathString(JNIEnv* env, jobject, jlong pathPtr, jstring inputStr,
jint stringLength) {
VectorDrawable::Path* path = reinterpret_cast<VectorDrawable::Path*>(pathPtr);
const char* pathString = env->GetStringUTFChars(inputStr, NULL);
- path->setPath(pathString, stringLength);
+
+ PathParser::ParseResult result;
+ PathData data;
+ PathParser::getPathDataFromString(&data, &result, pathString, stringLength);
+ path->mutateStagingProperties()->setData(data);
env->ReleaseStringUTFChars(inputStr, pathString);
}
+/**
+ * Setters and getters that should only be called from animation thread for animation purpose.
+ */
static jfloat getRotation(JNIEnv*, jobject, jlong groupPtr) {
VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
- return group->getRotation();
+ return group->stagingProperties()->getRotation();
}
static void setRotation(JNIEnv*, jobject, jlong groupPtr, jfloat rotation) {
VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
- group->setRotation(rotation);
+ group->mutateStagingProperties()->setRotation(rotation);
}
static jfloat getPivotX(JNIEnv*, jobject, jlong groupPtr) {
VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
- return group->getPivotX();
+ return group->stagingProperties()->getPivotX();
}
static void setPivotX(JNIEnv*, jobject, jlong groupPtr, jfloat pivotX) {
VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
- group->setPivotX(pivotX);
+ group->mutateStagingProperties()->setPivotX(pivotX);
}
static jfloat getPivotY(JNIEnv*, jobject, jlong groupPtr) {
VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
- return group->getPivotY();
+ return group->stagingProperties()->getPivotY();
}
static void setPivotY(JNIEnv*, jobject, jlong groupPtr, jfloat pivotY) {
VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
- group->setPivotY(pivotY);
+ group->mutateStagingProperties()->setPivotY(pivotY);
}
static jfloat getScaleX(JNIEnv*, jobject, jlong groupPtr) {
VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
- return group->getScaleX();
+ return group->stagingProperties()->getScaleX();
}
static void setScaleX(JNIEnv*, jobject, jlong groupPtr, jfloat scaleX) {
VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
- group->setScaleX(scaleX);
+ group->mutateStagingProperties()->setScaleX(scaleX);
}
static jfloat getScaleY(JNIEnv*, jobject, jlong groupPtr) {
VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
- return group->getScaleY();
+ return group->stagingProperties()->getScaleY();
}
static void setScaleY(JNIEnv*, jobject, jlong groupPtr, jfloat scaleY) {
VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
- group->setScaleY(scaleY);
+ group->mutateStagingProperties()->setScaleY(scaleY);
}
static jfloat getTranslateX(JNIEnv*, jobject, jlong groupPtr) {
VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
- return group->getTranslateX();
+ return group->stagingProperties()->getTranslateX();
}
static void setTranslateX(JNIEnv*, jobject, jlong groupPtr, jfloat translateX) {
VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
- group->setTranslateX(translateX);
+ group->mutateStagingProperties()->setTranslateX(translateX);
}
static jfloat getTranslateY(JNIEnv*, jobject, jlong groupPtr) {
VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
- return group->getTranslateY();
+ return group->stagingProperties()->getTranslateY();
}
static void setTranslateY(JNIEnv*, jobject, jlong groupPtr, jfloat translateY) {
VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
- group->setTranslateY(translateY);
+ group->mutateStagingProperties()->setTranslateY(translateY);
}
static void setPathData(JNIEnv*, jobject, jlong pathPtr, jlong pathDataPtr) {
VectorDrawable::Path* path = reinterpret_cast<VectorDrawable::Path*>(pathPtr);
PathData* pathData = reinterpret_cast<PathData*>(pathDataPtr);
- path->setPathData(*pathData);
+ path->mutateStagingProperties()->setData(*pathData);
}
static jfloat getStrokeWidth(JNIEnv*, jobject, jlong fullPathPtr) {
VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
- return fullPath->getStrokeWidth();
+ return fullPath->stagingProperties()->getStrokeWidth();
}
static void setStrokeWidth(JNIEnv*, jobject, jlong fullPathPtr, jfloat strokeWidth) {
VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
- fullPath->setStrokeWidth(strokeWidth);
+ fullPath->mutateStagingProperties()->setStrokeWidth(strokeWidth);
}
static jint getStrokeColor(JNIEnv*, jobject, jlong fullPathPtr) {
VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
- return fullPath->getStrokeColor();
+ return fullPath->stagingProperties()->getStrokeColor();
}
static void setStrokeColor(JNIEnv*, jobject, jlong fullPathPtr, jint strokeColor) {
VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
- fullPath->setStrokeColor(strokeColor);
+ fullPath->mutateStagingProperties()->setStrokeColor(strokeColor);
}
static jfloat getStrokeAlpha(JNIEnv*, jobject, jlong fullPathPtr) {
VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
- return fullPath->getStrokeAlpha();
+ return fullPath->stagingProperties()->getStrokeAlpha();
}
static void setStrokeAlpha(JNIEnv*, jobject, jlong fullPathPtr, jfloat strokeAlpha) {
VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
- fullPath->setStrokeAlpha(strokeAlpha);
+ fullPath->mutateStagingProperties()->setStrokeAlpha(strokeAlpha);
}
static jint getFillColor(JNIEnv*, jobject, jlong fullPathPtr) {
VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
- return fullPath->getFillColor();
+ return fullPath->stagingProperties()->getFillColor();
}
static void setFillColor(JNIEnv*, jobject, jlong fullPathPtr, jint fillColor) {
VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
- fullPath->setFillColor(fillColor);
+ fullPath->mutateStagingProperties()->setFillColor(fillColor);
}
static jfloat getFillAlpha(JNIEnv*, jobject, jlong fullPathPtr) {
VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
- return fullPath->getFillAlpha();
+ return fullPath->stagingProperties()->getFillAlpha();
}
static void setFillAlpha(JNIEnv*, jobject, jlong fullPathPtr, jfloat fillAlpha) {
VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
- fullPath->setFillAlpha(fillAlpha);
+ fullPath->mutateStagingProperties()->setFillAlpha(fillAlpha);
}
static jfloat getTrimPathStart(JNIEnv*, jobject, jlong fullPathPtr) {
VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
- return fullPath->getTrimPathStart();
+ return fullPath->stagingProperties()->getTrimPathStart();
}
static void setTrimPathStart(JNIEnv*, jobject, jlong fullPathPtr, jfloat trimPathStart) {
VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
- fullPath->setTrimPathStart(trimPathStart);
+ fullPath->mutateStagingProperties()->setTrimPathStart(trimPathStart);
}
static jfloat getTrimPathEnd(JNIEnv*, jobject, jlong fullPathPtr) {
VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
- return fullPath->getTrimPathEnd();
+ return fullPath->stagingProperties()->getTrimPathEnd();
}
static void setTrimPathEnd(JNIEnv*, jobject, jlong fullPathPtr, jfloat trimPathEnd) {
VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
- fullPath->setTrimPathEnd(trimPathEnd);
+ fullPath->mutateStagingProperties()->setTrimPathEnd(trimPathEnd);
}
static jfloat getTrimPathOffset(JNIEnv*, jobject, jlong fullPathPtr) {
VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
- return fullPath->getTrimPathOffset();
+ return fullPath->stagingProperties()->getTrimPathOffset();
}
static void setTrimPathOffset(JNIEnv*, jobject, jlong fullPathPtr, jfloat trimPathOffset) {
VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
- fullPath->setTrimPathOffset(trimPathOffset);
+ fullPath->mutateStagingProperties()->setTrimPathOffset(trimPathOffset);
}
static const JNINativeMethod gMethods[] = {
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 806fcc3..91f003d 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -567,6 +567,45 @@
// save context in opaque field
env->SetLongField(thiz, fields.context, (jlong)context.get());
+
+ // Update default display orientation in case the sensor is reverse-landscape
+ CameraInfo cameraInfo;
+ status_t rc = Camera::getCameraInfo(cameraId, &cameraInfo);
+ if (rc != NO_ERROR) {
+ return rc;
+ }
+ int defaultOrientation = 0;
+ switch (cameraInfo.orientation) {
+ case 0:
+ break;
+ case 90:
+ if (cameraInfo.facing == CAMERA_FACING_FRONT) {
+ defaultOrientation = 180;
+ }
+ break;
+ case 180:
+ defaultOrientation = 180;
+ break;
+ case 270:
+ if (cameraInfo.facing != CAMERA_FACING_FRONT) {
+ defaultOrientation = 180;
+ }
+ break;
+ default:
+ ALOGE("Unexpected camera orientation %d!", cameraInfo.orientation);
+ break;
+ }
+ if (defaultOrientation != 0) {
+ ALOGV("Setting default display orientation to %d", defaultOrientation);
+ rc = camera->sendCommand(CAMERA_CMD_SET_DISPLAY_ORIENTATION,
+ defaultOrientation, 0);
+ if (rc != NO_ERROR) {
+ ALOGE("Unable to update default orientation: %s (%d)",
+ strerror(-rc), rc);
+ return rc;
+ }
+ }
+
return NO_ERROR;
}
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index e39bb1c..90f407f 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -36,15 +36,17 @@
#include "core_jni_helpers.h"
-static struct {
+namespace {
+
+using namespace android;
+
+struct {
jclass clazz;
jmethodID dispatchSensorEvent;
jmethodID dispatchFlushCompleteEvent;
jmethodID dispatchAdditionalInfoEvent;
} gBaseEventQueueClassInfo;
-namespace android {
-
struct SensorOffsets
{
jclass clazz;
@@ -72,9 +74,9 @@
} gListOffsets;
/*
- * The method below are not thread-safe and not intended to be
+ * nativeClassInit is not inteneded to be thread-safe. It should be called before other native...
+ * functions (except nativeCreate).
*/
-
static void
nativeClassInit (JNIEnv *_env, jclass _this)
{
@@ -494,9 +496,7 @@
(void*)nativeInjectSensorData },
};
-}; // namespace android
-
-using namespace android;
+} //unnamed namespace
int register_android_hardware_SensorManager(JNIEnv *env)
{
diff --git a/core/jni/android_hardware_location_ContextHubService.cpp b/core/jni/android_hardware_location_ContextHubService.cpp
index 8724729..91a3b4f 100644
--- a/core/jni/android_hardware_location_ContextHubService.cpp
+++ b/core/jni/android_hardware_location_ContextHubService.cpp
@@ -16,23 +16,39 @@
#include "context_hub.h"
+#define LOG_NDEBUG 0
+#define LOG_TAG "ContextHubService"
+
+#include <inttypes.h>
+#include <jni.h>
+#include <map>
+#include <queue>
#include <string.h>
#include <stdint.h>
#include <stdio.h>
+#include <stdlib.h>
-#include <jni.h>
+#include <cutils/log.h>
+
#include "JNIHelp.h"
#include "core_jni_helpers.h"
-#include "stdint.h"
-#include "stdlib.h"
+
+static constexpr int OS_APP_ID=-1;
+
+static constexpr int MIN_APP_ID=1;
+static constexpr int MAX_APP_ID=128;
+
+static constexpr size_t MSG_HEADER_SIZE=4;
+static constexpr int HEADER_FIELD_MSG_TYPE=0;
+//static constexpr int HEADER_FIELD_MSG_VERSION=1;
+static constexpr int HEADER_FIELD_HUB_HANDLE=2;
+static constexpr int HEADER_FIELD_APP_INSTANCE=3;
namespace android {
namespace {
-// TODO: We should share this array_length function widely around Android
-// code.
/*
* Finds the length of a statically-sized array using template trickery that
* also prevents it from being applied to the wrong type.
@@ -64,35 +80,215 @@
jmethodID contextHubInfoSetPeakPowerDrawMw;
jmethodID contextHubInfoSetSupportedSensors;
jmethodID contextHubInfoSetMemoryRegions;
+ jmethodID contextHubInfoSetMaxPacketLenBytes;
jmethodID contextHubServiceMsgReceiptCallback;
+ jmethodID contextHubServiceAddAppInstance;
};
struct context_hub_info_s {
- int cookie;
+ uint32_t *cookies;
int numHubs;
const struct context_hub_t *hubs;
struct context_hub_module_t *contextHubModule;
};
+struct app_instance_info_s {
+ uint32_t hubHandle; // Id of the hub this app is on
+ int instanceId; // systemwide unique instance id - assigned
+ struct hub_app_info appInfo; // returned from the HAL
+ uint64_t truncName; // Possibly truncated name - logging
+};
+
struct contextHubServiceDb_s {
int initialized;
context_hub_info_s hubInfo;
jniInfo_s jniInfo;
+ std::queue<int> freeIds;
+ std::map<int, app_instance_info_s *> appInstances;
};
} // unnamed namespace
static contextHubServiceDb_s db;
-int context_hub_callback(uint32_t hub_id, const struct hub_message_t *msg,
+int context_hub_callback(uint32_t hubId, const struct hub_message_t *msg,
void *cookie);
+const context_hub_t *get_hub_info(int hubHandle) {
+ if (hubHandle >= 0 && hubHandle < db.hubInfo.numHubs) {
+ return &db.hubInfo.hubs[hubHandle];
+ }
+ return nullptr;
+}
+
+static int send_msg_to_hub(const hub_message_t *msg, int hubHandle) {
+ const context_hub_t *info = get_hub_info(hubHandle);
+
+ if (info) {
+ return db.hubInfo.contextHubModule->send_message(info->hub_id, msg);
+ } else {
+ ALOGD("%s: Hub information is null for hubHandle %d", __FUNCTION__, hubHandle);
+ return -1;
+ }
+}
+
+static int set_os_app_as_destination(hub_message_t *msg, int hubHandle) {
+ const context_hub_t *info = get_hub_info(hubHandle);
+
+ if (info) {
+ msg->app = info->os_app_name;
+ return 0;
+ } else {
+ ALOGD("%s: Hub information is null for hubHandle %d", __FUNCTION__, hubHandle);
+ return -1;
+ }
+}
+
+static int get_hub_id_for_hub_handle(int hubHandle) {
+ if (hubHandle < 0 || hubHandle >= db.hubInfo.numHubs) {
+ return -1;
+ } else {
+ return db.hubInfo.hubs[hubHandle].hub_id;
+ }
+}
+
+static int get_hub_id_for_app_instance(int id) {
+ if (db.appInstances.find(id) == db.appInstances.end()) {
+ ALOGD("%s: Cannot find app for app instance %d", __FUNCTION__, id);
+ return -1;
+ }
+
+ int hubHandle = db.appInstances[id]->hubHandle;
+
+ return db.hubInfo.hubs[hubHandle].hub_id;
+}
+
+static int set_dest_app(hub_message_t *msg, int id) {
+ if (db.appInstances.find(id) == db.appInstances.end()) {
+ ALOGD("%s: Cannod find app for app instance %d", __FUNCTION__, id);
+ return -1;
+ }
+
+ msg->app = db.appInstances[id]->appInfo.name;
+ return 0;
+}
+
+static void send_query_for_apps() {
+ hub_message_t msg;
+
+ msg.message_type = CONTEXT_HUB_QUERY_APPS;
+ msg.message_len = 0;
+
+ for (int i = 0; i < db.hubInfo.numHubs; i++ ) {
+ ALOGD("Sending query for apps to hub %d", i);
+ set_os_app_as_destination(&msg, i);
+ if (send_msg_to_hub(&msg, i) != 0) {
+ ALOGW("Could not query hub %i for apps", i);
+ }
+ }
+}
+
+static int return_id(int id) {
+ // Note : This method is not thread safe.
+ // id returned is guarenteed to be in use
+ db.freeIds.push(id);
+ return 0;
+}
+
+static int generate_id(void) {
+ // Note : This method is not thread safe.
+ int retVal = -1;
+
+ if (!db.freeIds.empty()) {
+ retVal = db.freeIds.front();
+ db.freeIds.pop();
+ }
+
+ return retVal;
+}
+
+int add_app_instance(const hub_app_info *appInfo, uint32_t hubHandle, JNIEnv *env) {
+ // Not checking if the apps are indeed distinct
+
+ app_instance_info_s *entry;
+ void *appName;
+ hub_app_name_t *name;
+
+ assert(appInfo && appInfo->name && appInfo->name->app_name);
+
+ entry = (app_instance_info_s *) malloc(sizeof(app_instance_info_s));
+ appName = malloc(appInfo->name->app_name_len);
+ name = (hub_app_name_t *) malloc(sizeof(hub_app_name_t));
+
+ int appInstanceHandle = generate_id();
+
+ if (appInstanceHandle < 0 || !appName || !entry || !name) {
+ ALOGE("Cannot find resources to add app instance %d, %p, %p",
+ appInstanceHandle, appName, entry);
+
+ free(appName);
+ free(entry);
+ free(name);
+
+ if (appInstanceHandle >= 0) {
+ return_id(appInstanceHandle);
+ }
+
+ return -1;
+ }
+
+ memcpy(&(entry->appInfo), appInfo, sizeof(entry->appInfo));
+ memcpy(appName, appInfo->name->app_name, appInfo->name->app_name_len);
+ name->app_name = appName;
+ name->app_name_len = appInfo->name->app_name_len;
+ entry->appInfo.name = name;
+ entry->truncName = 0;
+ memcpy(&(entry->truncName), name->app_name,
+ sizeof(entry->truncName) < name->app_name_len ?
+ sizeof(entry->truncName) : name->app_name_len);
+
+ // Not checking for sanity of hubId
+ entry->hubHandle = hubHandle;
+ entry->instanceId = appInstanceHandle;
+ db.appInstances[appInstanceHandle] = entry;
+
+ // Finally - let the service know of this app instance
+ env->CallIntMethod(db.jniInfo.jContextHubService,
+ db.jniInfo.contextHubServiceAddAppInstance,
+ hubHandle, entry->instanceId, entry->truncName,
+ entry->appInfo.version);
+
+ ALOGW("Added App 0x%" PRIx64 " on hub Handle %" PRId32
+ " as appInstance %d, original name_length %" PRId32, entry->truncName,
+ entry->hubHandle, appInstanceHandle, name->app_name_len);
+
+ return appInstanceHandle;
+}
+
+int delete_app_instance(int id) {
+ if (db.appInstances.find(id) == db.appInstances.end()) {
+ return -1;
+ }
+
+ return_id(id);
+
+ if (db.appInstances[id]) {
+ // Losing the const cast below. This is intentional.
+ free((void *)db.appInstances[id]->appInfo.name->app_name);
+ free((void *)db.appInstances[id]->appInfo.name);
+ free(db.appInstances[id]);
+ db.appInstances.erase(id);
+ }
+
+ return 0;
+}
+
+
static void initContextHubService() {
int err = 0;
- db.hubInfo.hubs = NULL;
+ db.hubInfo.hubs = nullptr;
db.hubInfo.numHubs = 0;
- db.hubInfo.cookie = 0;
int i;
err = hw_get_module(CONTEXT_HUB_MODULE_ID,
@@ -103,26 +299,45 @@
strerror(-err));
}
- if (db.hubInfo.contextHubModule) {
- ALOGD("Fetching hub info");
- db.hubInfo.numHubs = db.hubInfo.contextHubModule->get_hubs(db.hubInfo.contextHubModule,
- &db.hubInfo.hubs);
+ // Prep for storing app info
+ for(i = MIN_APP_ID; i <= MAX_APP_ID; i++) {
+ db.freeIds.push(i);
+ }
- if (db.hubInfo.numHubs > 0) {
- for (i = 0; i < db.hubInfo.numHubs; i++) {
- // TODO : Event though one cookie is OK for now, lets change
- // this to be one per hub
- db.hubInfo.contextHubModule->subscribe_messages(db.hubInfo.hubs[i].hub_id,
- context_hub_callback,
- &db.hubInfo.cookie);
+ if (db.hubInfo.contextHubModule) {
+ int retNumHubs = db.hubInfo.contextHubModule->get_hubs(db.hubInfo.contextHubModule,
+ &db.hubInfo.hubs);
+ ALOGD("ContextHubModule returned %d hubs ", retNumHubs);
+ db.hubInfo.numHubs = retNumHubs;
+
+ if (db.hubInfo.numHubs > 0) {
+ db.hubInfo.numHubs = retNumHubs;
+ db.hubInfo.cookies = (uint32_t *)malloc(sizeof(uint32_t) * db.hubInfo.numHubs);
+
+ if (!db.hubInfo.cookies) {
+ ALOGW("Ran out of memory allocating cookies, bailing");
+ return;
+ }
+
+ for (i = 0; i < db.hubInfo.numHubs; i++) {
+ db.hubInfo.cookies[i] = db.hubInfo.hubs[i].hub_id;
+ if (db.hubInfo.contextHubModule->subscribe_messages(db.hubInfo.hubs[i].hub_id,
+ context_hub_callback,
+ &db.hubInfo.cookies[i]) == 0) {
+ }
+ }
}
- }
+
+ send_query_for_apps();
+ } else {
+ ALOGW("No Context Hub Module present");
}
}
static int onMessageReceipt(int *header, int headerLen, char *msg, int msgLen) {
JNIEnv *env;
- if ((db.jniInfo.vm)->AttachCurrentThread(&env, NULL) != JNI_OK) {
+
+ if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) {
return -1;
}
@@ -132,28 +347,134 @@
env->SetByteArrayRegion(jmsg, 0, msgLen, (jbyte *)msg);
env->SetIntArrayRegion(jheader, 0, headerLen, (jint *)header);
-
- return env->CallIntMethod(db.jniInfo.jContextHubService,
+ return (env->CallIntMethod(db.jniInfo.jContextHubService,
db.jniInfo.contextHubServiceMsgReceiptCallback,
- jheader, jmsg);
+ jheader, jmsg) != 0);
}
-int context_hub_callback(uint32_t hub_id, const struct hub_message_t *msg,
+int handle_query_apps_response(char *msg, int msgLen, uint32_t hubHandle) {
+ int i;
+ JNIEnv *env;
+ if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) {
+ return -1;
+ }
+
+ int numApps = msgLen/sizeof(hub_app_info);
+ hub_app_info *info = (hub_app_info *)malloc(msgLen); // handle possible alignment
+
+ if (!info) {
+ return -1;
+ }
+
+ memcpy(info, msg, msgLen);
+ for (i = 0; i < numApps; i++) {
+ add_app_instance(info, hubHandle, env);
+ info++;
+ }
+
+ free(info);
+
+ return 0;
+}
+
+
+int handle_os_message(uint32_t msgType, uint32_t hubHandle,
+ char *msg, int msgLen) {
+ int retVal;
+
+ //ALOGD("Rcd OS message from hubHandle %" PRIu32 " type %" PRIu32 " length %d",
+ // hubHandle, msgType, msgLen);
+
+ switch(msgType) {
+ case CONTEXT_HUB_APPS_ENABLE:
+ retVal = 0;
+ break;
+
+ case CONTEXT_HUB_APPS_DISABLE:
+ retVal = 0;
+ break;
+
+ case CONTEXT_HUB_LOAD_APP:
+ retVal = 0;
+ break;
+
+ case CONTEXT_HUB_UNLOAD_APP:
+ retVal = 0;
+ break;
+
+ case CONTEXT_HUB_QUERY_APPS:
+ retVal = handle_query_apps_response(msg, msgLen, hubHandle);
+ break;
+
+ case CONTEXT_HUB_QUERY_MEMORY:
+ retVal = 0;
+ break;
+
+ case CONTEXT_HUB_LOAD_OS:
+ retVal = 0;
+ break;
+
+ default:
+ retVal = -1;
+ break;
+
+ }
+
+ return retVal;
+}
+
+static bool sanity_check_cookie(void *cookie, uint32_t hub_id) {
+ int *ptr = (int *)cookie;
+
+ if (!ptr || *ptr >= db.hubInfo.numHubs) {
+ return false;
+ }
+
+ if (db.hubInfo.hubs[*ptr].hub_id != hub_id) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+int context_hub_callback(uint32_t hubId,
+ const struct hub_message_t *msg,
void *cookie) {
- int msgHeader[4];
+ int msgHeader[MSG_HEADER_SIZE];
- msgHeader[0] = msg->message_type;
- msgHeader[1] = 0; // TODO : HAL does not have a version field
- msgHeader[2] = hub_id;
+ if (!msg) {
+ return -1;
+ }
- onMessageReceipt(msgHeader, sizeof(msgHeader), (char *)msg->message, msg->message_len); // TODO : Populate this
- return 0;
+ msgHeader[HEADER_FIELD_MSG_TYPE] = msg->message_type;
+
+ if (!sanity_check_cookie(cookie, hubId)) {
+ ALOGW("Incorrect cookie %" PRId32 " for cookie %p! Bailing",
+ hubId, cookie);
+
+ return -1;
+ }
+
+ msgHeader[HEADER_FIELD_HUB_HANDLE] = *(uint32_t*)cookie;
+
+ if (msgHeader[HEADER_FIELD_MSG_TYPE] < CONTEXT_HUB_TYPE_PRIVATE_MSG_BASE &&
+ msgHeader[HEADER_FIELD_MSG_TYPE] != 0 ) {
+ handle_os_message(msgHeader[HEADER_FIELD_MSG_TYPE],
+ msgHeader[HEADER_FIELD_HUB_HANDLE],
+ (char *)msg->message,
+ msg->message_len);
+ } else {
+ onMessageReceipt(msgHeader, sizeof(msgHeader),
+ (char *)msg->message, msg->message_len);
+ }
+
+ return 0;
}
static int init_jni(JNIEnv *env, jobject instance) {
if (env->GetJavaVM(&db.jniInfo.vm) != JNI_OK) {
- return -1;
+ return -1;
}
db.jniInfo.jContextHubService = env->NewGlobalRef(instance);
@@ -167,7 +488,6 @@
db.jniInfo.memoryRegionsClass =
env->FindClass("android/hardware/location/MemoryRegion");
- //TODO :: Add error checking
db.jniInfo.contextHubInfoCtor =
env->GetMethodID(db.jniInfo.contextHubInfoClass, "<init>", "()V");
db.jniInfo.contextHubInfoSetId =
@@ -209,6 +529,9 @@
db.jniInfo.contextHubInfoSetMemoryRegions =
env->GetMethodID(db.jniInfo.contextHubInfoClass,
"setMemoryRegions", "([Landroid/hardware/location/MemoryRegion;)V");
+ db.jniInfo.contextHubInfoSetMaxPacketLenBytes =
+ env->GetMethodID(db.jniInfo.contextHubInfoClass,
+ "setMaxPacketLenBytes", "(I)V");
db.jniInfo.contextHubServiceMsgReceiptCallback =
@@ -218,6 +541,11 @@
env->GetMethodID(db.jniInfo.contextHubInfoClass, "setName",
"(Ljava/lang/String;)V");
+ db.jniInfo.contextHubServiceAddAppInstance =
+ env->GetMethodID(db.jniInfo.contextHubServiceClass,
+ "addAppInstance", "(IIJI)I");
+
+
return 0;
}
@@ -245,20 +573,29 @@
env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetPlatformVersion, hub->platform_version);
env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetToolchainVersion, hub->toolchain_version);
env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetPeakMips, hub->peak_mips);
- env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetStoppedPowerDrawMw, hub->stopped_power_draw_mw);
- env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetSleepPowerDrawMw, hub->sleep_power_draw_mw);
- env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetPeakPowerDrawMw, hub->peak_power_draw_mw);
+ env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetStoppedPowerDrawMw,
+ hub->stopped_power_draw_mw);
+ env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetSleepPowerDrawMw,
+ hub->sleep_power_draw_mw);
+ env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetPeakPowerDrawMw,
+ hub->peak_power_draw_mw);
+ env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetMaxPacketLenBytes,
+ hub->max_supported_msg_len);
+
// TODO : jintBuf = env->NewIntArray(hub->num_connected_sensors);
- // TODO : env->SetIntArrayRegion(jintBuf, 0, hub->num_connected_sensors, hub->connected_sensors);
+ // TODO : env->SetIntArrayRegion(jintBuf, 0, hub->num_connected_sensors,
+ // hub->connected_sensors);
jintBuf = env->NewIntArray(array_length(dummyConnectedSensors));
env->SetIntArrayRegion(jintBuf, 0, hub->num_connected_sensors, dummyConnectedSensors);
+ env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetSupportedSensors, jintBuf);
// We are not getting the memory regions from the CH Hal - change this when it is available
- jmemBuf = env->NewObjectArray(0, db.jniInfo.memoryRegionsClass, NULL);
+ jmemBuf = env->NewObjectArray(0, db.jniInfo.memoryRegionsClass, nullptr);
// Note the zero size above. We do not need to set any elements
env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetMemoryRegions, jmemBuf);
+
return jHub;
}
@@ -267,18 +604,18 @@
jobject hub;
jobjectArray retArray;
- initContextHubService();
-
if (init_jni(env, instance) < 0) {
- return NULL;
+ return nullptr;
}
- // Note : The service is clamping the number of hubs to 1
- db.hubInfo.numHubs = 1;
-
initContextHubService();
- retArray = env->NewObjectArray(db.hubInfo.numHubs, db.jniInfo.contextHubInfoClass, NULL);
+ if (db.hubInfo.numHubs > 1) {
+ ALOGW("Clamping the number of hubs to 1");
+ db.hubInfo.numHubs = 1;
+ }
+
+ retArray = env->NewObjectArray(db.hubInfo.numHubs, db.jniInfo.contextHubInfoClass, nullptr);
for(int i = 0; i < db.hubInfo.numHubs; i++) {
hub = constructJContextHubInfo(env, &db.hubInfo.hubs[i]);
@@ -290,29 +627,41 @@
static jint nativeSendMessage(JNIEnv *env, jobject instance, jintArray header_,
jbyteArray data_) {
- hub_message_t msg;
- hub_app_name_t dest;
- uint8_t os_name[8];
-
- memset(os_name, 0, sizeof(os_name));
+ jint retVal = -1; // Default to failure
jint *header = env->GetIntArrayElements(header_, 0);
- //int numHeaderElements = env->GetArrayLength(header_);
+ unsigned int numHeaderElements = env->GetArrayLength(header_);
jbyte *data = env->GetByteArrayElements(data_, 0);
int dataBufferLength = env->GetArrayLength(data_);
- /* Assume an int - thats all we understand */
- dest.app_name_len = array_length(os_name); // TODO : Check this
- //dest.app_name = &header[1];
- dest.app_name = os_name;
- msg.app = &dest;
+ if (numHeaderElements >= MSG_HEADER_SIZE) {
+ bool setAddressSuccess;
+ int hubId;
+ hub_message_t msg;
- msg.message_type = header[3];
- msg.message_len = dataBufferLength;
- msg.message = data;
+ if (header[HEADER_FIELD_APP_INSTANCE] == OS_APP_ID) {
+ setAddressSuccess = (set_os_app_as_destination(&msg, header[HEADER_FIELD_HUB_HANDLE]) == 0);
+ hubId = get_hub_id_for_hub_handle(header[HEADER_FIELD_HUB_HANDLE]);
+ } else {
+ setAddressSuccess = (set_dest_app(&msg, header[HEADER_FIELD_APP_INSTANCE]) == 0);
+ hubId = get_hub_id_for_app_instance(header[HEADER_FIELD_APP_INSTANCE]);
+ }
- jint retVal = db.hubInfo.contextHubModule->send_message(header[0], &msg);
+ if (setAddressSuccess && hubId >= 0) {
+ msg.message_type = header[HEADER_FIELD_MSG_TYPE];
+ msg.message_len = dataBufferLength;
+ msg.message = data;
+ retVal = db.hubInfo.contextHubModule->send_message(hubId, &msg);
+ } else {
+ ALOGD("Could not find app instance %d on hubHandle %d, setAddress %d",
+ header[HEADER_FIELD_APP_INSTANCE],
+ header[HEADER_FIELD_HUB_HANDLE],
+ (int)setAddressSuccess);
+ }
+ } else {
+ ALOGD("Malformed header len");
+ }
env->ReleaseIntArrayElements(header_, header, 0);
env->ReleaseByteArrayElements(data_, data, 0);
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 79b518f..27e2ee8 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -43,6 +43,54 @@
? (reinterpret_cast<RenderNode*>(renderNodePtr)->setPropertyFieldsDirty(dirtyFlag), true) \
: false)
+static JNIEnv* getenv(JavaVM* vm) {
+ JNIEnv* env;
+ if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+ LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", vm);
+ }
+ return env;
+}
+
+static jmethodID gOnRenderNodeDetached;
+
+class RenderNodeContext : public VirtualLightRefBase {
+public:
+ RenderNodeContext(JNIEnv* env, jobject jobjRef) {
+ env->GetJavaVM(&mVm);
+ // This holds a weak ref because otherwise there's a cyclic global ref
+ // with this holding a strong global ref to the view which holds
+ // a strong ref to RenderNode which holds a strong ref to this.
+ mWeakRef = env->NewWeakGlobalRef(jobjRef);
+ }
+
+ virtual ~RenderNodeContext() {
+ JNIEnv* env = getenv(mVm);
+ env->DeleteWeakGlobalRef(mWeakRef);
+ }
+
+ jobject acquireLocalRef(JNIEnv* env) {
+ return env->NewLocalRef(mWeakRef);
+ }
+
+private:
+ JavaVM* mVm;
+ jweak mWeakRef;
+};
+
+// Called by ThreadedRenderer's JNI layer
+void onRenderNodeRemoved(JNIEnv* env, RenderNode* node) {
+ auto context = reinterpret_cast<RenderNodeContext*>(node->getUserContext());
+ if (!context) return;
+ jobject jnode = context->acquireLocalRef(env);
+ if (!jnode) {
+ // The owning node has been GC'd, release the context
+ node->setUserContext(nullptr);
+ return;
+ }
+ env->CallVoidMethod(jnode, gOnRenderNodeDetached);
+ env->DeleteLocalRef(jnode);
+}
+
// ----------------------------------------------------------------------------
// DisplayList view properties
// ----------------------------------------------------------------------------
@@ -59,7 +107,8 @@
return renderNode->getDebugSize();
}
-static jlong android_view_RenderNode_create(JNIEnv* env, jobject clazz, jstring name) {
+static jlong android_view_RenderNode_create(JNIEnv* env, jobject thiz,
+ jstring name) {
RenderNode* renderNode = new RenderNode();
renderNode->incStrong(0);
if (name != NULL) {
@@ -67,6 +116,7 @@
renderNode->setName(textArray);
env->ReleaseStringUTFChars(name, textArray);
}
+ renderNode->setUserContext(new RenderNodeContext(env, thiz));
return reinterpret_cast<jlong>(renderNode);
}
@@ -627,6 +677,9 @@
jclass clazz = FindClassOrDie(env, "android/view/SurfaceView");
gSurfaceViewPositionUpdateMethod = GetMethodIDOrDie(env, clazz,
"updateWindowPositionRT", "(JIIII)V");
+ clazz = FindClassOrDie(env, "android/view/RenderNode");
+ gOnRenderNodeDetached = GetMethodIDOrDie(env, clazz,
+ "onRenderNodeDetached", "()V");
return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
}
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 07868c5..8019326 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -120,7 +120,13 @@
std::string mMessage;
};
-class RootRenderNode : public RenderNode, ErrorHandler {
+// TODO: Clean this up, it's a bit odd to need to call over to
+// rendernode's jni layer. Probably means RootRenderNode should be pulled
+// into HWUI with appropriate callbacks for the various JNI hooks so
+// that RenderNode's JNI layer can handle its own thing
+void onRenderNodeRemoved(JNIEnv* env, RenderNode* node);
+
+class RootRenderNode : public RenderNode, ErrorHandler, TreeObserver {
public:
RootRenderNode(JNIEnv* env) : RenderNode() {
mLooper = Looper::getForThread();
@@ -131,12 +137,15 @@
virtual ~RootRenderNode() {}
- virtual void onError(const std::string& message) {
+ virtual void onError(const std::string& message) override {
mLooper->sendMessage(new RenderingException(mVm, message), 0);
}
- virtual void prepareTree(TreeInfo& info) {
+ virtual void prepareTree(TreeInfo& info) override {
info.errorHandler = this;
+ if (info.mode == TreeInfo::MODE_FULL) {
+ info.observer = this;
+ }
// TODO: This is hacky
info.windowInsetLeft = -stagingProperties().getLeft();
info.windowInsetTop = -stagingProperties().getTop();
@@ -145,7 +154,8 @@
info.updateWindowPositions = false;
info.windowInsetLeft = 0;
info.windowInsetTop = 0;
- info.errorHandler = NULL;
+ info.errorHandler = nullptr;
+ info.observer = nullptr;
}
void sendMessage(const sp<MessageHandler>& handler) {
@@ -171,10 +181,27 @@
mPendingAnimatingRenderNodes.clear();
}
+ virtual void onMaybeRemovedFromTree(RenderNode* node) override {
+ mMaybeRemovedNodes.insert(sp<RenderNode>(node));
+ }
+
+ void processMaybeRemovedNodes(JNIEnv* env) {
+ // We can safely access mMaybeRemovedNodes here because
+ // we will only modify it in prepareTree calls that are
+ // MODE_FULL
+
+ for (auto& node : mMaybeRemovedNodes) {
+ if (node->hasParents()) continue;
+ onRenderNodeRemoved(env, node.get());
+ }
+ mMaybeRemovedNodes.clear();
+ }
+
private:
sp<Looper> mLooper;
JavaVM* mVm;
std::vector< sp<RenderNode> > mPendingAnimatingRenderNodes;
+ std::set< sp<RenderNode> > mMaybeRemovedNodes;
};
class AnimationContextBridge : public AnimationContext {
@@ -473,13 +500,16 @@
}
static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
- jlong proxyPtr, jlongArray frameInfo, jint frameInfoSize) {
+ jlong proxyPtr, jlongArray frameInfo, jint frameInfoSize, jlong rootNodePtr) {
LOG_ALWAYS_FATAL_IF(frameInfoSize != UI_THREAD_FRAME_INFO_SIZE,
"Mismatched size expectations, given %d expected %d",
frameInfoSize, UI_THREAD_FRAME_INFO_SIZE);
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+ RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootNodePtr);
env->GetLongArrayRegion(frameInfo, 0, frameInfoSize, proxy->frameInfo());
- return proxy->syncAndDrawFrame();
+ int ret = proxy->syncAndDrawFrame();
+ rootRenderNode->processMaybeRemovedNodes(env);
+ return ret;
}
static void android_view_ThreadedRenderer_destroy(JNIEnv* env, jobject clazz,
@@ -706,7 +736,7 @@
{ "nSetup", "(JIIFII)V", (void*) android_view_ThreadedRenderer_setup },
{ "nSetLightCenter", "(JFFF)V", (void*) android_view_ThreadedRenderer_setLightCenter },
{ "nSetOpaque", "(JZ)V", (void*) android_view_ThreadedRenderer_setOpaque },
- { "nSyncAndDrawFrame", "(J[JI)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
+ { "nSyncAndDrawFrame", "(J[JIJ)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
{ "nDestroy", "(JJ)V", (void*) android_view_ThreadedRenderer_destroy },
{ "nRegisterAnimatingRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_registerAnimatingRenderNode },
{ "nInvokeFunctor", "(JZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor },
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 60f05448..9a2e39c7 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -348,6 +348,8 @@
<protected-broadcast android:name="android.app.action.DEVICE_OWNER_CHANGED" />
<protected-broadcast android:name="android.intent.action.MANAGED_PROFILE_AVAILABILITY_CHANGED" />
+ <protected-broadcast android:name="android.intent.action.MANAGED_PROFILE_AVAILABLE" />
+ <protected-broadcast android:name="android.intent.action.MANAGED_PROFILE_UNAVAILABLE" />
<!-- Added in N -->
<protected-broadcast android:name="android.intent.action.ANR" />
@@ -399,7 +401,6 @@
<protected-broadcast android:name="android.telephony.action.CARRIER_CONFIG_CHANGED" />
<protected-broadcast android:name="com.android.bluetooth.btservice.action.ALARM_WAKEUP" />
- <protected-broadcast android:name="com.android.ims.IMS_SERVICE_UP" />
<protected-broadcast android:name="com.android.server.action.NETWORK_STATS_POLL" />
<protected-broadcast android:name="com.android.server.action.NETWORK_STATS_UPDATED" />
<protected-broadcast android:name="com.android.server.NetworkTimeUpdateService.action.POLL" />
@@ -459,8 +460,15 @@
<protected-broadcast android:name="android.net.wifi.PASSPOINT_ICON_RECEIVED" />
<protected-broadcast android:name="com.android.server.notification.CountdownConditionProvider" />
- <!-- @hide UCE service Notification -->
+ <protected-broadcast android:name="com.android.ims.IMS_SERVICE_UP" />
+ <protected-broadcast android:name="com.android.ims.IMS_INCOMING_CALL" />
<protected-broadcast android:name="com.android.ims.internal.uce.UCE_SERVICE_UP" />
+ <protected-broadcast android:name="com.android.intent.action.IMS_FEATURE_CHANGED" />
+ <protected-broadcast android:name="com.android.intent.action.IMS_CONFIG_CHANGED" />
+
+ <protected-broadcast android:name="com.android.internal.location.ALARM_WAKEUP" />
+ <protected-broadcast android:name="com.android.internal.location.ALARM_TIMEOUT" />
+ <protected-broadcast android:name="android.intent.action.GLOBAL_BUTTON" />
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
@@ -706,7 +714,9 @@
android:description="@string/permgroupdesc_phone"
android:priority="500" />
- <!-- Allows read only access to phone state.
+ <!-- Allows read only access to phone state, including the phone number of the device,
+ current cellular network information, the status of any ongoing calls, and a list of any
+ {@link android.telecom.PhoneAccount}s registered on the device.
<p class="note"><strong>Note:</strong> If <em>both</em> your <a
href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code
minSdkVersion}</a> and <a
@@ -843,14 +853,14 @@
-->
<permission android:name="android.permission.ACCESS_UCE_PRESENCE_SERVICE"
android:permissionGroup="android.permission-group.PHONE"
- android:protectionLevel="dangerous"/>
+ android:protectionLevel="signatureOrSystem"/>
<!-- @hide Allows an application to Access UCE-OPTIONS.
<p>Protection level: dangerous
-->
<permission android:name="android.permission.ACCESS_UCE_OPTIONS_SERVICE"
android:permissionGroup="android.permission-group.PHONE"
- android:protectionLevel="dangerous"/>
+ android:protectionLevel="signatureOrSystem"/>
@@ -2002,6 +2012,11 @@
<permission android:name="android.permission.GET_ACCOUNTS_PRIVILEGED"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi Allows but does not guarantee access to user passwords at the conclusion of add
+ account -->
+ <permission android:name="android.permission.GET_PASSWORD_PRIVILEGED"
+ android:protectionLevel="signature|privileged" />
+
<!-- @SystemApi Allows applications to RW to diagnostic resources.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.DIAGNOSTIC"
@@ -2170,6 +2185,15 @@
<permission android:name="android.permission.BIND_PRINT_SERVICE"
android:protectionLevel="signature" />
+ <!-- Must be required by a {@link android.printservice.recommendation.RecommendationService},
+ to ensure that only the system can bind to it.
+ @hide
+ @SystemApi
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_PRINT_RECOMMENDATION_SERVICE"
+ android:protectionLevel="signature" />
+
<!-- Must be required by a {@link android.nfc.cardemulation.HostApduService}
or {@link android.nfc.cardemulation.OffHostApduService} to ensure that only
the system can bind to it.
@@ -2973,6 +2997,16 @@
<permission android:name="android.permission.BIND_VR_LISTENER_SERVICE"
android:protectionLevel="signature" />
+ <!-- Required to make calls to {@link android.service.vr.IVrManager}.
+ @hide -->
+ <permission android:name="android.permission.ACCESS_VR_MANAGER"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an application to whitelist tasks during lock task mode
+ @hide <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.UPDATE_LOCK_TASK_PACKAGES"
+ android:protectionLevel="signature|setup" />
+
<application android:process="system"
android:persistent="true"
android:hasCode="false"
@@ -2982,7 +3016,7 @@
android:killAfterRestore="false"
android:icon="@drawable/ic_launcher_android"
android:supportsRtl="true"
- android:theme="@style/Theme.Material.DayNight.DarkActionBar"
+ android:theme="@style/Theme.Material.Light.DarkActionBar"
android:defaultToDeviceProtectedStorage="true"
android:directBootAware="true">
<activity android:name="com.android.internal.app.ChooserActivity"
@@ -3018,7 +3052,7 @@
android:label="@string/managed_profile_label">
</activity-alias>
<activity android:name="com.android.internal.app.HeavyWeightSwitcherActivity"
- android:theme="@style/Theme.Material.DayNight.Dialog"
+ android:theme="@style/Theme.Material.Light.Dialog"
android:label="@string/heavy_weight_switcher_title"
android:finishOnCloseSystemDialogs="true"
android:excludeFromRecents="true"
@@ -3051,7 +3085,7 @@
<activity android:name="android.accounts.ChooseAccountActivity"
android:excludeFromRecents="true"
android:exported="true"
- android:theme="@style/Theme.Material.DayNight.Dialog"
+ android:theme="@style/Theme.Material.Light.Dialog"
android:label="@string/choose_account_label"
android:process=":ui">
</activity>
@@ -3059,14 +3093,14 @@
<activity android:name="android.accounts.ChooseTypeAndAccountActivity"
android:excludeFromRecents="true"
android:exported="true"
- android:theme="@style/Theme.Material.DayNight.Dialog"
+ android:theme="@style/Theme.Material.Light.Dialog"
android:label="@string/choose_account_label"
android:process=":ui">
</activity>
<activity android:name="android.accounts.ChooseAccountTypeActivity"
android:excludeFromRecents="true"
- android:theme="@style/Theme.Material.DayNight.Dialog"
+ android:theme="@style/Theme.Material.Light.Dialog"
android:label="@string/choose_account_label"
android:process=":ui">
</activity>
@@ -3074,19 +3108,19 @@
<activity android:name="android.accounts.CantAddAccountActivity"
android:excludeFromRecents="true"
android:exported="true"
- android:theme="@style/Theme.Material.DayNight.Dialog.NoActionBar"
+ android:theme="@style/Theme.Material.Light.Dialog.NoActionBar"
android:process=":ui">
</activity>
<activity android:name="android.accounts.GrantCredentialsPermissionActivity"
android:excludeFromRecents="true"
android:exported="true"
- android:theme="@style/Theme.Material.DayNight.DialogWhenLarge"
+ android:theme="@style/Theme.Material.Light.DialogWhenLarge"
android:process=":ui">
</activity>
<activity android:name="android.content.SyncActivityTooManyDeletes"
- android:theme="@style/Theme.Material.DayNight.Dialog"
+ android:theme="@style/Theme.Material.Light.Dialog"
android:label="@string/sync_too_many_deletes"
android:process=":ui">
</activity>
@@ -3106,7 +3140,7 @@
</activity>
<activity android:name="com.android.internal.app.NetInitiatedActivity"
- android:theme="@style/Theme.Material.DayNight.Dialog.Alert"
+ android:theme="@style/Theme.Material.Light.Dialog.Alert"
android:excludeFromRecents="true"
android:process=":ui">
</activity>
@@ -3127,7 +3161,7 @@
<activity android:name="com.android.internal.app.ConfirmUserCreationActivity"
android:excludeFromRecents="true"
android:process=":ui"
- android:theme="@style/Theme.Material.DayNight.Dialog.Alert">
+ android:theme="@style/Theme.Material.Light.Dialog.Alert">
<intent-filter android:priority="1000">
<action android:name="android.os.action.CREATE_USER" />
<category android:name="android.intent.category.DEFAULT" />
@@ -3135,7 +3169,7 @@
</activity>
<activity android:name="com.android.internal.app.UnlaunchableAppActivity"
- android:theme="@style/Theme.Material.DayNight.Dialog.Alert"
+ android:theme="@style/Theme.Material.Light.Dialog.Alert"
android:excludeFromRecents="true"
android:process=":ui">
</activity>
diff --git a/core/res/res/anim/slide_in_micro.xml b/core/res/res/anim/slide_in_enter_micro.xml
similarity index 67%
rename from core/res/res/anim/slide_in_micro.xml
rename to core/res/res/anim/slide_in_enter_micro.xml
index 6320e80..c70874c 100644
--- a/core/res/res/anim/slide_in_micro.xml
+++ b/core/res/res/anim/slide_in_enter_micro.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/* //device/apps/common/res/anim/slide_in_micro.xml
**
** Copyright 2014, The Android Open Source Project
**
@@ -18,10 +17,13 @@
*/
-->
-<set xmlns:android="http://schemas.android.com/apk/res/android">
- <translate android:fromXDelta="100%p" android:toXDelta="0"
- android:duration="@android:integer/config_mediumAnimTime"/>
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:zAdjustment="top">
+ <translate android:fromYDelta="5%p" android:toYDelta="0"
+ android:duration="417"
+ android:interpolator="@android:interpolator/launch_task_micro_ydelta" />
<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:duration="@android:integer/config_mediumAnimTime" />
+ android:duration="150"
+ android:interpolator="@android:interpolator/launch_task_micro_alpha" />
</set>
diff --git a/core/java/com/android/internal/app/ProcessStats.aidl b/core/res/res/anim/slide_in_exit_micro.xml
similarity index 60%
copy from core/java/com/android/internal/app/ProcessStats.aidl
copy to core/res/res/anim/slide_in_exit_micro.xml
index 48b1f85..f0d8c0b 100644
--- a/core/java/com/android/internal/app/ProcessStats.aidl
+++ b/core/res/res/anim/slide_in_exit_micro.xml
@@ -1,5 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
/*
-** Copyright 2013, The Android Open Source Project
+** Copyright 2016, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
@@ -13,7 +15,10 @@
** See the License for the specific language governing permissions and
** limitations under the License.
*/
+-->
-package com.android.internal.app;
-
-parcelable ProcessStats;
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:detachWallpaper="true" android:shareInterpolator="false" android:zAdjustment="normal">
+ <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
+ android:duration="417" />
+</set>
diff --git a/core/res/res/anim/slide_out_micro.xml b/core/res/res/anim/slide_out_micro.xml
index 4cb6df0..c647093 100644
--- a/core/res/res/anim/slide_out_micro.xml
+++ b/core/res/res/anim/slide_out_micro.xml
@@ -18,10 +18,13 @@
*/
-->
-<set xmlns:android="http://schemas.android.com/apk/res/android">
- <translate android:fromXDelta="0" android:toXDelta="100%p"
- android:duration="@android:integer/config_mediumAnimTime"/>
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:zAdjustment="top">
+ <translate android:fromYDelta="0" android:toYDelta="5%p"
+ android:duration="250"
+ android:interpolator="@android:interpolator/fast_out_slow_in"/>
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:duration="@android:integer/config_mediumAnimTime" />
+ android:startOffset="100" android:duration="150"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
</set>
diff --git a/core/java/com/android/internal/app/ProcessStats.aidl b/core/res/res/interpolator/launch_task_micro_alpha.xml
similarity index 61%
copy from core/java/com/android/internal/app/ProcessStats.aidl
copy to core/res/res/interpolator/launch_task_micro_alpha.xml
index 48b1f85..41b79b5 100644
--- a/core/java/com/android/internal/app/ProcessStats.aidl
+++ b/core/res/res/interpolator/launch_task_micro_alpha.xml
@@ -1,5 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
/*
-** Copyright 2013, The Android Open Source Project
+** Copyright 2014, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
@@ -13,7 +15,10 @@
** See the License for the specific language governing permissions and
** limitations under the License.
*/
+-->
-package com.android.internal.app;
-
-parcelable ProcessStats;
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:controlX1="0"
+ android:controlY1="0"
+ android:controlX2="0.7"
+ android:controlY2="1" />
diff --git a/core/java/com/android/internal/app/ProcessStats.aidl b/core/res/res/interpolator/launch_task_micro_ydelta.xml
similarity index 61%
copy from core/java/com/android/internal/app/ProcessStats.aidl
copy to core/res/res/interpolator/launch_task_micro_ydelta.xml
index 48b1f85..acac761 100644
--- a/core/java/com/android/internal/app/ProcessStats.aidl
+++ b/core/res/res/interpolator/launch_task_micro_ydelta.xml
@@ -1,5 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
/*
-** Copyright 2013, The Android Open Source Project
+** Copyright 2014, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
@@ -13,7 +15,10 @@
** See the License for the specific language governing permissions and
** limitations under the License.
*/
+-->
-package com.android.internal.app;
-
-parcelable ProcessStats;
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:controlX1="0"
+ android:controlY1="0"
+ android:controlX2="0.4"
+ android:controlY2="1" />
diff --git a/core/res/res/layout-w600dp/preference_list_content_single.xml b/core/res/res/layout-sw600dp/preference_list_content_single.xml
similarity index 88%
rename from core/res/res/layout-w600dp/preference_list_content_single.xml
rename to core/res/res/layout-sw600dp/preference_list_content_single.xml
index d2fa5b9..88b1aa8 100644
--- a/core/res/res/layout-w600dp/preference_list_content_single.xml
+++ b/core/res/res/layout-sw600dp/preference_list_content_single.xml
@@ -33,20 +33,22 @@
style="?attr/preferencePanelStyle"
android:orientation="vertical"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingStart="32dip"
- android:paddingEnd="32dip"
- android:paddingTop="32dip"
- android:paddingBottom="32dip" >
+ android:layout_height="match_parent">
- <ListView android:id="@android:id/list"
+ <ListView
+ android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="1"
android:drawSelectorOnTop="false"
android:cacheColorHint="@android:color/transparent"
android:listPreferredItemHeight="48dp"
- android:scrollbarAlwaysDrawVerticalTrack="true" />
+ android:scrollbarAlwaysDrawVerticalTrack="true"
+ android:paddingStart="32dip"
+ android:paddingEnd="32dip"
+ android:paddingTop="32dip"
+ android:paddingBottom="32dip"
+ android:clipToPadding="false"/>
<FrameLayout android:id="@+id/list_footer"
android:layout_width="match_parent"
diff --git a/core/res/res/layout/alert_dialog_material.xml b/core/res/res/layout/alert_dialog_material.xml
index 9f50937..6d33de6 100644
--- a/core/res/res/layout/alert_dialog_material.xml
+++ b/core/res/res/layout/alert_dialog_material.xml
@@ -34,7 +34,6 @@
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingTop="@dimen/dialog_padding_top_material"
android:clipToPadding="false">
<LinearLayout
@@ -42,6 +41,12 @@
android:layout_height="wrap_content"
android:orientation="vertical">
+ <Space
+ android:id="@+id/textSpacerNoTitle"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/dialog_padding_top_material" />
+
<TextView
android:id="@+id/message"
android:layout_width="match_parent"
@@ -53,7 +58,7 @@
<Space
android:id="@+id/textSpacerNoButtons"
android:visibility="gone"
- android:layout_width="0dp"
+ android:layout_width="match_parent"
android:layout_height="@dimen/dialog_padding_top_material" />
</LinearLayout>
</ScrollView>
diff --git a/core/res/res/layout/alert_dialog_title_material.xml b/core/res/res/layout/alert_dialog_title_material.xml
index 738a637..eef9585 100644
--- a/core/res/res/layout/alert_dialog_title_material.xml
+++ b/core/res/res/layout/alert_dialog_title_material.xml
@@ -21,6 +21,8 @@
android:layout_height="wrap_content"
android:orientation="vertical">
+ <!-- If the client uses a customTitle, it will be added here. -->
+
<LinearLayout
android:id="@+id/title_template"
android:layout_width="match_parent"
@@ -49,5 +51,9 @@
style="?attr/windowTitleStyle" />
</LinearLayout>
- <!-- If the client uses a customTitle, it will be added here. -->
+ <Space
+ android:id="@+id/titleDividerNoCustom"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/dialog_title_divider_material" />
</LinearLayout>
diff --git a/core/res/res/layout/app_anr_dialog.xml b/core/res/res/layout/app_anr_dialog.xml
index 8bef116..5ad0f4c 100644
--- a/core/res/res/layout/app_anr_dialog.xml
+++ b/core/res/res/layout/app_anr_dialog.xml
@@ -19,7 +19,7 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="@dimen/aerr_padding_list_top"
- android:paddingBottom="@dimen/dialog_list_padding_vertical_material">
+ android:paddingBottom="@dimen/aerr_padding_list_bottom">
<Button
android:id="@+id/aerr_close"
diff --git a/core/res/res/layout/app_error_dialog.xml b/core/res/res/layout/app_error_dialog.xml
index b9531f4..7147ea2 100644
--- a/core/res/res/layout/app_error_dialog.xml
+++ b/core/res/res/layout/app_error_dialog.xml
@@ -22,7 +22,7 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="@dimen/aerr_padding_list_top"
- android:paddingBottom="@dimen/dialog_list_padding_vertical_material">
+ android:paddingBottom="@dimen/aerr_padding_list_bottom">
<Button
diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml
index 41726fb..d12c8ba 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -25,63 +25,48 @@
android:maxCollapsedHeightSmall="56dp"
android:id="@id/contentPanel">
- <LinearLayout
+ <RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alwaysShow="true"
android:elevation="8dp"
android:paddingStart="16dp"
android:background="@color/white" >
+ <TextView android:id="@+id/profile_button"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:layout_marginEnd="8dp"
+ android:paddingStart="8dp"
+ android:paddingEnd="8dp"
+ android:visibility="gone"
+ style="?attr/borderlessButtonStyle"
+ android:textAppearance="?attr/textAppearanceButton"
+ android:textColor="@color/material_deep_teal_500"
+ android:gravity="center_vertical"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentRight="true"
+ android:singleLine="true"/>
<ImageView android:id="@+id/title_icon"
android:layout_width="24dp"
android:layout_height="24dp"
- android:layout_gravity="start|center_vertical"
android:layout_marginEnd="16dp"
android:visibility="gone"
- android:scaleType="fitCenter" />
+ android:scaleType="fitCenter"
+ android:layout_below="@id/profile_button"
+ android:layout_alignParentLeft="true"
+ />
<TextView android:id="@+id/title"
- android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_weight="1"
+ android:layout_width="wrap_content"
android:textAppearance="?attr/textAppearanceMedium"
android:textSize="14sp"
android:gravity="start|center_vertical"
android:paddingEnd="?attr/dialogPreferredPadding"
android:paddingTop="12dp"
- android:paddingBottom="12dp" />
- <LinearLayout android:id="@+id/profile_button"
- android:layout_width="wrap_content"
- android:layout_height="48dp"
- android:layout_marginTop="4dp"
- android:layout_marginEnd="4dp"
- android:paddingStart="8dp"
- android:paddingEnd="8dp"
- android:paddingTop="4dp"
- android:paddingBottom="4dp"
- android:focusable="true"
- android:visibility="gone"
- style="?attr/borderlessButtonStyle">
- <ImageView android:id="@+id/icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_gravity="start|center_vertical"
- android:layout_marginStart="4dp"
- android:layout_marginEnd="16dp"
- android:layout_marginTop="12dp"
- android:layout_marginBottom="12dp"
- android:scaleType="fitCenter" />
- <TextView android:id="@id/text1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="start|center_vertical"
- android:layout_marginEnd="16dp"
- android:textAppearance="?attr/textAppearanceButton"
- android:textColor="?attr/textColorPrimary"
- android:minLines="1"
- android:maxLines="1"
- android:ellipsize="marquee" />
- </LinearLayout>
- </LinearLayout>
+ android:paddingBottom="12dp"
+ android:layout_below="@id/profile_button"
+ android:layout_toRightOf="@id/title_icon"/>
+ </RelativeLayout>
<ListView
android:layout_width="match_parent"
diff --git a/core/res/res/layout/notification_material_action_list.xml b/core/res/res/layout/notification_material_action_list.xml
index 30b5a790..ac37c4a 100644
--- a/core/res/res/layout/notification_material_action_list.xml
+++ b/core/res/res/layout/notification_material_action_list.xml
@@ -17,9 +17,7 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/actions_container"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="-16dp"
- android:layout_marginEnd="-16dp">
+ android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/actions"
android:layout_width="match_parent"
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 0bbaa24..992e88e 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -42,7 +42,7 @@
android:singleLine="true"
/>
<TextView
- android:id="@+id/sub_text_divider"
+ android:id="@+id/header_text_divider"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.Material.Notification.Info"
@@ -51,26 +51,7 @@
android:text="@string/notification_header_divider_symbol"
android:visibility="gone"/>
<TextView
- android:id="@+id/header_sub_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="@style/TextAppearance.Material.Notification.Info"
- android:layout_marginStart="2dp"
- android:layout_marginEnd="2dp"
- android:visibility="gone"
- android:singleLine="true"/>
- <TextView
- android:id="@+id/content_info_divider"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="@style/TextAppearance.Material.Notification.Info"
- android:layout_marginStart="2dp"
- android:layout_marginEnd="2dp"
- android:text="@string/notification_header_divider_symbol"
- android:singleLine="true"
- android:visibility="gone"/>
- <TextView
- android:id="@+id/header_content_info"
+ android:id="@+id/header_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.Material.Notification.Info"
diff --git a/core/res/res/layout/notification_template_material_big_base.xml b/core/res/res/layout/notification_template_material_big_base.xml
index d53fb5f..c54fa18 100644
--- a/core/res/res/layout/notification_template_material_big_base.xml
+++ b/core/res/res/layout/notification_template_material_big_base.xml
@@ -60,9 +60,5 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
- <include
- layout="@layout/notification_material_action_list"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- />
+ <include layout="@layout/notification_material_action_list" />
</LinearLayout>
diff --git a/core/res/res/layout/notification_template_material_big_picture.xml b/core/res/res/layout/notification_template_material_big_picture.xml
index 50aec6b..d87b9d9 100644
--- a/core/res/res/layout/notification_template_material_big_picture.xml
+++ b/core/res/res/layout/notification_template_material_big_picture.xml
@@ -27,8 +27,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="top"
- android:paddingStart="@dimen/notification_content_margin_start"
- android:paddingEnd="@dimen/notification_content_margin_end"
android:layout_marginTop="@dimen/notification_content_margin_top"
android:clipToPadding="false"
android:orientation="vertical"
@@ -37,6 +35,8 @@
android:id="@+id/notification_main_column"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/notification_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end"
android:orientation="vertical">
<include layout="@layout/notification_template_part_line1"/>
<include layout="@layout/notification_template_progress"/>
@@ -50,14 +50,15 @@
android:layout_weight="1"
android:layout_marginTop="13dp"
android:layout_marginBottom="16dp"
+ android:layout_marginStart="@dimen/notification_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end"
android:scaleType="centerCrop"
/>
+
<ViewStub android:layout="@layout/notification_material_reply_text"
android:id="@+id/notification_material_reply_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginStart="-16dp"
- android:layout_marginEnd="-16dp"
/>
<include layout="@layout/notification_material_action_list" />
</LinearLayout>
diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml
index fdfefe1..3638b20 100644
--- a/core/res/res/layout/notification_template_material_big_text.xml
+++ b/core/res/res/layout/notification_template_material_big_text.xml
@@ -22,38 +22,45 @@
android:tag="bigText"
>
<include layout="@layout/notification_template_header" />
+
<LinearLayout
- android:id="@+id/notification_main_column"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="top"
- android:paddingStart="@dimen/notification_content_margin_start"
- android:paddingEnd="@dimen/notification_content_margin_end"
- android:layout_marginTop="@dimen/notification_content_margin_top"
- android:clipToPadding="false"
- android:minHeight="@dimen/notification_min_content_height"
- android:orientation="vertical"
- >
- <include layout="@layout/notification_template_part_line1" />
- <include layout="@layout/notification_template_progress" />
- <com.android.internal.widget.ImageFloatingTextView android:id="@+id/big_text"
android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_marginTop="0.5dp"
- android:paddingBottom="@dimen/notification_content_margin_bottom"
- android:textAppearance="@style/TextAppearance.Material.Notification"
- android:singleLine="false"
- android:layout_weight="1"
- android:gravity="top"
- android:visibility="gone"
- />
+ android:layout_height="match_parent"
+ android:layout_gravity="top"
+ android:layout_marginTop="@dimen/notification_content_margin_top"
+ android:clipToPadding="false"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:id="@+id/notification_main_column"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top"
+ android:paddingStart="@dimen/notification_content_margin_start"
+ android:paddingEnd="@dimen/notification_content_margin_end"
+ android:clipToPadding="false"
+ android:minHeight="@dimen/notification_min_content_height"
+ android:orientation="vertical"
+ >
+ <include layout="@layout/notification_template_part_line1" />
+ <include layout="@layout/notification_template_progress" />
+ <com.android.internal.widget.ImageFloatingTextView android:id="@+id/big_text"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_marginTop="0.5dp"
+ android:paddingBottom="@dimen/notification_content_margin_bottom"
+ android:textAppearance="@style/TextAppearance.Material.Notification"
+ android:singleLine="false"
+ android:layout_weight="1"
+ android:gravity="top"
+ android:visibility="gone"
+ />
+ </LinearLayout>
+
<ViewStub android:layout="@layout/notification_material_reply_text"
android:id="@+id/notification_material_reply_container"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="-16dp"
- android:layout_marginEnd="-16dp"
- />
+ android:layout_height="wrap_content" />
<include layout="@layout/notification_material_action_list" />
</LinearLayout>
<include layout="@layout/notification_template_right_icon" />
diff --git a/core/res/res/layout/notification_template_material_inbox.xml b/core/res/res/layout/notification_template_material_inbox.xml
index 86902db..ce9acda 100644
--- a/core/res/res/layout/notification_template_material_inbox.xml
+++ b/core/res/res/layout/notification_template_material_inbox.xml
@@ -23,87 +23,99 @@
>
<include layout="@layout/notification_template_header" />
<LinearLayout
- android:id="@+id/notification_main_column"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="top"
- android:paddingStart="@dimen/notification_content_margin_start"
- android:paddingEnd="@dimen/notification_content_margin_end"
- android:layout_marginTop="@dimen/notification_content_margin_top"
- android:minHeight="@dimen/notification_min_content_height"
- android:clipToPadding="false"
- android:orientation="vertical"
- >
- <include layout="@layout/notification_template_part_line1"
android:layout_width="match_parent"
- android:layout_height="wrap_content" />
- <include layout="@layout/notification_template_progress"
+ android:layout_height="match_parent"
+ android:layout_gravity="top"
+ android:layout_marginTop="@dimen/notification_content_margin_top"
+ android:clipToPadding="false"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:id="@+id/notification_main_column"
android:layout_width="match_parent"
- android:layout_height="15dp"
- android:layout_marginTop="4dp"/>
- <TextView android:id="@+id/inbox_text0"
- android:textAppearance="@style/TextAppearance.Material.Notification"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:visibility="gone"
- android:layout_weight="1"
- />
- <TextView android:id="@+id/inbox_text1"
- android:textAppearance="@style/TextAppearance.Material.Notification"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:visibility="gone"
- android:layout_weight="1"
- />
- <TextView android:id="@+id/inbox_text2"
- android:textAppearance="@style/TextAppearance.Material.Notification"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:visibility="gone"
- android:layout_weight="1"
- />
- <TextView android:id="@+id/inbox_text3"
- android:textAppearance="@style/TextAppearance.Material.Notification"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:visibility="gone"
- android:layout_weight="1"
- />
- <TextView android:id="@+id/inbox_text4"
- android:textAppearance="@style/TextAppearance.Material.Notification"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:visibility="gone"
- android:layout_weight="1"
- />
- <TextView android:id="@+id/inbox_text5"
- android:textAppearance="@style/TextAppearance.Material.Notification"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:visibility="gone"
- android:layout_weight="1"
- />
- <TextView android:id="@+id/inbox_text6"
- android:textAppearance="@style/TextAppearance.Material.Notification"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:visibility="gone"
- android:layout_weight="1"
- />
+ android:layout_height="wrap_content"
+ android:layout_gravity="top"
+ android:paddingStart="@dimen/notification_content_margin_start"
+ android:paddingEnd="@dimen/notification_content_margin_end"
+ android:minHeight="@dimen/notification_min_content_height"
+ android:clipToPadding="false"
+ android:orientation="vertical"
+ >
+ <include layout="@layout/notification_template_part_line1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ <include layout="@layout/notification_template_progress"
+ android:layout_width="match_parent"
+ android:layout_height="15dp"
+ android:layout_marginTop="4dp"/>
+ <TextView android:id="@+id/inbox_text0"
+ android:textAppearance="@style/TextAppearance.Material.Notification"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ <TextView android:id="@+id/inbox_text1"
+ android:textAppearance="@style/TextAppearance.Material.Notification"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ <TextView android:id="@+id/inbox_text2"
+ android:textAppearance="@style/TextAppearance.Material.Notification"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ <TextView android:id="@+id/inbox_text3"
+ android:textAppearance="@style/TextAppearance.Material.Notification"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ <TextView android:id="@+id/inbox_text4"
+ android:textAppearance="@style/TextAppearance.Material.Notification"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ <TextView android:id="@+id/inbox_text5"
+ android:textAppearance="@style/TextAppearance.Material.Notification"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ <TextView android:id="@+id/inbox_text6"
+ android:textAppearance="@style/TextAppearance.Material.Notification"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ </LinearLayout>
+ <ViewStub android:layout="@layout/notification_material_reply_text"
+ android:id="@+id/notification_material_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
<include layout="@layout/notification_material_action_list" />
</LinearLayout>
<include layout="@layout/notification_template_right_icon" />
diff --git a/core/res/res/layout/notification_template_material_media.xml b/core/res/res/layout/notification_template_material_media.xml
index 809e525..ba6d30a 100644
--- a/core/res/res/layout/notification_template_material_media.xml
+++ b/core/res/res/layout/notification_template_material_media.xml
@@ -51,7 +51,7 @@
<LinearLayout
android:id="@+id/media_actions"
android:layout_width="wrap_content"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginStart="10dp"
android:layout_marginBottom="12dp"
diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml
index 00c25e6..fe43e1c 100644
--- a/core/res/res/layout/resolver_list.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -25,56 +25,43 @@
android:maxCollapsedHeightSmall="56dp"
android:id="@id/contentPanel">
- <LinearLayout
+ <RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alwaysShow="true"
android:elevation="8dp"
- android:background="@color/white" >
- <TextView android:id="@+id/title"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:minHeight="56dp"
- android:textAppearance="?attr/textAppearanceMedium"
- android:gravity="start|center_vertical"
- android:paddingStart="?attr/dialogPreferredPadding"
- android:paddingEnd="?attr/dialogPreferredPadding"
- android:paddingTop="8dp"
- android:paddingBottom="8dp" />
- <LinearLayout android:id="@+id/profile_button"
- android:layout_width="wrap_content"
- android:layout_height="48dp"
- android:layout_marginTop="4dp"
- android:layout_marginEnd="4dp"
- android:paddingStart="8dp"
- android:paddingEnd="8dp"
- android:paddingTop="4dp"
- android:paddingBottom="4dp"
- android:focusable="true"
- android:visibility="gone"
- style="?attr/borderlessButtonStyle">
- <ImageView android:id="@+id/icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_gravity="start|center_vertical"
- android:layout_marginStart="4dp"
- android:layout_marginEnd="16dp"
- android:layout_marginTop="12dp"
- android:layout_marginBottom="12dp"
- android:scaleType="fitCenter" />
- <TextView android:id="@id/text1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="start|center_vertical"
- android:layout_marginEnd="16dp"
- android:textAppearance="?attr/textAppearanceButton"
- android:textColor="?attr/textColorPrimary"
- android:minLines="1"
- android:maxLines="1"
- android:ellipsize="marquee" />
- </LinearLayout>
- </LinearLayout>
+ android:background="@color/white">
+
+ <TextView
+ android:id="@+id/profile_button"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:layout_marginEnd="8dp"
+ android:paddingStart="8dp"
+ android:paddingEnd="8dp"
+ android:visibility="gone"
+ style="?attr/borderlessButtonStyle"
+ android:textAppearance="?attr/textAppearanceButton"
+ android:textColor="@color/material_deep_teal_500"
+ android:gravity="center_vertical"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentRight="true"
+ android:singleLine="true" />
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minHeight="56dp"
+ android:textAppearance="?attr/textAppearanceMedium"
+ android:gravity="start|center_vertical"
+ android:paddingStart="?attr/dialogPreferredPadding"
+ android:paddingEnd="?attr/dialogPreferredPadding"
+ android:paddingTop="8dp"
+ android:layout_below="@id/profile_button"
+ android:layout_alignParentLeft="true"
+ android:paddingBottom="8dp" />
+ </RelativeLayout>
<ListView
android:layout_width="match_parent"
@@ -85,21 +72,23 @@
android:background="@color/white"
android:elevation="8dp"
android:nestedScrollingEnabled="true"
+ android:scrollIndicators="top|bottom"
android:divider="@null" />
- <TextView android:id="@+id/empty"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_alwaysShow="true"
- android:text="@string/noApplications"
- android:padding="32dp"
- android:gravity="center"
- android:visibility="gone" />
+ <TextView
+ android:id="@+id/empty"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alwaysShow="true"
+ android:text="@string/noApplications"
+ android:padding="32dp"
+ android:gravity="center"
+ android:visibility="gone" />
<LinearLayout
android:id="@+id/button_bar"
android:visibility="gone"
- style="?android:attr/buttonBarStyle"
+ style="?attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_ignoreOffset="true"
@@ -114,26 +103,30 @@
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:elevation="8dp">
- <Button android:id="@+id/button_once"
- android:layout_width="wrap_content"
- android:layout_gravity="start"
- android:maxLines="2"
- style="?android:attr/buttonBarNegativeButtonStyle"
- android:minHeight="@dimen/alert_dialog_button_bar_height"
- android:layout_height="wrap_content"
- android:enabled="false"
- android:text="@string/activity_resolver_use_once"
- android:onClick="onButtonClick" />
- <Button android:id="@+id/button_always"
- android:layout_width="wrap_content"
- android:layout_gravity="end"
- android:maxLines="2"
- android:minHeight="@dimen/alert_dialog_button_bar_height"
- style="?android:attr/buttonBarPositiveButtonStyle"
- android:layout_height="wrap_content"
- android:enabled="false"
- android:text="@string/activity_resolver_use_always"
- android:onClick="onButtonClick" />
+
+ <Button
+ android:id="@+id/button_once"
+ android:layout_width="wrap_content"
+ android:layout_gravity="start"
+ android:maxLines="2"
+ style="?attr/buttonBarNegativeButtonStyle"
+ android:minHeight="@dimen/alert_dialog_button_bar_height"
+ android:layout_height="wrap_content"
+ android:enabled="false"
+ android:text="@string/activity_resolver_use_once"
+ android:onClick="onButtonClick" />
+
+ <Button
+ android:id="@+id/button_always"
+ android:layout_width="wrap_content"
+ android:layout_gravity="end"
+ android:maxLines="2"
+ android:minHeight="@dimen/alert_dialog_button_bar_height"
+ style="?attr/buttonBarPositiveButtonStyle"
+ android:layout_height="wrap_content"
+ android:enabled="false"
+ android:text="@string/activity_resolver_use_always"
+ android:onClick="onButtonClick" />
</LinearLayout>
</com.android.internal.widget.ResolverDrawerLayout>
diff --git a/core/res/res/layout/resolver_list_with_default.xml b/core/res/res/layout/resolver_list_with_default.xml
index 31361e5..ed7ef5e 100644
--- a/core/res/res/layout/resolver_list_with_default.xml
+++ b/core/res/res/layout/resolver_list_with_default.xml
@@ -22,8 +22,7 @@
android:layout_height="match_parent"
android:maxWidth="@dimen/resolver_max_width"
android:maxCollapsedHeight="144dp"
- android:id="@id/contentPanel"
- >
+ android:id="@id/contentPanel">
<LinearLayout
android:layout_width="match_parent"
@@ -31,66 +30,75 @@
android:layout_alwaysShow="true"
android:orientation="vertical"
android:background="@color/white"
- android:elevation="8dp" >
+ android:elevation="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="64dp"
- android:orientation="horizontal" >
+ android:orientation="horizontal">
- <ImageView android:id="@+id/icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_gravity="start|top"
- android:layout_marginStart="16dp"
- android:layout_marginEnd="16dp"
- android:layout_marginTop="20dp"
- android:scaleType="fitCenter" />
- <TextView android:id="@+id/title"
- android:layout_width="0dp"
- android:layout_weight="1"
- android:layout_height="?android:attr/listPreferredItemHeight"
- android:layout_marginStart="16dp"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:gravity="start|center_vertical"
- android:paddingEnd="16dp" />
- <LinearLayout android:id="@+id/profile_button"
- android:layout_width="wrap_content"
- android:layout_height="48dp"
- android:layout_marginTop="4dp"
- android:layout_marginEnd="4dp"
- android:paddingStart="8dp"
- android:paddingEnd="8dp"
- android:paddingTop="4dp"
- android:paddingBottom="4dp"
- android:focusable="true"
- android:visibility="gone"
- style="?attr/borderlessButtonStyle">
- <ImageView android:id="@+id/icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_gravity="start|center_vertical"
- android:layout_marginEnd="?attr/listPreferredItemPaddingEnd"
- android:layout_marginTop="12dp"
- android:layout_marginBottom="12dp"
- android:scaleType="fitCenter" />
- <TextView android:id="@id/text1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="start|center_vertical"
- android:layout_marginEnd="?attr/listPreferredItemPaddingEnd"
- android:textAppearance="?attr/textAppearanceButton"
- android:textColor="?attr/textColorPrimary"
- android:minLines="1"
- android:maxLines="1"
- android:ellipsize="marquee" />
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_gravity="start|top"
+ android:layout_marginStart="16dp"
+ android:layout_marginEnd="16dp"
+ android:layout_marginTop="20dp"
+ android:scaleType="fitCenter" />
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="?attr/listPreferredItemHeight"
+ android:layout_marginStart="16dp"
+ android:textAppearance="?attr/textAppearanceMedium"
+ android:gravity="start|center_vertical"
+ android:paddingEnd="16dp" />
+
+ <LinearLayout
+ android:id="@+id/profile_button"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:layout_marginTop="4dp"
+ android:layout_marginEnd="4dp"
+ android:paddingStart="8dp"
+ android:paddingEnd="8dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp"
+ android:focusable="true"
+ android:visibility="gone"
+ style="?attr/borderlessButtonStyle">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_gravity="start|center_vertical"
+ android:layout_marginEnd="?attr/listPreferredItemPaddingEnd"
+ android:layout_marginTop="12dp"
+ android:layout_marginBottom="12dp"
+ android:scaleType="fitCenter" />
+
+ <TextView
+ android:id="@id/text1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="start|center_vertical"
+ android:layout_marginEnd="?attr/listPreferredItemPaddingEnd"
+ android:textAppearance="?attr/textAppearanceButton"
+ android:textColor="?attr/textColorPrimary"
+ android:minLines="1"
+ android:maxLines="1"
+ android:ellipsize="marquee" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/button_bar"
android:visibility="gone"
- style="?android:attr/buttonBarStyle"
+ style="?attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alwaysShow="true"
@@ -104,30 +112,36 @@
android:paddingEnd="12dp"
android:background="@color/white"
android:elevation="8dp">
- <Button android:id="@+id/button_once"
- android:layout_width="wrap_content"
- android:layout_gravity="start"
- android:maxLines="2"
- style="?android:attr/buttonBarNegativeButtonStyle"
- android:minHeight="@dimen/alert_dialog_button_bar_height"
- android:layout_height="wrap_content"
- android:enabled="false"
- android:text="@string/activity_resolver_use_once"
- android:onClick="onButtonClick" />
- <Button android:id="@+id/button_always"
- android:layout_width="wrap_content"
- android:layout_gravity="end"
- android:maxLines="2"
- android:minHeight="@dimen/alert_dialog_button_bar_height"
- style="?android:attr/buttonBarPositiveButtonStyle"
- android:layout_height="wrap_content"
- android:enabled="false"
- android:text="@string/activity_resolver_use_always"
- android:onClick="onButtonClick" />
+
+ <Button
+ android:id="@+id/button_once"
+ android:layout_width="wrap_content"
+ android:layout_gravity="start"
+ android:maxLines="2"
+ style="?attr/buttonBarNegativeButtonStyle"
+ android:minHeight="@dimen/alert_dialog_button_bar_height"
+ android:layout_height="wrap_content"
+ android:enabled="false"
+ android:text="@string/activity_resolver_use_once"
+ android:onClick="onButtonClick" />
+
+ <Button
+ android:id="@+id/button_always"
+ android:layout_width="wrap_content"
+ android:layout_gravity="end"
+ android:maxLines="2"
+ android:minHeight="@dimen/alert_dialog_button_bar_height"
+ style="?attr/buttonBarPositiveButtonStyle"
+ android:layout_height="wrap_content"
+ android:enabled="false"
+ android:text="@string/activity_resolver_use_always"
+ android:onClick="onButtonClick" />
</LinearLayout>
- <View android:layout_width="match_parent"
- android:layout_height="1dp"
- android:background="?android:attr/dividerVertical" />
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?attr/dividerVertical" />
</LinearLayout>
<ListView
@@ -140,6 +154,6 @@
android:elevation="8dp"
android:nestedScrollingEnabled="true"
android:divider="@null"
- />
+ android:scrollIndicators="top|bottom" />
</com.android.internal.widget.ResolverDrawerLayout>
diff --git a/core/res/res/layout/select_dialog_material.xml b/core/res/res/layout/select_dialog_material.xml
index 19ad407..9a068f9a 100644
--- a/core/res/res/layout/select_dialog_material.xml
+++ b/core/res/res/layout/select_dialog_material.xml
@@ -30,6 +30,6 @@
android:scrollbars="vertical"
android:overScrollMode="ifContentScrolls"
android:textAlignment="viewStart"
- android:paddingTop="@dimen/dialog_list_padding_vertical_material"
- android:paddingBottom="@dimen/dialog_list_padding_vertical_material"
- android:clipToPadding="false" />
+ android:clipToPadding="false"
+ android:paddingBottomNoButtons="@dimen/dialog_list_padding_bottom_no_buttons"
+ android:paddingTopNoTitle="@dimen/dialog_list_padding_top_no_title" />
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index be140a5..2a16a1e 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Inhoud word versteek volgens beleid"</string>
<string name="safeMode" msgid="2788228061547930246">"Veiligmodus"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android-stelsel"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Persoonlik"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Werk"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Skakel oor na persoonlik"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Skakel oor na werk"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontakte"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"in te gaan by jou kontakte"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Ligging"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Geen toestemmings benodig nie"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"dit kan jou dalk geld kos"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB vir batterylaai"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"USB laai tans hierdie toestel"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB verskaf tans krag aan gekoppelde toestel"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB vir lêeroordrag"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB vir foto-oordrag"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB vir MIDI"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"DEEL"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"WEIER"</string>
<string name="select_input_method" msgid="8547250819326693584">"Verander sleutelbord"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Ander sleutelborde"</string>
<string name="show_ime" msgid="2506087537466597099">"Hou dit op die skerm terwyl fisieke sleutelbord aktief is"</string>
<string name="hardware" msgid="194658061510127999">"Wys virtuele sleutelbord"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Kies sleutelborduitleg"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Raak om \'n sleutelborduitleg te kies."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Stel fisieke sleutelbord op"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Tik om taal en uitleg te kies"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"kandidate"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Meer opsies"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s - %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s-%2$s%3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Interne gedeelde berging"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD-kaart"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g>-SD-kaart"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB-datastokkie"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index a87090a..0668c35 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"ይዘቶች በመመሪያ ተደብቀዋል"</string>
<string name="safeMode" msgid="2788228061547930246">"የሚያስተማምን ሁነታ"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android ስርዓት"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"የግል"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"ስራ"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"ወደ የግል ቀይር"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"ወደ ሥራ ቀይር"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"ዕውቂያዎች"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"የእርስዎ እውቂያዎች ላይ ይድረሱባቸው"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"መገኛ አካባቢ"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"ምንም ፍቃዶች አይጠየቁም"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"ይህ ገንዘብ ሊያስወጣዎት ይችላል"</string>
<string name="dlg_ok" msgid="7376953167039865701">"እሺ"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"ዩኤስቢ ለኃይል መሙላት"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"ዩኤስቢ የዚህን መሣሪያ ኃይል በመሙላት ላይ"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"ዩኤስቢ ለተያያዘው መሣሪያ ኃይል በማቅረብ ላይ"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"ዩኤስቢ ለፋይል ሽግግር"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"ዩኤስቢ ለፎቶ ሽግግር"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"ዩኤስቢ ለMIDI"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"አጋራ"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"አትቀበል"</string>
<string name="select_input_method" msgid="8547250819326693584">"ቁልፍ ሰሌዳ ይቀይሩ"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"ሌሎች ቁልፍ ሰሌዳዎች"</string>
<string name="show_ime" msgid="2506087537466597099">"አካላዊ የቁልፍ ሰሌዳ ገቢር ሆኖ ሳለ በማያ ገጽ ላይ አቆየው"</string>
<string name="hardware" msgid="194658061510127999">"ምናባዊ የቁልፍ ሰሌዳን አሳይ"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"የቁልፍ ሰሌዳ አቀማመጥ ምረጥ"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"የቁልፍ ሰሌዳ አቀማመጥ ለመምረጥ ንካ።"</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"አካላዊ ቁልፍ ሰሌዳን ያዋቅሩ"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"ቋንቋ እና አቀማመጥን ለመምረጥ መታ ያድርጉ"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"ዕጩዎች"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"ተጨማሪ አማራጮች"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s፣ %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s፣ %2$s፣ %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"የውስጥ የተጋራ ማከማቻ"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD ካርድ"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> ኤስዲ ካርድ"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"የዩኤስቢ አንጻፊ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 01d47cf..6a7c76c 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -241,8 +241,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"تم إخفاء المحتويات بواسطة السياسة"</string>
<string name="safeMode" msgid="2788228061547930246">"الوضع الآمن"</string>
<string name="android_system_label" msgid="6577375335728551336">"نظام Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"شخصي"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"عمل"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"التبديل إلى الشخصي"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"التبديل إلى العمل"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"جهات الاتصال"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"الوصول إلى جهات اتصالك"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"الموقع"</string>
@@ -1077,7 +1077,8 @@
<string name="no_permissions" msgid="7283357728219338112">"لا أذونات مطلوبة"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"قد يكلفك هذا مالاً."</string>
<string name="dlg_ok" msgid="7376953167039865701">"موافق"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB للشحن"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"يتم استخدام الاتصال عبر USB لشحن هذا الجهاز"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"يتم استخدام الاتصال عبر USB لإمداد الجهاز المتصل بالطاقة"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB لنقل الملفات"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB لنقل الصور"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB لـ MIDI"</string>
@@ -1092,11 +1093,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"مشاركة"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"رفض"</string>
<string name="select_input_method" msgid="8547250819326693584">"تغيير لوحة المفاتيح"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"لوحات المفاتيح الأخرى"</string>
<string name="show_ime" msgid="2506087537466597099">"استمرار عرضها على الشاشة أثناء نشاط لوحة المفاتيح الفعلية"</string>
<string name="hardware" msgid="194658061510127999">"إظهار لوحة المفاتيح الظاهرية"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"تحديد تخطيط لوحة مفاتيح"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"المس لتحديد تخطيط لوحة مفاتيح."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"تهيئة لوحة المفاتيح الفعلية"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"انقر لاختيار لغة وتنسيق"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" أ ب ت ث ج ح خ د ذ ر ز س ش ص ض ط ظ ع غ ف ق ك ل م ن ه و ي"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789 أ ب ت ث ج ح خ د ذ ر ز س ش ص ض ط ظ ع غ ف ق ك ل م ن ه و ي"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"العناصر المرشحة"</u></string>
@@ -1260,8 +1260,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"المزيد من الخيارات"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s، %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s، %2$s، %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"السعة التخزينية المشتركة الداخلية"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"بطاقة SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"بطاقة SD من <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"محرك أقراص USB"</string>
diff --git a/core/res/res/values-az-rAZ/strings.xml b/core/res/res/values-az-rAZ/strings.xml
index 58d81b2..e76ce87 100644
--- a/core/res/res/values-az-rAZ/strings.xml
+++ b/core/res/res/values-az-rAZ/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Məzmun siyasət tərəfindən gizlədilib"</string>
<string name="safeMode" msgid="2788228061547930246">"Təhlükəsiz rejim"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android sistemi"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Şəxsi"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"İş"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Şəxsi profilə keçirin"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"İş profilinə keçirin"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontaktlar"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"kontaktlarınıza daxil olun"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Yer"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Heç bir icazə tələb olunmur"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"bununla sizdən xərc tutula bilər"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"Enerji doldurmaq üçün USB"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"USB bu cihaza enerji doldurur"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Qolulmuş cihaz üçün USB enerji tədarükü"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"Fayl transferi üçün USB"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"Foto transfer üçün USB"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI üçün USB"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"PAYLAŞIN"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"RƏDD EDİN"</string>
<string name="select_input_method" msgid="8547250819326693584">"Klaviaturanı dəyişin"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Digər klaviaturalar"</string>
<string name="show_ime" msgid="2506087537466597099">"Fiziki klaviatura aktiv olduğu halda ekranda saxlayın"</string>
<string name="hardware" msgid="194658061510127999">"Virtual klaviaturanı göstərin"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Klaviatura sxemi seçin"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Klaviatura tərtibatı seçmək üçün toxunun."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Fiziki klaviaturanı konfiqurasiya edin"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Dil və tərtibatı seçmək üçün tıklayın"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCÇDEƏFGĞHXIİJKQLMNOÖPRSŞTUÜVYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCÇDEƏFGĞHİIJKLMNOÖPQRSŞTUÜVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"namizədlər"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Əlavə seçimlər"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Daxili paylaşılan yaddaş"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD kart"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD kart"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB drayv"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index b8c6fd3..9231e9a 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -235,8 +235,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Sadržaj je sakriven smernicama"</string>
<string name="safeMode" msgid="2788228061547930246">"Bezbedni režim"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android sistem"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Lično"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Posao"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Pređi na Lični profil"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Pređi na profil za Work"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontakti"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"pristupi kontaktima"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Lokacija"</string>
@@ -1053,7 +1053,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Nije potrebna nijedna dozvola"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"ovo će vam možda biti naplaćeno"</string>
<string name="dlg_ok" msgid="7376953167039865701">"Potvrdi"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB za punjenje"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"USB puni ovaj uređaj"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB snabdeva energijom priključeni uređaj"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB za prenos datoteka"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB za prenos slika"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB za MIDI"</string>
@@ -1068,11 +1069,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"DELI"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ODBIJ"</string>
<string name="select_input_method" msgid="8547250819326693584">"Promenite tastaturu"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Druge tastature"</string>
<string name="show_ime" msgid="2506087537466597099">"Zadrži ga na ekranu dok je fizička tastatura aktivna"</string>
<string name="hardware" msgid="194658061510127999">"Prikaži virtuelnu tastaturu"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Izbor rasporeda tastature"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Dodirnite da biste izabrali raspored tastature."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Konfigurišite fizičku tastaturu"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Dodirnite da biste izabrali jezik i raspored"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"kandidati"</u></string>
@@ -1233,8 +1233,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Još opcija"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Unutrašnji deljeni memorijski prostor"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD kartica"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD kartica"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB disk"</string>
diff --git a/core/res/res/values-be-rBY/strings.xml b/core/res/res/values-be-rBY/strings.xml
index 502e1e9..8a23529 100644
--- a/core/res/res/values-be-rBY/strings.xml
+++ b/core/res/res/values-be-rBY/strings.xml
@@ -237,8 +237,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Змесціва, схаванае ў адпаведнасці з палітыкай"</string>
<string name="safeMode" msgid="2788228061547930246">"Бяспечны рэжым"</string>
<string name="android_system_label" msgid="6577375335728551336">"Сістэма Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Асабістыя"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Працоўны"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Пераключыцца на асабісты"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Пераключыцца на працоўны"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Кантакты"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"атрымліваць доступ да вашых кантактаў"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Месцазнаходжанне"</string>
@@ -1061,7 +1061,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Дазволу не патрабуецца"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"за гэта можа спаганяцца плата"</string>
<string name="dlg_ok" msgid="7376953167039865701">"ОК"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB для зарадкі"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Па USB зараджаецца гэта прыладу"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Па USB падачецца сілкаванне падключанай прыладзе"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB для перадачы файлаў"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB для перадачы фота"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB для MIDI"</string>
@@ -1076,11 +1077,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"АБАГУЛІЦЬ"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"АДХІЛІЦЬ"</string>
<string name="select_input_method" msgid="8547250819326693584">"Змяніць клавіятуру"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Іншыя праграмныя клавіятуры"</string>
<string name="show_ime" msgid="2506087537466597099">"Захоўваць яе на экране ў той час, калі фізічная клавіятура актыўная"</string>
<string name="hardware" msgid="194658061510127999">"Паказаць віртуальную клавіятуру"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Выбраць раскладку клавіятуры"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Націсніце, каб выбраць раскладку клавіятуры."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Наладжванне фізічнай клавіятуры"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Дакраніцеся, каб выбраць мову і раскладку"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" АБВГДЕЁЖЗІЙКЛМНОПРСТУЎФХЦЧШ\'ЫЬЭЮЯ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"кандыдат."</u></string>
@@ -1242,8 +1242,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Больш налад"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Унутранае абагуленае сховішча"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD-карта"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"SD-карта <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB-дыск"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 62d0d372..76c7002 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Съдържанието е скрито чрез правило"</string>
<string name="safeMode" msgid="2788228061547930246">"Безопасен режим"</string>
<string name="android_system_label" msgid="6577375335728551336">"Системно от Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Личен"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Служебен"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Превключване към личния потребителски профил"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Превключване към служебния пoтребителски профил"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Контакти"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"има достъп до контактите ви"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Местоположение"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Не се изискват разрешения"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"това може да ви струва пари"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB за зареждане"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"През USB се зарежда това устройство"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"През USB се зарежда свързаното устройство"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB за прехвърляне на файлове"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB за прехвърляне на снимки"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB за MIDI"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"СПОДЕЛЯНЕ"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ОТХВЪРЛЯНЕ"</string>
<string name="select_input_method" msgid="8547250819326693584">"Промяна на клавиатурата"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Други клавиатури"</string>
<string name="show_ime" msgid="2506087537466597099">"Показване на екрана, докато физическата клавиатура е активна"</string>
<string name="hardware" msgid="194658061510127999">"Показване на вирт. клавиатура"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Избиране на клавиатурна подредба"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Докоснете, за да изберете клавиатурна подредба."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Конфигуриране на физическата клавиатура"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Докоснете, за да изберете език и подредба"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"кандидати"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Още опции"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"„%1$s“ – %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"„%1$s“, „%2$s“ – %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Вътрешно споделено хранилище"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD карта"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"SD карта от <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB устройство"</string>
diff --git a/core/res/res/values-bn-rBD/strings.xml b/core/res/res/values-bn-rBD/strings.xml
index cbe8eca..620e532 100644
--- a/core/res/res/values-bn-rBD/strings.xml
+++ b/core/res/res/values-bn-rBD/strings.xml
@@ -28,11 +28,11 @@
<string name="petabyteShort" msgid="5637816680144990219">"PB"</string>
<string name="fileSizeSuffix" msgid="8897567456150907538">"<xliff:g id="NUMBER">%1$s</xliff:g> <xliff:g id="UNIT">%2$s</xliff:g>"</string>
<string name="durationDays" msgid="6652371460511178259">"<xliff:g id="DAYS">%1$d</xliff:g> দিন"</string>
- <string name="durationDayHours" msgid="2713107458736744435">"<xliff:g id="DAYS">%1$d</xliff:g> দিন <xliff:g id="HOURS">%2$d</xliff:g> ঘন্টা"</string>
- <string name="durationDayHour" msgid="7293789639090958917">"<xliff:g id="DAYS">%1$d</xliff:g> দিন <xliff:g id="HOURS">%2$d</xliff:g> ঘন্টা"</string>
- <string name="durationHours" msgid="4266858287167358988">"<xliff:g id="HOURS">%1$d</xliff:g> ঘন্টা"</string>
- <string name="durationHourMinutes" msgid="9029176248692041549">"<xliff:g id="HOURS">%1$d</xliff:g> ঘন্টা <xliff:g id="MINUTES">%2$d</xliff:g> মিনিট"</string>
- <string name="durationHourMinute" msgid="2741677355177402539">"<xliff:g id="HOURS">%1$d</xliff:g> ঘন্টা <xliff:g id="MINUTES">%2$d</xliff:g> মিনিট"</string>
+ <string name="durationDayHours" msgid="2713107458736744435">"<xliff:g id="DAYS">%1$d</xliff:g> দিন <xliff:g id="HOURS">%2$d</xliff:g> ঘণ্টা"</string>
+ <string name="durationDayHour" msgid="7293789639090958917">"<xliff:g id="DAYS">%1$d</xliff:g> দিন <xliff:g id="HOURS">%2$d</xliff:g> ঘণ্টা"</string>
+ <string name="durationHours" msgid="4266858287167358988">"<xliff:g id="HOURS">%1$d</xliff:g> ঘণ্টা"</string>
+ <string name="durationHourMinutes" msgid="9029176248692041549">"<xliff:g id="HOURS">%1$d</xliff:g> ঘণ্টা <xliff:g id="MINUTES">%2$d</xliff:g> মিনিট"</string>
+ <string name="durationHourMinute" msgid="2741677355177402539">"<xliff:g id="HOURS">%1$d</xliff:g> ঘণ্টা <xliff:g id="MINUTES">%2$d</xliff:g> মিনিট"</string>
<string name="durationMinutes" msgid="3134226679883579347">"<xliff:g id="MINUTES">%1$d</xliff:g> মিনিট"</string>
<string name="durationMinute" msgid="7155301744174623818">"<xliff:g id="MINUTES">%1$d</xliff:g> মিনিট"</string>
<string name="durationMinuteSeconds" msgid="1424656185379003751">"<xliff:g id="MINUTES">%1$d</xliff:g> মিনিট <xliff:g id="SECONDS">%2$d</xliff:g> সেকেন্ড"</string>
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"নীতির কারণে সামগ্রী লুকানো আছে"</string>
<string name="safeMode" msgid="2788228061547930246">"নিরাপদ মোড"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android সিস্টেম"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"ব্যক্তিগত"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"কর্মক্ষেত্র"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"ব্যক্তিগততে পাল্টান"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"কর্মস্থানে পাল্টান"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"পরিচিতি"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"আপনার পরিচিতিগুলিতে অ্যাক্সেস করুন"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"অবস্থান"</string>
@@ -829,8 +829,8 @@
<string name="preposition_for_year" msgid="5040395640711867177">"<xliff:g id="YEAR">%s</xliff:g> এ"</string>
<string name="day" msgid="8144195776058119424">"দিন"</string>
<string name="days" msgid="4774547661021344602">"দিন"</string>
- <string name="hour" msgid="2126771916426189481">"ঘন্টা"</string>
- <string name="hours" msgid="894424005266852993">"ঘন্টা"</string>
+ <string name="hour" msgid="2126771916426189481">"ঘণ্টা"</string>
+ <string name="hours" msgid="894424005266852993">"ঘণ্টা"</string>
<string name="minute" msgid="9148878657703769868">"মি"</string>
<string name="minutes" msgid="5646001005827034509">"মিনিট"</string>
<string name="second" msgid="3184235808021478">"সেকেন্ড"</string>
@@ -848,8 +848,8 @@
<item quantity="other"><xliff:g id="COUNT">%d</xliff:g> মিনিট</item>
</plurals>
<plurals name="duration_hours" formatted="false" msgid="6826233369186668274">
- <item quantity="one"><xliff:g id="COUNT">%d</xliff:g> ঘন্টা</item>
- <item quantity="other"><xliff:g id="COUNT">%d</xliff:g> ঘন্টা</item>
+ <item quantity="one"><xliff:g id="COUNT">%d</xliff:g> ঘণ্টা</item>
+ <item quantity="other"><xliff:g id="COUNT">%d</xliff:g> ঘণ্টা</item>
</plurals>
<string name="VideoView_error_title" msgid="3534509135438353077">"ভিডিও সমস্যা"</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"এই ভিডিওটি এই ডিভাইসে স্ট্রিমিং করার জন্য বৈধ নয়৷"</string>
@@ -869,7 +869,7 @@
<string name="paste_as_plain_text" msgid="5427792741908010675">"প্লেইন টেক্সট হিসাবে আটকান"</string>
<string name="replace" msgid="5781686059063148930">"প্রতিস্থাপন করুন..."</string>
<string name="delete" msgid="6098684844021697789">"মুছুন"</string>
- <string name="copyUrl" msgid="2538211579596067402">"URL অনুলিপি করুন"</string>
+ <string name="copyUrl" msgid="2538211579596067402">"URL কপি করুন"</string>
<string name="selectTextMode" msgid="1018691815143165326">"পাঠ্য নির্বাচন করুন"</string>
<string name="undo" msgid="7905788502491742328">"পূর্বাবস্থায় ফিরুন"</string>
<string name="redo" msgid="7759464876566803888">"পুনরায় করুন"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"কোনো অনুমতির প্রয়োজন নেই"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"এর জন্য অর্থপ্রদান করতে হতে পারে"</string>
<string name="dlg_ok" msgid="7376953167039865701">"ঠিক আছে"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"চার্জ করার জন্য USB"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"এই ডিভাইসটি USB দ্বারা চার্জ করা হচ্ছে"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"সংযুক্ত ডিভাইসটিতে USB পাওয়ার সরবরাহ করছে"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"ফাইল স্থানান্তরের জন্য USB"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"ফটো স্থানান্তরের জন্য USB"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI এর জন্য USB"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"শেয়ার করুন"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"অস্বীকার করুন"</string>
<string name="select_input_method" msgid="8547250819326693584">"কীবোর্ড পরিবর্তন করুন"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"অন্যান্য কীবোর্ড"</string>
<string name="show_ime" msgid="2506087537466597099">"ফিজিক্যাল কীবোর্ড সক্রিয় থাকার সময় এটিকে স্ক্রীনে রাখুন"</string>
<string name="hardware" msgid="194658061510127999">"ভার্চুয়াল কীবোর্ড দেখান"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"কীবোর্ডের লেআউট নির্বাচন করুন"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"একটি কীবোর্ডের লেআউট নির্বাচন করতে স্পর্শ করুন৷"</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"ফিজিক্যাল কীবোর্ড কনফিগার করুন"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"ভাষা এবং লেআউট নির্বাচন করুন আলতো চাপ দিন"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"প্রার্থীরা"</u></string>
@@ -1192,8 +1192,8 @@
<string name="number_picker_increment_scroll_action" msgid="9101473045891835490">"বাড়ানোর জন্য উপরের দিকে এবং কমানোর জন্য নীচের দিকে স্লাইড করুন৷"</string>
<string name="time_picker_increment_minute_button" msgid="8865885114028614321">"মিনিট বাড়ান"</string>
<string name="time_picker_decrement_minute_button" msgid="6246834937080684791">"মিনিট কমান"</string>
- <string name="time_picker_increment_hour_button" msgid="3652056055810223139">"ঘন্টা বাড়ান"</string>
- <string name="time_picker_decrement_hour_button" msgid="1377479863429214792">"ঘন্টা কমান"</string>
+ <string name="time_picker_increment_hour_button" msgid="3652056055810223139">"ঘণ্টা বাড়ান"</string>
+ <string name="time_picker_decrement_hour_button" msgid="1377479863429214792">"ঘণ্টা কমান"</string>
<string name="time_picker_increment_set_pm_button" msgid="4147590696151230863">"PM সেট করুন"</string>
<string name="time_picker_decrement_set_am_button" msgid="8302140353539486752">"AM সেট করুন"</string>
<string name="date_picker_increment_month_button" msgid="5369998479067934110">"মাস বাড়ান"</string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"আরো বিকল্প"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"অভ্যন্তরীণ শেয়ার করা সঞ্চয়স্থান"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD কার্ড"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD কার্ড"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB ড্রাইভ"</string>
@@ -1453,9 +1452,9 @@
<string name="immersive_cling_description" msgid="3482371193207536040">"প্রস্থান করতে উপর থেকে নীচের দিকে সোয়াইপ করুন"</string>
<string name="immersive_cling_positive" msgid="5016839404568297683">"বুঝেছি"</string>
<string name="done_label" msgid="2093726099505892398">"সম্পন্ন হয়েছে"</string>
- <string name="hour_picker_description" msgid="6698199186859736512">"বৃত্তাকার ঘন্টা নির্বাচকের স্লাইডার"</string>
+ <string name="hour_picker_description" msgid="6698199186859736512">"বৃত্তাকার ঘণ্টা নির্বাচকের স্লাইডার"</string>
<string name="minute_picker_description" msgid="8606010966873791190">"বৃত্তাকার মিনিট নির্বাচকের স্লাইডার"</string>
- <string name="select_hours" msgid="6043079511766008245">"ঘন্টা নির্বাচন করুন"</string>
+ <string name="select_hours" msgid="6043079511766008245">"ঘণ্টা নির্বাচন করুন"</string>
<string name="select_minutes" msgid="3974345615920336087">"মিনিট নির্বাচন করুন"</string>
<string name="select_day" msgid="7774759604701773332">"মাস এবং দিন নির্বাচন করুন"</string>
<string name="select_year" msgid="7952052866994196170">"বছর নির্বাচন করুন"</string>
diff --git a/core/res/res/values-bs-rBA/strings.xml b/core/res/res/values-bs-rBA/strings.xml
index 6e7d8d2..82639af 100644
--- a/core/res/res/values-bs-rBA/strings.xml
+++ b/core/res/res/values-bs-rBA/strings.xml
@@ -235,8 +235,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Sadržaj skriven u skladu sa pravilima"</string>
<string name="safeMode" msgid="2788228061547930246">"Siguran način rada"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android sistem"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Lično"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Poslovni"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Prebacite se na lični"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Prebacite se na radni"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontakti"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"pristupa vašim kontaktima"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Lokacija"</string>
@@ -1053,7 +1053,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Nisu potrebne dozvole"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"ovo se možda dodatno plaća"</string>
<string name="dlg_ok" msgid="7376953167039865701">"Uredu"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB za punjenje"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Ovaj uređaj se puni preko USB-a"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB napaja priključeni uređaj"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB za prijenos fajlova"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB za prijenos slika"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB za MIDI"</string>
@@ -1068,11 +1069,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"PODIJELI"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ODBACI"</string>
<string name="select_input_method" msgid="8547250819326693584">"Promijeni tastaturu"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Ostale tastature"</string>
<string name="show_ime" msgid="2506087537466597099">"Prikaži na ekranu dok je fizička tastatura aktivna"</string>
<string name="hardware" msgid="194658061510127999">"Prikaži virtualnu tastaturu"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Odaberite raspored tastature"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Dodirnite za odabir rasporeda tastature."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Konfiguriraj fizičku tastaturu"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Dodirnite za odabir jezika i rasporeda"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"kandidati"</u></string>
@@ -1233,8 +1233,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Više opcija"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Interno dijeljena pohrana"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD kartica"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD kartica"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB disk"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 9044a1a..6fcadfa 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Contingut amagat de conformitat amb la política"</string>
<string name="safeMode" msgid="2788228061547930246">"Mode segur"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Personal"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Feina"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Canvia al perfil personal"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Canvia al perfil professional"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Contactes"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"accedir als contactes"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Ubicació"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"No cal cap permís"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"pot ser que comporti càrrecs"</string>
<string name="dlg_ok" msgid="7376953167039865701">"D\'acord"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB per carregar"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"L\'USB està carregant el dispositiu"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"L\'USB subministra energia al dispositiu connectat"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB per transferir fitxers"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB per transferir fotos"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB per a MIDI"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"COMPARTEIX"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"REBUTJA"</string>
<string name="select_input_method" msgid="8547250819326693584">"Canvia el teclat"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Altres teclats"</string>
<string name="show_ime" msgid="2506087537466597099">"El deixa a la pantalla mentre el teclat físic està actiu"</string>
<string name="hardware" msgid="194658061510127999">"Mostra el teclat virtual"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Selecciona una disposició de teclat"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Toca per seleccionar una disposició de teclat."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Configura el teclat físic"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Toca per seleccionar l\'idioma i el disseny"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"candidats"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Més opcions"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Emmagatzematge intern compartit"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Targeta SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Targeta SD de: <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Unitat USB"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 237d44d..127d38b 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -237,8 +237,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Obsah skrytý zásadami"</string>
<string name="safeMode" msgid="2788228061547930246">"Nouzový režim"</string>
<string name="android_system_label" msgid="6577375335728551336">"Systém Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Osobní"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Práce"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Přepnout na osobní profil"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Přepnout na pracovní profil"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontakty"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"přístup ke kontaktům"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Poloha"</string>
@@ -1061,7 +1061,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Nejsou vyžadována žádná oprávnění"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"může vás to něco stát"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB na nabíjení"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Nabíjení zařízení přes USB"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Připojené zařízení se nabíjí přes USB"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB na přenos souborů"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB na přenos fotek"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB v režimu MIDI"</string>
@@ -1076,11 +1077,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"SDÍLET"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ODMÍTNOUT"</string>
<string name="select_input_method" msgid="8547250819326693584">"Změna klávesnice"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Další klávesnice"</string>
<string name="show_ime" msgid="2506087537466597099">"Ponechat na obrazovce, když je aktivní fyzická klávesnice"</string>
<string name="hardware" msgid="194658061510127999">"Zobrazit virtuální klávesnici"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Výběr rozložení klávesnice"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Dotykem vyberte rozložení klávesnice."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Konfigurace fyzické klávesnice"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Klepnutím vyberte jazyk a rozvržení"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" AÁBCČDĎEÉĚFGHCHIÍJKLMNŇOÓPQRŘSŠTŤUÚVWXYÝZŽ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789AÁBCČDĎEÉĚFGHCHIÍJKLMNŇOÓPQRŘSŠTŤUÚVWXYÝZŽ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"kandidáti"</u></string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 8875ead..407f1f8 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Indholdet er skjult af politikken"</string>
<string name="safeMode" msgid="2788228061547930246">"Sikker tilstand"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android-system"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Personlig"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Arbejde"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Skift til Tilpasset"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Skift til arbejde"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontaktpersoner"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"have adgang til dine kontaktpersoner"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Placering"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Der kræves ingen tilladelser"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"dette kan koste dig penge"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB til opladning"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"USB, der oplader denne enhed"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB, der leverer strøm til den tilsluttede enhed"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB til filoverførsel"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB til billedoverførsel"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB til MIDI"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"DEL"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"AFVIS"</string>
<string name="select_input_method" msgid="8547250819326693584">"Skift tastatur"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Andre tastaturer"</string>
<string name="show_ime" msgid="2506087537466597099">"Behold den på skærmen, mens det fysiske tastatur er aktivt"</string>
<string name="hardware" msgid="194658061510127999">"Vis virtuelt tastatur"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Vælg tastaturlayout"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Tryk for at vælge et tastaturlayout."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Konfigurer fysisk tastatur"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Tryk for at vælge sprog og layout"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"kandidater"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Flere valgmuligheder"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Intern delt lagerplads"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD-kort"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"SD-kort fra <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB-drev"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index a1183e9..e539a28 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Inhalte aufgrund der Richtlinien ausgeblendet"</string>
<string name="safeMode" msgid="2788228061547930246">"Abgesicherter Modus"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android-System"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Privat"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Geschäftlich"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Zu \"Privat\" wechseln"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Zu \"Arbeit\" wechseln"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontakte"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"auf Kontakte zuzugreifen"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Standort"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Keine Berechtigungen erforderlich"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"Hierfür können Gebühren anfallen."</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB zum Aufladen"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Gerät wird über USB aufgeladen"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Angeschlossenes Gerät wird über USB aufgeladen"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB für die Dateiübertragung"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB für die Fotoübertragung"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB für MIDI"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"TEILEN"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ABLEHNEN"</string>
<string name="select_input_method" msgid="8547250819326693584">"Tastatur ändern"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Weitere Tastaturen"</string>
<string name="show_ime" msgid="2506087537466597099">"Auf dem Display einblenden, wenn die physische Tastatur aktiv ist"</string>
<string name="hardware" msgid="194658061510127999">"Virtuelle Tastatur einblenden"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Tastaturlayout auswählen"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Zum Auswählen eines Tastaturlayouts berühren"</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Physische Tastatur konfigurieren"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Zum Auswählen von Sprache und Layout tippen"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"Kandidaten"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Weitere Optionen"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s. %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s. %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Interner gemeinsamer Speicher"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD-Karte"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"SD-Karte von <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB-Speichergerät"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 4e5f8fb..1e891d0 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Το περιεχόμενο είναι κρυφό βάσει πολιτικής"</string>
<string name="safeMode" msgid="2788228061547930246">"Ασφαλής λειτουργία"</string>
<string name="android_system_label" msgid="6577375335728551336">"Σύστημα Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Προσωπικό"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Εργασία"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Μετάβαση σε προσωπικό προφίλ"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Μετάβαση σε προφίλ εργασίας"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Επαφές"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"πρόσβαση στις επαφές σας"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Τοποθεσία"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Δεν απαιτούνται άδειες"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"ενδέχεται να χρεωθείτε"</string>
<string name="dlg_ok" msgid="7376953167039865701">"ΟΚ"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB για φόρτιση"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Αυτή η συσκευή φορτίζεται μέσω USB"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Η συνδεδεμένη συσκευή τροφοδοτείται μέσω USB"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB για μεταφορά αρχείων"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB για μεταφορά φωτογραφιών"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB για MIDI"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"ΚΟΙΝΟΠΟΙΗΣΗ"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ΑΠΟΡΡΙΨΗ"</string>
<string name="select_input_method" msgid="8547250819326693584">"Αλλαγή πληκτρολογίου"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Άλλα πληκτρολόγια"</string>
<string name="show_ime" msgid="2506087537466597099">"Να παραμένει στην οθόνη όταν είναι ενεργό το φυσικό πληκτρολόγιο"</string>
<string name="hardware" msgid="194658061510127999">"Εμφάνιση εικονικού πληκτρολ."</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Επιλογή διάταξης πληκτρολογίου"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Αγγίξτε για να επιλέξετε διάταξη πληκτρολογίου."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Διαμόρφωση φυσικού πληκτρολογίου"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Πατήστε για να επιλέξετε γλώσσα και διάταξη"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"υποψήφιοι"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Περισσότερες επιλογές"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Εσωτερικός κοινόχρηστος αποθηκευτικός χώρος"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Κάρτα SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Κάρτα SD <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Μονάδα USB"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 335f1f0..b94222c 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Contents hidden by policy"</string>
<string name="safeMode" msgid="2788228061547930246">"Safe mode"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android system"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Personal"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Work"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Switch to Personal"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Switch to Work"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Contacts"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"access your contacts"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Location"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"No permission required"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"this may cost you money"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB for charging"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"USB charging this device"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB supplying power to attached device"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB for file transfer"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB for photo transfer"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB for MIDI"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"SHARE"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"DECLINE"</string>
<string name="select_input_method" msgid="8547250819326693584">"Change keyboard"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Other keyboards"</string>
<string name="show_ime" msgid="2506087537466597099">"Keep it on screen while physical keyboard is active"</string>
<string name="hardware" msgid="194658061510127999">"Show virtual keyboard"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Select keyboard layout"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Touch to select a keyboard layout."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Configure physical keyboard"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Tap to select language and layout"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"candidates"</u></string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 335f1f0..b94222c 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Contents hidden by policy"</string>
<string name="safeMode" msgid="2788228061547930246">"Safe mode"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android system"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Personal"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Work"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Switch to Personal"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Switch to Work"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Contacts"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"access your contacts"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Location"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"No permission required"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"this may cost you money"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB for charging"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"USB charging this device"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB supplying power to attached device"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB for file transfer"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB for photo transfer"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB for MIDI"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"SHARE"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"DECLINE"</string>
<string name="select_input_method" msgid="8547250819326693584">"Change keyboard"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Other keyboards"</string>
<string name="show_ime" msgid="2506087537466597099">"Keep it on screen while physical keyboard is active"</string>
<string name="hardware" msgid="194658061510127999">"Show virtual keyboard"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Select keyboard layout"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Touch to select a keyboard layout."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Configure physical keyboard"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Tap to select language and layout"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"candidates"</u></string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 335f1f0..b94222c 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Contents hidden by policy"</string>
<string name="safeMode" msgid="2788228061547930246">"Safe mode"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android system"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Personal"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Work"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Switch to Personal"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Switch to Work"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Contacts"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"access your contacts"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Location"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"No permission required"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"this may cost you money"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB for charging"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"USB charging this device"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB supplying power to attached device"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB for file transfer"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB for photo transfer"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB for MIDI"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"SHARE"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"DECLINE"</string>
<string name="select_input_method" msgid="8547250819326693584">"Change keyboard"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Other keyboards"</string>
<string name="show_ime" msgid="2506087537466597099">"Keep it on screen while physical keyboard is active"</string>
<string name="hardware" msgid="194658061510127999">"Show virtual keyboard"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Select keyboard layout"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Touch to select a keyboard layout."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Configure physical keyboard"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Tap to select language and layout"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"candidates"</u></string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 2484fe5..0bfb9e0 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Contenido oculto debido a la política"</string>
<string name="safeMode" msgid="2788228061547930246">"Modo seguro"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Personal"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Trabajo"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Cambiar al perfil personal"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Cambiar al perfil de trabajo"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Contactos"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"acceder a los contactos"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Ubicación"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"No se requieren permisos"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"esto puede costarte dinero"</string>
<string name="dlg_ok" msgid="7376953167039865701">"Aceptar"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB solo para realizar cargas"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Este dispositivo se está cargando mediante USB"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"El dispositivo conectado se está cargando mediante USB"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB para transferir archivos"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB para transferir fotos"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB para MIDI"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"COMPARTIR"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"RECHAZAR"</string>
<string name="select_input_method" msgid="8547250819326693584">"Cambiar el teclado"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Otros teclados"</string>
<string name="show_ime" msgid="2506087537466597099">"Mantener en la pantalla cuando el teclado físico está activo"</string>
<string name="hardware" msgid="194658061510127999">"Mostrar teclado virtual"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Selecciona un diseño de teclado"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Toca para seleccionar un diseño de teclado."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Configura el teclado físico"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Presiona para seleccionar el idioma y el diseño"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"candidatos"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Más opciones"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Almacenamiento interno compartido"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Tarjeta SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Tarjeta SD de <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Unidad USB"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 6521cca..99202ab 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Contenidos ocultos por política"</string>
<string name="safeMode" msgid="2788228061547930246">"Modo seguro"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Personal"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Trabajo"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Cambiar a perfil personal"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Cambiar a perfil de trabajo"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Contactos"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"acceder a tus contactos"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Ubicación"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"No es necesario ningún permiso"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"es posible que esto te cueste dinero"</string>
<string name="dlg_ok" msgid="7376953167039865701">"Aceptar"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB para cargar"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Cargando este dispositivo por USB"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"El dispositivo conectado recibe la alimentación por USB"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB para transferir archivos"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB para transferir fotos"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB para MIDI"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"COMPARTIR"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"RECHAZAR"</string>
<string name="select_input_method" msgid="8547250819326693584">"Cambiar teclado"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Otros teclados"</string>
<string name="show_ime" msgid="2506087537466597099">"Debe seguir en pantalla mientras el teclado físico esté activo"</string>
<string name="hardware" msgid="194658061510127999">"Mostrar teclado virtual"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Selecciona un diseño de teclado"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Toca para seleccionar un diseño de teclado."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Configura el teclado físico"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Toca para seleccionar el idioma y el diseño"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"candidatos"</u></string>
diff --git a/core/res/res/values-et-rEE/strings.xml b/core/res/res/values-et-rEE/strings.xml
index 3905a55..b1cc971 100644
--- a/core/res/res/values-et-rEE/strings.xml
+++ b/core/res/res/values-et-rEE/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Sisu on eeskirjadega peidetud"</string>
<string name="safeMode" msgid="2788228061547930246">"Turvarežiim"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android-süsteem"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Isiklik"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Töö"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Lülita isiklikule profiilile"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Lülita tööprofiilile"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontaktid"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"juurdepääs kontaktidele"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Asukoht"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Lube pole vaja"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"see võib olla tasuline"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB laadimiseks"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Laadimiseks ühendage USB"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Ühendatud seade saab toidet USB kaudu"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB failide edastamiseks"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB fotode edastamiseks"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB MIDI jaoks"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"JAGA"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"KEELDU"</string>
<string name="select_input_method" msgid="8547250819326693584">"Klaviatuuri muutmine"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Muud klaviatuurid"</string>
<string name="show_ime" msgid="2506087537466597099">"Hoia seda ekraanil, kui füüsiline klaviatuur on aktiivne"</string>
<string name="hardware" msgid="194658061510127999">"Virtuaalse klaviatuuri kuvam."</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Klaviatuuri paigutuse valimine"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Puudutage klaviatuuri paigutuse valimiseks."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Füüsilise klaviatuuri seadistamine"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Puudutage keele ja paigutuse valimiseks"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSŠZŽTUVWÕÄÖÜXY"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSŠZŽTUVWÕÄÖÜXY"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"kandidaadid"</u></string>
diff --git a/core/res/res/values-eu-rES/strings.xml b/core/res/res/values-eu-rES/strings.xml
index ed5db6c..ea666d9 100644
--- a/core/res/res/values-eu-rES/strings.xml
+++ b/core/res/res/values-eu-rES/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Gidalerro batzuk ezkutatu dira, gidalerroei jarraiki"</string>
<string name="safeMode" msgid="2788228061547930246">"Modu segurua"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android sistema"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Pertsonalak"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Lana"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Aldatu profil pertsonalera"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Aldatu laneko profilera"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontaktuak"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"kontaktuak atzitzeko"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Kokapena"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Ez da baimenik behar"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"dirua kosta dakizuke"</string>
<string name="dlg_ok" msgid="7376953167039865701">"Ados"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"Kargatzeko USBa"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Gailua USB bidez ari da kargatzen"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Konektatutako gailua USB bidez jasotzen ari da energia"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"Fitxategiak transferitzeko USBa"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"Argazkiak transferitzeko USBa"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI modurako USBa"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"PARTEKATU"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"BAZTERTU"</string>
<string name="select_input_method" msgid="8547250819326693584">"Aldatu teklatua"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Beste teklatu batzuk"</string>
<string name="show_ime" msgid="2506087537466597099">"Erakutsi pantailan teklatu fisikoa aktibo dagoen bitartean"</string>
<string name="hardware" msgid="194658061510127999">"Erakutsi teklatu birtuala"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Hautatu teklatuaren diseinua"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Ukitu teklatuaren diseinua hautatzeko."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Konfiguratu teklatu fisikoa"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Hizkuntza eta diseinua hautatzeko, sakatu hau"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"hautagaiak"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Aukera gehiago"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Barneko biltegiratze partekatua"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD txartela"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD txartela"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB unitatea"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index b143038..df9d78f 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"محتوا بر اساس خطمشی پنهان شده است"</string>
<string name="safeMode" msgid="2788228061547930246">"حالت ایمن"</string>
<string name="android_system_label" msgid="6577375335728551336">"سیستم Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"شخصی"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"محل کار"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"رفتن به نمایه شخصی"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"رفتن به نمایه کاری"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"مخاطبین"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"دسترسی به مخاطبین شما"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"مکان"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"مجوزی لازم نیست"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"ممکن است برای شما هزینه داشته باشد"</string>
<string name="dlg_ok" msgid="7376953167039865701">"تأیید"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB برای شارژ کردن"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"شارژ کردن این دستگاه از طریق USB"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"شارژ دستگاه متصل از طریق USB"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB برای انتقال فایل"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB برای انتقال عکس"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB برای MIDI"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"اشتراکگذاری"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"نپذیرفتن"</string>
<string name="select_input_method" msgid="8547250819326693584">"تغییر صفحهکلید"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"صفحهکلیدهای دیگر"</string>
<string name="show_ime" msgid="2506087537466597099">"وقتی صفحهکلید فیزیکی فعال است این ویرایشگر را روی صفحه نگهمیدارد"</string>
<string name="hardware" msgid="194658061510127999">"نمایش صفحهکلید مجازی"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"انتخاب طرحبندی صفحهکلید"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"برای انتخاب یک طرحبندی صفحهکلید لمس کنید…"</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"پیکربندی صفحهکلید فیزیکی"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"برای انتخاب زبان و چیدمان ضربه بزنید"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"داوطلبین"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"سایر گزینهها"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"حافظه داخلی مشترک"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"کارت SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"کارت SD <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"درایو USB"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 8bb613e..c869961 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Sisältö on piilotettu käytännön perusteella."</string>
<string name="safeMode" msgid="2788228061547930246">"Suojattu tila"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android-järjestelmä"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Henkilökoht."</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Työ"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Siirry henkilökohtaiseen profiiliin"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Siirry työprofiiliin"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontaktit"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"käyttää yhteystietoja"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Sijainti"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Lupia ei tarvita"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"tämä voi maksaa"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB on lataustilassa"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Laitetta ladataan USB-yhteyden kautta"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Liitetty laite saa virtaa USB-yhteyden kautta"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB on tiedonsiirtotilassa"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB on kuvansiirtotilassa"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB on MIDI-tilassa"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"JAA"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"HYLKÄÄ"</string>
<string name="select_input_method" msgid="8547250819326693584">"Vaihda näppäimistö"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Muut näppäimistöt"</string>
<string name="show_ime" msgid="2506087537466597099">"Pidä näytöllä, kun fyysinen näppäimistö on aktiivinen."</string>
<string name="hardware" msgid="194658061510127999">"Näytä virtuaalinen näppäimistö"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Valitse näppäimistöasettelu"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Kosketa ja valitse näppäimistöasettelu."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Määritä fyysinen näppäimistö"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Valitse kieli ja asettelu koskettamalla."</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZÅÄÖ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"kandidaatit"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Lisää asetuksia"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Sisäinen jaettu tallennustila"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD-kortti"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"SD-kortti: <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB-asema"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 3375e185..efc8af3 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Contenu masqué conformément aux politiques"</string>
<string name="safeMode" msgid="2788228061547930246">"Mode sécurisé"</string>
<string name="android_system_label" msgid="6577375335728551336">"Système Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Personnel"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Travail"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Passer au profil personnel"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Passer au profil professionnel"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Contacts"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"accéder à vos contacts"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Localisation"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Aucune autorisation requise"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"cela peut engendrer des frais"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB pour la recharge"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Cet appareil est en cours de charge par USB"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Un connecteur USB alimente cet appareil"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB pour le transfert de fichiers"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB pour le transfert de photos"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB pour MIDI"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"PARTAGER"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"REFUSER"</string>
<string name="select_input_method" msgid="8547250819326693584">"Changer de clavier"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Autres claviers"</string>
<string name="show_ime" msgid="2506087537466597099">"Afficher lorsque le clavier physique est activé"</string>
<string name="hardware" msgid="194658061510127999">"Afficher le clavier virtuel"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Sélectionnez la disposition du clavier"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Appuyez ici pour sélectionner une disposition de clavier."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Configurer le clavier physique"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Touchez pour sélectionner la langue et la configuration du clavier"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"candidats"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Plus d\'options"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Stockage interne partagé"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Carte SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Carte mémoire SD <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Clé USB"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 741d0e1..4e83fa9 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Contenu masqué conformément aux règles"</string>
<string name="safeMode" msgid="2788228061547930246">"Mode sécurisé"</string>
<string name="android_system_label" msgid="6577375335728551336">"Système Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Personnel"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Professionnel"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Passer au profil personnel"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Passer au profil professionnel"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Contacts"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"accéder à vos contacts"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Position"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Aucune autorisation requise"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"Cela peut engendrer des frais"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"Recharge par USB"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Recharge via USB de cet appareil…"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Alimentation via USB de l\'appareil connecté…"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB pour le transfert de fichiers"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB pour le transfert de photos"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB en mode MIDI"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"PARTAGER"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"REFUSER"</string>
<string name="select_input_method" msgid="8547250819326693584">"Changer de clavier"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Autres claviers"</string>
<string name="show_ime" msgid="2506087537466597099">"Afficher lorsque le clavier physique est activé"</string>
<string name="hardware" msgid="194658061510127999">"Afficher le clavier virtuel"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Sélectionnez la disposition du clavier"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Appuyez ici pour sélectionner une disposition de clavier."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Configurer le clavier physique"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Appuyer pour sélectionner la langue et la disposition"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"candidats"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Plus d\'options"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Espace de stockage interne partagé"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Carte SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Carte SD <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Clé USB"</string>
diff --git a/core/res/res/values-gl-rES/strings.xml b/core/res/res/values-gl-rES/strings.xml
index be83dab..2fa6087 100644
--- a/core/res/res/values-gl-rES/strings.xml
+++ b/core/res/res/values-gl-rES/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Ocultouse contido por causa da política"</string>
<string name="safeMode" msgid="2788228061547930246">"Modo seguro"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Persoal"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Traballo"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Cambiar ao perfil persoal"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Cambiar ao perfil de traballo"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Contactos"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"acceder aos teus contactos"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Localización"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Non é necesario ningún permiso"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"é posible que teñas que pagar"</string>
<string name="dlg_ok" msgid="7376953167039865701">"Aceptar"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB para carga"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"O USB está cargando este dispositivo"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"O USB está proporcionando enerxía ao dispositivo conectado"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB para transferencia de ficheiros"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB para transferencia de fotos"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB para MIDI"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"COMPARTIR"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ANULAR"</string>
<string name="select_input_method" msgid="8547250819326693584">"Cambiar teclado"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Outros teclados"</string>
<string name="show_ime" msgid="2506087537466597099">"Manteno na pantalla mentres o teclado físico estea activo"</string>
<string name="hardware" msgid="194658061510127999">"Mostrar teclado virtual"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Seleccionar deseño de teclado"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Toca para seleccionar un deseño de teclado."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Configura o teclado físico"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Toca para seleccionar o idioma e o deseño"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNÑOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNÑOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"candidatos"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Máis opcións"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Almacenamento compartido interno"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Tarxeta SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Tarxeta SD de <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Unidade USB"</string>
diff --git a/core/res/res/values-gu-rIN/strings.xml b/core/res/res/values-gu-rIN/strings.xml
index 4389cff..0b9f4ad 100644
--- a/core/res/res/values-gu-rIN/strings.xml
+++ b/core/res/res/values-gu-rIN/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"નીતિ દ્વારા સામગ્રી છુપાવાઈ"</string>
<string name="safeMode" msgid="2788228061547930246">"સુરક્ષિત મોડ"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android સિસ્ટમ"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"વ્યક્તિગત"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"કાર્યાલય"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"વ્યક્તિગત પર સ્વિચ કરો"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"કાર્ય પર સ્વિચ કરો"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"સંપર્કો"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"તમારા સંપર્કોને ઍક્સેસ કરો"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"સ્થાન"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"કોઈ પરવાનગીઓ જરૂરી નથી"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"આનાથી તમારા પૈસા ખર્ચ થઈ શકે છે"</string>
<string name="dlg_ok" msgid="7376953167039865701">"ઑકે"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"ચાર્જ કરવા માટે USB"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"આ ઉપકરણને USB થી ચાર્જ કરે છે"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"જોડાયેલ ઉપકરણ માટે USB પાવર પૂરો પાડે છે"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"ફાઇલ ટ્રાન્સફર માટે USB"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"ફોટા ટ્રાન્સફર માટે USB"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI માટે USB"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"શેર કરો"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"નકારો"</string>
<string name="select_input_method" msgid="8547250819326693584">"કીબોર્ડ બદલો"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"અન્ય કીબોર્ડ્સ"</string>
<string name="show_ime" msgid="2506087537466597099">"જ્યારે ભૌતિક કીબોર્ડ સક્રિય હોય ત્યારે તેને સ્ક્રીન પર રાખો"</string>
<string name="hardware" msgid="194658061510127999">"વર્ચ્યુઅલ કીબોર્ડ બતાવો"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"કીબોર્ડ લેઆઉટ પસંદ કરો."</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"કીબોર્ડ લેઆઉટ પસંદ કરવા માટે ટચ કરો."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"ભૌતિક કીબોર્ડ ગોઠવો"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"ભાષા અને લેઆઉટ પસંદ કરવા માટે ટૅપ કરો"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"ઉમેદવારો"</u></string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 18c4007..474e81e 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"सामग्री पॉलिसी के द्वारा छिपी हुई है"</string>
<string name="safeMode" msgid="2788228061547930246">"सुरक्षित मोड"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android सिस्टम"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"व्यक्तिगत"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"कार्यालय"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"व्यक्तिगत प्रोफ़ाइल में स्विच करें"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"कार्य प्रोफ़ाइल में स्विच करें"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"संपर्क"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"अपने संपर्कों को ऐक्सेस करें"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"स्थान"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"किसी अनुमति की आवश्यकता नहीं है"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"इससे आपको धन देना पड़ सकता है"</string>
<string name="dlg_ok" msgid="7376953167039865701">"ठीक है"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"चार्जिंग के लिए USB"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"यह डिवाइस USB से चार्ज हो रहा है"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"अटैच किए गए डिवाइस को USB से पावर मिल रही है"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"फ़ाइल स्थानांतरण के लिए USB"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"फ़ोटो स्थानांतरण के लिए USB"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI के लिए USB"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"साझा करें"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"अस्वीकार करें"</string>
<string name="select_input_method" msgid="8547250819326693584">"कीबोर्ड बदलें"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"अन्य कीबोर्ड"</string>
<string name="show_ime" msgid="2506087537466597099">"भौतिक कीबोर्ड के सक्रिय होने के दौरान इसे स्क्रीन पर बनाए रखें"</string>
<string name="hardware" msgid="194658061510127999">"वर्चुअल कीबोर्ड दिखाएं"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"कीबोर्ड लेआउट को चुनें"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"कीबोर्ड लेआउट का चयन करने के लिए स्पर्श करें."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"भौतिक कीबोर्ड कॉन्फ़िगर करें"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"भाषा और लेआउट चुनने के लिए टैप करें"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"उम्मीदवार"</u></string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 2ce73f4..837aa56 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -235,8 +235,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Sadržaj je skriven prema pravilima"</string>
<string name="safeMode" msgid="2788228061547930246">"Siguran način rada"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sustav Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Osobno"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Posao"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Prijeđite na osobni"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Prijeđite na radni"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontakti"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"pristupati vašim kontaktima"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Lokacija"</string>
@@ -1053,7 +1053,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Nije potrebno dopuštenje"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"možda ćete morati platiti"</string>
<string name="dlg_ok" msgid="7376953167039865701">"U redu"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB za punjenje"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Punjenje uređaja USB-om"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Napajanje priključenog uređaja USB-om"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB za prijenos datoteka"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB za prijenos fotografija"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB za MIDI"</string>
@@ -1068,11 +1069,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"DIJELI"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ODBIJ"</string>
<string name="select_input_method" msgid="8547250819326693584">"Promjena tipkovnice"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Ostale tipkovnice"</string>
<string name="show_ime" msgid="2506087537466597099">"Zadržava se na zaslonu dok je fizička tipkovnica aktivna"</string>
<string name="hardware" msgid="194658061510127999">"Prikaži virtualnu tipkovnicu"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Odaberite izgled tipkovnice"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Dodirnite za odabir izgleda tipkovnice."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Konfigurirajte fizičku tipkovnicu"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Dodirnite da biste odabrali jezik i raspored"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"kandidati"</u></string>
@@ -1233,8 +1233,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Više opcija"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Unutarnja dijeljena pohrana"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD kartica"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD kartica"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB pogon"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index bf23b71..0c4705d 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"A tartalom irányelv miatt elrejtve"</string>
<string name="safeMode" msgid="2788228061547930246">"Biztonsági üzemmód"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android rendszer"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Személyes"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Munkahelyi"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Átváltás személyes profilra"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Átváltás munkaprofilra"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Névjegyek"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"hozzáférés a névjegyekhez"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Helyadatok"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Nincs szükség engedélyre"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"ez pénzbe kerülhet Önnek"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB töltéshez"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Az eszköz USB-s töltése"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"A csatlakoztatott eszköz USB-n keresztül való töltése"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB fájlátvitelhez"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB fotóátvitelhez"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB MIDI-hez"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"MEGOSZTÁS"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ELUTASÍTÁS"</string>
<string name="select_input_method" msgid="8547250819326693584">"Billentyűzet megváltoztatása"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Más billentyűzetek"</string>
<string name="show_ime" msgid="2506087537466597099">"Maradjon a képernyőn, amíg a billentyűzet aktív"</string>
<string name="hardware" msgid="194658061510127999">"Virtuális billentyűzet"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Válasszon billentyűzetkiosztást"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Érintse meg az egyik billentyűzetkiosztás kiválasztásához."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Állítsa be a fizikai billentyűzetet"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Koppintson a nyelv és a billentyűzetkiosztás kiválasztásához"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"jelöltek"</u></string>
diff --git a/core/res/res/values-hy-rAM/strings.xml b/core/res/res/values-hy-rAM/strings.xml
index 8921e88..4be90da 100644
--- a/core/res/res/values-hy-rAM/strings.xml
+++ b/core/res/res/values-hy-rAM/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Բովանդակությունը թաքցվել է ըստ քաղաքականության"</string>
<string name="safeMode" msgid="2788228061547930246">"Անվտանգ ռեժիմ"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android համակարգ"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Անձնական"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Աշխատանքային"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Անցնել անհատական պրոֆիլին"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Անցնել աշխատանքային պրոֆիլին"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Կոնտակտներ"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"կոնտակտների հասանելիություն"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Տեղադրություն"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Թույլտվություններ չեն պահանջվում"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"Սա կարող է գումար պահանջել"</string>
<string name="dlg_ok" msgid="7376953167039865701">"Լավ"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"Լիցքավորման USB"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Սարքի լիցքավորում USB լարի միջոցով"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Հոսանքի մատակարարում կցված սարքերին USB լարի միջոցով"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"Ֆայլերի փոխանցման USB"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"Լուսանկարների փոխանցման USB"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI-ի USB"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"ՏՐԱՄԱԴՐԵԼ"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ՄԵՐԺԵԼ"</string>
<string name="select_input_method" msgid="8547250819326693584">"Փոխել ստեղնաշարը"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Այլ ստեղնաշարեր"</string>
<string name="show_ime" msgid="2506087537466597099">"Պահել էկրանին մինչդեռ ֆիզիկական ստեղնաշարն ակտիվ է"</string>
<string name="hardware" msgid="194658061510127999">"Ցույց տալ վիրտուալ ստեղնաշարը"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Ընտրեք ստեղնաշարի դիրքը"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Հպեք` ստեղնաշարի դիրքը ընտրելու համար:"</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Կազմաձևեք ֆիզիկական ստեղնաշարը"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Հպեք՝ լեզուն և դասավորությունն ընտրելու համար"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉՊՋՌՍՎՏՐՑՈՒՓՔԵւՕՖ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"թեկնածուները"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Ավելի շատ ընտրանքներ"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Համօգտագործվող ներքին հիշողություն"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD քարտ"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"SD քարտ <xliff:g id="MANUFACTURER">%s</xliff:g>-ից"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB սարքավար"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index e7c706d..7218617 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Konten disembunyikan menurut kebijakan"</string>
<string name="safeMode" msgid="2788228061547930246">"Mode aman"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistem Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Pribadi"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Kantor"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Beralih ke Pribadi"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Beralih ke Kantor"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontak"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"mengakses kontak"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Lokasi"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Tidak perlu izin"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"ini mungkin tidak gratis"</string>
<string name="dlg_ok" msgid="7376953167039865701">"Oke"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB untuk pengisian daya"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Isi daya perangkat ini melalui USB"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Suplai daya melalui USB ke perangkat yang terpasang"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB untuk transfer file"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB untuk transfer foto"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB untuk MIDI"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"BAGIKAN"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"TOLAK"</string>
<string name="select_input_method" msgid="8547250819326693584">"Ubah keyboard"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Keyboard lainnya"</string>
<string name="show_ime" msgid="2506087537466597099">"Pertahankan di layar jika keyboard fisik masih aktif"</string>
<string name="hardware" msgid="194658061510127999">"Tampilkan keyboard virtual"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Pilih tata letak keyboard"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Sentuh untuk memilih tata letak keyboard."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Mengonfigurasi keyboard fisik"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Ketuk untuk memilih bahasa dan tata letak"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"calon"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Opsi lainnya"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Penyimpanan bersama internal"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Kartu SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Kartu SD <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Drive USB"</string>
diff --git a/core/res/res/values-is-rIS/strings.xml b/core/res/res/values-is-rIS/strings.xml
index 5edb125..cebc4cc 100644
--- a/core/res/res/values-is-rIS/strings.xml
+++ b/core/res/res/values-is-rIS/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Efni falið með reglu"</string>
<string name="safeMode" msgid="2788228061547930246">"Örugg stilling"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android kerfið"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Persónulegt"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Vinna"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Skipta yfir í persónulegt snið"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Skipta yfir í vinnusnið"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Tengiliðir"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"fá aðgang að tengiliðunum þínum"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Staðsetning"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Engra heimilda þörf"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"þú gætir þurft að borga fyrir þetta"</string>
<string name="dlg_ok" msgid="7376953167039865701">"Í lagi"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB fyrir hleðslu"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Þetta tæki er í USB-hleðslu"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Tengt tæki er í USB-hleðslu"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB fyrir skráaflutning"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB fyrir myndaflutning"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB fyrir MIDI"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"DEILA"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"HAFNA"</string>
<string name="select_input_method" msgid="8547250819326693584">"Skipta um lyklaborð"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Önnur lyklaborð"</string>
<string name="show_ime" msgid="2506087537466597099">"Haltu því á skjánum meðan vélbúnaðarlyklaborðið er virkt"</string>
<string name="hardware" msgid="194658061510127999">"Sýna sýndarlyklaborð"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Veldu lyklaskipan"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Snertu til að velja lyklaskipan."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Stilla vélbúnaðarlyklaborð"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Ýttu til að velja tungumál og útlit"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" AÁBCDÐEÉFGHIÍJKLMNOÓPQRSTUÚVWXYÝZÞÆÖ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789AÁBCDÐEÉFGHIÍJKLMNOÓPQRSTUÚVWXYÝZÞÆÖ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"möguleikar"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Fleiri valkostir"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Innbyggð samnýtt geymsla"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD-kort"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"SD-kort frá <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB-drif"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 73775a0..2a73990 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Contenuti nascosti in base alle norme"</string>
<string name="safeMode" msgid="2788228061547930246">"Modalità provvisoria"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Personale"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Lavoro"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Passa al profilo personale"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Passa al profilo di lavoro"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Contatti"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"accedere ai contatti"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Posizione"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Nessuna autorizzazione richiesta"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"potrebbe comportare dei costi"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB per la ricarica"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Dispositivo in carica tramite USB"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Dispositivo collegato alimentato tramite USB"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB per il trasferimento di file"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB per il trasferimento di foto"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB per la modalità MIDI"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"CONDIVIDI"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"RIFIUTO"</string>
<string name="select_input_method" msgid="8547250819326693584">"Cambia tastiera"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Altre tastiere"</string>
<string name="show_ime" msgid="2506087537466597099">"Tieni sullo schermo quando è attiva la tastiera fisica"</string>
<string name="hardware" msgid="194658061510127999">"Mostra tastiera virtuale"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Seleziona layout tastiera"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Tocca per selezionare un layout di tastiera."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Configura la tastiera fisica"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Tocca per selezionare la lingua e il layout"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"candidati"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Altre opzioni"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Archivio condiviso interno"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Scheda SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Scheda SD <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Unità USB"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index b06434e..80435b6 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -237,8 +237,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"התוכן מוסתר על ידי המדיניות"</string>
<string name="safeMode" msgid="2788228061547930246">"מצב בטוח"</string>
<string name="android_system_label" msgid="6577375335728551336">"מערכת Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"אישי"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"עבודה"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"עבור ל\'אישי\'"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"עבור ל\'עבודה\'"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"אנשי קשר"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"גישה אל אנשי הקשר"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"מיקום"</string>
@@ -1061,7 +1061,8 @@
<string name="no_permissions" msgid="7283357728219338112">"לא דרושים אישורים"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"פעולה זו עשויה לחייב אותך בכסף:"</string>
<string name="dlg_ok" msgid="7376953167039865701">"אישור"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB לטעינה"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"USB טוען את המכשיר הזה"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB מספק מתח למכשיר המצורף"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB להעברת קבצים"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB להעברת תמונות"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB ל-MIDI"</string>
@@ -1076,11 +1077,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"שתף"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"דחה"</string>
<string name="select_input_method" msgid="8547250819326693584">"שינוי מקלדת"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"מקלדות אחרות"</string>
<string name="show_ime" msgid="2506087537466597099">"השאר אותו במסך בזמן שהמקלדת הפיזית פעילה"</string>
<string name="hardware" msgid="194658061510127999">"הצג מקלדת וירטואלית"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"בחירת פריסת מקלדת"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"גע כדי לבחור פריסת מקלדת."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"הגדרת מקלדת פיזית"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"הקש כדי לבחור שפה ופריסה"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"מועמדים"</u></string>
@@ -1242,8 +1242,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"אפשרויות נוספות"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"אחסון משותף פנימי"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"כרטיס SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"כרטיס SD של <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"כונן USB"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 14d64b0..394775f 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"ポリシーによって非表示になっているコンテンツ"</string>
<string name="safeMode" msgid="2788228061547930246">"セーフモード"</string>
<string name="android_system_label" msgid="6577375335728551336">"Androidシステム"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"個人用"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"仕事用"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"個人用に切り替える"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"仕事用に切り替える"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"連絡先"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"連絡先へのアクセス"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"位置情報"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"権限の許可は必要ありません"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"料金が発生する場合があります"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USBを充電に使用"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"この端末を USB で充電"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"接続した端末に USB で給電"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USBをファイル転送に使用"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USBを写真転送に使用"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USBをMIDIに使用"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"共有する"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"共有しない"</string>
<string name="select_input_method" msgid="8547250819326693584">"キーボードの変更"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"その他のキーボード"</string>
<string name="show_ime" msgid="2506087537466597099">"物理キーボードが有効になっている間は、画面に表示されます"</string>
<string name="hardware" msgid="194658061510127999">"仮想キーボードの表示"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"キーボードレイアウトの選択"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"タップしてキーボードレイアウトを選択してください。"</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"物理キーボードの設定"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"タップして言語とレイアウトを選択してください"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"候補"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"その他のオプション"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s、%2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s、%2$s、%3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"内部共有ストレージ"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SDカード"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g>製SDカード"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USBドライブ"</string>
diff --git a/core/res/res/values-ka-rGE/strings.xml b/core/res/res/values-ka-rGE/strings.xml
index 92e57b7..fcc56da 100644
--- a/core/res/res/values-ka-rGE/strings.xml
+++ b/core/res/res/values-ka-rGE/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"შიგთავსი დამალულია წესების შესაბამისად"</string>
<string name="safeMode" msgid="2788228061547930246">"უსაფრთხო რეჟიმი"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android-ის სისტემა"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"პირადი"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"სამსახური"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"პირად პროფილზე გადართვა"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"სამსახურის პროფილზე გადართვა"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"კონტაქტები"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"თქვენს კონტაქტებზე წვდომა"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"მდებარეობა"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"ნებართვა საჭირო არ არის"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"ამისათვის შესაძლოა მოგიწიოთ თანხის გადახდა"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB დამუხტვისთვის"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"მოწყობილობა USB-ის მეშვეობით იტენება"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"მიერთებულ მოწყობილობას ელკვებას USB აწვდის"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB ფაილების გადაცემისთვის"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB ფოტოების გადაცემისთვის"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB MIDI-სთვის"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"გაზიარება"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"უარყოფა"</string>
<string name="select_input_method" msgid="8547250819326693584">"კლავიატურის შეცვლა"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"სხვა კლავიატურები"</string>
<string name="show_ime" msgid="2506087537466597099">"აქტიური ფიზიკური კლავიატურისას ეკრანზე შენარჩუნება"</string>
<string name="hardware" msgid="194658061510127999">"ვირტუალური კლავიატურის ჩვენება"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"შეარჩიეთ კლავიატურის განლაგება."</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"კლავიატურის განლაგების შესარჩევად შეეხეთ."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"მოახდინეთ ფიზიკური კლავიატურის კონფიგურაცია"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"შეეხეთ ენისა და განლაგების ასარჩევად"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"კანდიდატები"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"მეტი ვარიანტები"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"შიდა გაზიარებული მეხსიერება"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD ბარათი"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD ბარათი"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB დისკი"</string>
diff --git a/core/res/res/values-kk-rKZ/strings.xml b/core/res/res/values-kk-rKZ/strings.xml
index 85621ed..6eaa770 100644
--- a/core/res/res/values-kk-rKZ/strings.xml
+++ b/core/res/res/values-kk-rKZ/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Мазмұн саясатқа сай жасырылған"</string>
<string name="safeMode" msgid="2788228061547930246">"Қауіпсіз режим"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android жүйесі"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Жеке"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Жұмыс"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Жекеге ауысу"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Жұмысқа ауысу"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Контактілер"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"контактілерге кіру"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Орын"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Рұқсат қажет емес"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"бұған төлем қажет болуы мүмкін"</string>
<string name="dlg_ok" msgid="7376953167039865701">"Жарайды"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"Зарядтауға арналған USB"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"USB арқылы зарядтау"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB жалғанған құрылғыға қуат беруде"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"Файлды тасымалдауға арналған USB"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"Фотосуретті тасымалдауға арналған USB"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI режиміне арналған USB"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"БӨЛІСУ"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ҚАБЫЛДАМАУ"</string>
<string name="select_input_method" msgid="8547250819326693584">"Пернетақтаны өзгерту"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Басқа пернетақталар"</string>
<string name="show_ime" msgid="2506087537466597099">"Физикалық пернетақта белсенді кезде оны экранда ұстау"</string>
<string name="hardware" msgid="194658061510127999">"Виртуалды пернетақтаны көрсету"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Пернетақта орналасуын таңдау"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Пернетақта орналасуын таңдау үшін түртіңіз."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Физикалық пернетақтаны конфигурациялау"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Тіл мен пернетақта схемасын таңдау үшін түртіңіз"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"үміткерлер"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Басқа опциялар"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Ішкі ортақ қойма"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD картасы"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD картасы"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB дискі"</string>
diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml
index e9be6b6..30d3c0a 100644
--- a/core/res/res/values-km-rKH/strings.xml
+++ b/core/res/res/values-km-rKH/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"មាតិកាត្រូវបានលាក់ដោយផ្អែកលើគោលការណ៍"</string>
<string name="safeMode" msgid="2788228061547930246">"របៀបសុវត្ថិភាព"</string>
<string name="android_system_label" msgid="6577375335728551336">"ប្រព័ន្ធ Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"ផ្ទាល់ខ្លួន"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"កន្លែងធ្វើការ"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"ប្តូរទៅផ្ទាល់ខ្លួន"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"ប្តូរទៅការងារ"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"ទំនាក់ទំនង"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"ចូលប្រើទំនាក់ទំនងរបស់អ្នក"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"ទីតាំង"</string>
@@ -1047,7 +1047,8 @@
<string name="no_permissions" msgid="7283357728219338112">"មិនទាមទារសិទ្ធិ"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"វាអាចកាត់លុយអ្នក"</string>
<string name="dlg_ok" msgid="7376953167039865701">"យល់ព្រម"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB សម្រាប់បញ្ចូលថ្ម"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"USB កំពុងសាកឧបករណ៍នេះ"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB កំពុងផ្គត់ផ្គង់ថាមពលទៅឧបករណ៍ដែលបានភ្ជាប់"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB សម្រាប់ការផ្ទេរឯកសារ"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB សម្រាប់ការផ្ទេររូបថត"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB សម្រាប់ MIDI"</string>
@@ -1062,11 +1063,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"ចែករំលែក"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"បដិសេធ"</string>
<string name="select_input_method" msgid="8547250819326693584">"ប្ដូរក្ដារចុច"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"ក្តារចុចផ្សេងទៀត"</string>
<string name="show_ime" msgid="2506087537466597099">"ទុកវានៅលើអេក្រង់ខណៈពេលក្តារចុចពិតប្រាកដកំពុងសកម្ម"</string>
<string name="hardware" msgid="194658061510127999">"បង្ហាញក្ដារចុចនិម្មិត"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"ជ្រើសប្លង់ក្ដារចុច"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"ប៉ះ ដើម្បីជ្រើសប្លង់ក្ដារចុច។"</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"កំណត់រចនាសម្ព័ន្ធក្តារចុចពិតប្រាកដ"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"ប៉ះដើម្បីជ្រើសភាសា និងប្លង់"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"បេក្ខជន"</u></string>
@@ -1226,8 +1226,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"ជម្រើសច្រើនទៀត"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"ឧបករណ៍ផ្ទុកដែលចែករំលែកខាងក្នុង"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"កាតអេសឌី"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"កាត SD <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"ឧបករណ៍ផ្ទុក USB"</string>
diff --git a/core/res/res/values-kn-rIN/strings.xml b/core/res/res/values-kn-rIN/strings.xml
index 2e04419..087b399 100644
--- a/core/res/res/values-kn-rIN/strings.xml
+++ b/core/res/res/values-kn-rIN/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"ನೀತಿಯಿಂದ ಮರೆಮಾಡಲಾಗಿರುವ ವಿಷಯಗಳು"</string>
<string name="safeMode" msgid="2788228061547930246">"ಸುರಕ್ಷಿತ ಮೋಡ್"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android ಸಿಸ್ಟಂ"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"ವೈಯಕ್ತಿಕ"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"ಕಚೇರಿ"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"ವೈಯಕ್ತಿಕಗೆ ಬದಲಿಸಿ"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"ಕೆಲಸಕ್ಕೆ ಬದಲಿಸು"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"ಸಂಪರ್ಕಗಳು"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"ನಿಮ್ಮ ಸಂಪರ್ಕಗಳನ್ನು ಪ್ರವೇಶಿಸಲು"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"ಸ್ಥಳ"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"ಯಾವುದೇ ಅನುಮತಿಗಳ ಅಗತ್ಯವಿಲ್ಲ"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"ಇದು ನಿಮ್ಮ ಹಣವನ್ನು ವ್ಯಯಿಸಬಹುದು"</string>
<string name="dlg_ok" msgid="7376953167039865701">"ಸರಿ"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"ಚಾರ್ಜ್ ಮಾಡುವುದಕ್ಕಾಗಿ USB"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"ಈ ಸಾಧನಕ್ಕೆ USB ಅನ್ನು ಚಾರ್ಜ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB, ಲಗತ್ತಿಸಲಾದ ಸಾಧನಕ್ಕೆ ಪವರ್ ಪೂರೈಸುತ್ತಿದೆ"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"ಫೈಲ್ ವರ್ಗಾವಣೆಗೆ USB"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"ಫೋಟೋ ವರ್ಗಾವಣೆಗೆ USB"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI ಗೆ USB"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"ಹಂಚಿಕೊಳ್ಳಿ"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ನಿರಾಕರಿಸು"</string>
<string name="select_input_method" msgid="8547250819326693584">"ಕೀಬೋರ್ಡ್ ಬದಲಿಸಿ"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"ಇತರೆ ಕೀಬೋರ್ಡ್ಗಳು"</string>
<string name="show_ime" msgid="2506087537466597099">"ಭೌತಿಕ ಕೀಬೋರ್ಡ್ ಸಕ್ರಿಯವಾಗಿರುವಾಗ ಅದನ್ನು ಪರದೆಯ ಮೇಲೆ ಇರಿಸಿಕೊಳ್ಳಿ"</string>
<string name="hardware" msgid="194658061510127999">"ವರ್ಚ್ಯುಯಲ್ ಕೀಬೋರ್ಡ್ ತೋರಿಸು"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"ಕೀಬೋರ್ಡ್ ಲೇಔಟ್ ಆಯ್ಕೆಮಾಡಿ"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"ಕೀಬೋರ್ಡ್ ಲೇಔಟ್ ಆಯ್ಕೆ ಮಾಡಲು ಸ್ಪರ್ಶಿಸಿ"</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"ಭೌತಿಕ ಕೀಬೋರ್ಡ್ ಕಾನ್ಫಿಗರ್ ಮಾಡಿ"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"ಭಾಷೆ ಮತ್ತು ವಿನ್ಯಾಸವನ್ನು ಆಯ್ಕೆ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"ಅಭ್ಯರ್ಥಿಗಳು"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"ಇನ್ನಷ್ಟು ಆಯ್ಕೆಗಳು"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"ಆಂತರಿಕವಾಗಿ ಹಂಚಲಾದ ಸಂಗ್ರಹಣೆ"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD ಕಾರ್ಡ್"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD ಕಾರ್ಡ್"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB ಡ್ರೈವ್"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index b61df80..52de5ba 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"콘텐츠가 정책에 의해 숨겨졌습니다."</string>
<string name="safeMode" msgid="2788228061547930246">"안전 모드"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android 시스템"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"개인"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"직장"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"개인으로 전환"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"직장으로 전환"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"주소록"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"주소록에 접근할 수 있도록"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"위치"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"권한 필요 없음"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"비용이 부과될 수 있습니다."</string>
<string name="dlg_ok" msgid="7376953167039865701">"확인"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"충전용 USB"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"이 기기를 USB로 충전"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"연결된 기기에 USB로 전력 공급"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"파일 전송용 USB"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"사진 전송용 USB"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI용 USB"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"공유"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"거부"</string>
<string name="select_input_method" msgid="8547250819326693584">"키보드 변경"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"기타 키보드"</string>
<string name="show_ime" msgid="2506087537466597099">"물리적 키보드가 활성 상태인 경우 화면에 켜 둠"</string>
<string name="hardware" msgid="194658061510127999">"가상 키보드 표시"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"키보드 레이아웃 선택"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"터치하여 키보드 레이아웃을 선택합니다."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"물리적 키보드 설정"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"탭하여 언어와 레이아웃을 선택하세요."</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"가능한 원인"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"옵션 더보기"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"내부 공유 저장공간"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD 카드"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD 카드"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB 드라이브"</string>
diff --git a/core/res/res/values-ky-rKG/strings.xml b/core/res/res/values-ky-rKG/strings.xml
index 55b2b7c..34cc7e5 100644
--- a/core/res/res/values-ky-rKG/strings.xml
+++ b/core/res/res/values-ky-rKG/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Тийиштүү саясат боюнча жашырылган мазмундар"</string>
<string name="safeMode" msgid="2788228061547930246">"Коопсуз режим"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android Тутуму"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Жеке"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Жумуш"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Жеке профилге которулуу"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Жумуш профилине которулуу"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Байланыштар"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"байланыштарыңызга уруксат"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Жайгашкан жер"</string>
@@ -900,8 +900,8 @@
<string name="whichEditApplicationNamed" msgid="1775815530156447790">"%1$s менен түзөтүү"</string>
<string name="whichSendApplication" msgid="6902512414057341668">"Төмөнкү менен бөлүшүү"</string>
<string name="whichSendApplicationNamed" msgid="2799370240005424391">"%1$s менен бөлүшүү"</string>
- <string name="whichSendToApplication" msgid="8272422260066642057">"Колдонуп жөнөтүү"</string>
- <string name="whichSendToApplicationNamed" msgid="7768387871529295325">"%1$s колдонуп жөнөтүү"</string>
+ <string name="whichSendToApplication" msgid="8272422260066642057">"Колдонмо тандаңыз"</string>
+ <string name="whichSendToApplicationNamed" msgid="7768387871529295325">"%1$s аркылуу жөнөтүү"</string>
<string name="whichHomeApplication" msgid="4307587691506919691">"Башкы бет колдонмосун тандаңыз"</string>
<string name="whichHomeApplicationNamed" msgid="4493438593214760979">"Башкы бет колдонмосу катары %1$s пайдалануу"</string>
<string name="alwaysUse" msgid="4583018368000610438">"Бул аракет үчүн демейки боюнча колдонулсун."</string>
@@ -1046,7 +1046,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Эч уруксаттын кереги жок"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"бул үчүн акы алынышы мүмкүн"</string>
<string name="dlg_ok" msgid="7376953167039865701">"Жарайт"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"Кубаттоо үчүн USB"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Бул түзмөк USB менен кубатталууда"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Тиркелген түзмөк USB менен кубатталууда"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"Файл өткөрүү үчүн USB"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"Сүрөт өткөрүү үчүн USB"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI үчүн USB"</string>
@@ -1061,11 +1062,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"БӨЛҮШҮҮ"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ЧЕТКЕ КАГУУ"</string>
<string name="select_input_method" msgid="8547250819326693584">"Баскычтопту өзгөртүү"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Башка баскычтоптор"</string>
<string name="show_ime" msgid="2506087537466597099">"Баскычтоп иштетилгенде экранда көрүнүп турсун"</string>
<string name="hardware" msgid="194658061510127999">"Виртуалдык баскычтоп"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Тергичтин жайгашуусун тандоо"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Тергичтин жайгашуусун тандаш үчүн басыңыз."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Аппараттык баскычтопту конфигурациялоо"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Тил жана калып тандоо үчүн таптап коюңуз"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"талапкерлер"</u></string>
@@ -1225,8 +1225,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Дагы параметрлер"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Жалпы ички сактагыч"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD-карта"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD карта"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB түзмөк"</string>
diff --git a/core/res/res/values-lo-rLA/strings.xml b/core/res/res/values-lo-rLA/strings.xml
index 5d1c37e..ea62f9b 100644
--- a/core/res/res/values-lo-rLA/strings.xml
+++ b/core/res/res/values-lo-rLA/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"ເນື້ອຫາຖືກເຊື່ອງຕາມນະໂຍບາຍ"</string>
<string name="safeMode" msgid="2788228061547930246">"Safe mode"</string>
<string name="android_system_label" msgid="6577375335728551336">"ລະບົບ Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"ສ່ວນໂຕ"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"ບ່ອນເຮັດວຽກ"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"ສະລັບໄປໂປຣໄຟລ໌ສ່ວນຕົວ"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"ສະລັບໄປໂປຣໄຟລ໌ວຽ."</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"ລາຍຊື່"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"ເຂົ້າຫາລາຍຊື່ຂອງທ່ານ"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"ສະຖານທີ່"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"ບໍ່ຕ້ອງການການອະນຸຍາດ"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"ລາຍການນີ້ອາດມີການເກັບເງິນ"</string>
<string name="dlg_ok" msgid="7376953167039865701">"ຕົກລົງ"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB ສຳລັບການສາກ"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"ກຳລັງສາກໄຟ USB ອຸປະກອນນີ້"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB ກຳລັງສະໜອງໄຟໃຫ້ກັບອຸປະກອນທີ່ເຊື່ອມຕໍ່ກັນ"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB ສຳລັບການໂອນໄຟລ໌"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB ສຳລັບການໂອນໄຟລ໌"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB ສຳລັບ MIDI"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"ແບ່ງປັນ"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ປະຕິເສດ"</string>
<string name="select_input_method" msgid="8547250819326693584">"ປ່ຽນແປ້ນພິມ"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"ແປ້ນພິມອື່ນໆ"</string>
<string name="show_ime" msgid="2506087537466597099">"ເປີດໃຊ້ໃຫ້ມັນຢູ່ໃນໜ້າຈໍໃນຂະນະທີ່ໃຊ້ແປ້ນພິມພາຍນອກຢູ່"</string>
<string name="hardware" msgid="194658061510127999">"ສະແດງແປ້ນພິມສະເໝືອນ"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"ເລືອກຮູບແບບແປ້ນພິມ"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"ກົດເພື່ອເລືອກຮູບແບບແປ້ນພິມ."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"ຕັ້ງຄ່າແປ້ນພິມພາຍນອກ"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"ແຕະເພື່ອເລືອກພາສາ ແລະ ໂຄງແປ້ນພິມ"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"ຕົວເລືອກ"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"ໂຕເລືອກອື່ນ"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"ພື້ນທີ່ຈັດເກັບຂໍ້ມູນທີ່ແບ່ງປັນພາຍໃນ"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD card"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> ແຜ່ນ SD"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB ດຣ້າຍ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 1460b9b..3e815ec 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -237,8 +237,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Turinys paslėptas vadovaujantis politika"</string>
<string name="safeMode" msgid="2788228061547930246">"Saugos režimas"</string>
<string name="android_system_label" msgid="6577375335728551336">"„Android“ sistema"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Asmeninė"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Darbo"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Perjungti į asmeninį režimą"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Perjungti į darbo režimą"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontaktai"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"pasiekti kontaktus"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Vietovė"</string>
@@ -1061,7 +1061,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Nereikia leidimų"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"tai gali kainuoti"</string>
<string name="dlg_ok" msgid="7376953167039865701">"Gerai"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB (įkrovimas)"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Įrenginys įkraunamas naudojant USB"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Maitinimas prijungtam įrenginiui tiekiamas naudojant USB"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB (failų perkėlimas)"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB (nuotraukų perkėlimas)"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB (MIDI)"</string>
@@ -1076,11 +1077,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"BENDRINTI"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ATMESTI"</string>
<string name="select_input_method" msgid="8547250819326693584">"Klaviatūros keitimas"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Kitos klaviatūros"</string>
<string name="show_ime" msgid="2506087537466597099">"Palikti ekrane, kol fizinė klaviatūra aktyvi"</string>
<string name="hardware" msgid="194658061510127999">"Rodyti virtualiąją klaviatūrą"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Pasirinkite klaviatūros išdėstymą"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Palieskite, kad pasirinktumėte klaviatūros išdėstymą."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Fizinės klaviatūros konfigūravimas"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Palieskite, kad pasirinktumėte kalbą ir išdėstymą"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" AĄBCČDEĘĖFGHIĮYJKLMNOPRSŠTUŲŪVZŽ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789AĄBCČDEĘĖFGHIĮYJKLMNOPRSŠTUŲŪVZŽ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"kandidatai"</u></string>
@@ -1242,8 +1242,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Daugiau parinkčių"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Vidinė bendroji atmintis"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD kortelė"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"„<xliff:g id="MANUFACTURER">%s</xliff:g>“ SD kortelė"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Atmintukas"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 566180f..56001fc 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -235,8 +235,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Saskaņā ar politiku saturs ir paslēpts."</string>
<string name="safeMode" msgid="2788228061547930246">"Drošais režīms"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android sistēma"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Personisks"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Darba"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Pārslēgt personīgo profilu"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Pārslēgt darba profilu"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontaktpersonas"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"piekļūt jūsu kontaktpersonu datiem"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Atrašanās vieta"</string>
@@ -1053,7 +1053,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Atļaujas nav nepieciešamas."</string>
<string name="perm_costs_money" msgid="4902470324142151116">"par to no jums var tikt iekasēta maksa"</string>
<string name="dlg_ok" msgid="7376953167039865701">"Labi"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB savienojums uzlādei"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"USB savienojums tiek izmantots šīs ierīces uzlādei"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB savienojums tiek izmantots pievienotās ierīces barošanai"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB savienojums failu pārsūtīšanai"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB savienojums fotoattēlu pārsūtīšanai"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB savienojums MIDI režīmā"</string>
@@ -1068,11 +1069,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"KOPĪGOT"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"NORAIDĪT"</string>
<string name="select_input_method" msgid="8547250819326693584">"Tastatūras maiņa"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Citas tastatūras"</string>
<string name="show_ime" msgid="2506087537466597099">"Paturēt ekrānā, kamēr ir aktīva fiziskā tastatūra"</string>
<string name="hardware" msgid="194658061510127999">"Virtuālās tastatūras rādīšana"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Atlasiet tastatūras izkārtojumu"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Pieskarieties, lai atlasītu tastatūras izkārtojumu."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Fiziskās tastatūras konfigurēšana"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Pieskarieties, lai atlasītu valodu un izkārtojumu"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" AĀBCČDEĒFGĢHIĪJKĶLĻMNŅOPRSŠTUŪVZŽ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789AĀBCČDEĒFGĢHIĪJKĶLĻMNŅOPRSŠTUŪVZŽ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"kandidāti"</u></string>
diff --git a/core/res/res/values-mk-rMK/strings.xml b/core/res/res/values-mk-rMK/strings.xml
index c4ef82c..060a9fa 100644
--- a/core/res/res/values-mk-rMK/strings.xml
+++ b/core/res/res/values-mk-rMK/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Содржините се скриени поради политиката"</string>
<string name="safeMode" msgid="2788228061547930246">"Безбеден режим"</string>
<string name="android_system_label" msgid="6577375335728551336">"Систем Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Лични"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Работа"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Префрлете на личен профил"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Префрли на работен профил"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Контакти"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"пристапува до контактите"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Локација"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Не се потребни дозволи"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"ова може да ве чини пари"</string>
<string name="dlg_ok" msgid="7376953167039865701">"Во ред"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"УСБ за полнење"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Уредов се полни преку USB"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Прикачениот уред се напојува преку USB"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"УСБ за пренос на датотеки"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"УСБ за пренос на фотографии"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"УСБ за МИДИ"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"СПОДЕЛИ"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ОДБИЈ"</string>
<string name="select_input_method" msgid="8547250819326693584">"Измени тастатура"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Други тастатури"</string>
<string name="show_ime" msgid="2506087537466597099">"Прикажувај го на екранот додека е активна физичката тастатура"</string>
<string name="hardware" msgid="194658061510127999">"Прикажи виртуелна тастатура"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Избери изглед на тастатура"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Допри за да избереш изглед на тастатура."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Конфигурирајте физичка тастатура"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Допрете за избирање јазик и распоред"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"кандидати"</u></string>
@@ -1226,8 +1226,7 @@
<!-- no translation found for action_bar_home_description_format (7965984360903693903) -->
<skip />
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Внатрешно заедничко место за складирање"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"СД картичка"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> СД-картичка"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"УСБ-меморија"</string>
diff --git a/core/res/res/values-ml-rIN/strings.xml b/core/res/res/values-ml-rIN/strings.xml
index 9b96501..f6ff1d8 100644
--- a/core/res/res/values-ml-rIN/strings.xml
+++ b/core/res/res/values-ml-rIN/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"നയം അനുസരിച്ച് ഉള്ളടക്കം മറച്ചിരിക്കുന്നു"</string>
<string name="safeMode" msgid="2788228061547930246">"സുരക്ഷിത മോഡ്"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android സിസ്റ്റം"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"വ്യക്തിഗതം"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"ഔദ്യോഗിക പ്രൊഫൈൽ"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"വ്യക്തിഗത പ്രൊഫൈലിലേക്ക് മാറുക"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"ഔദ്യോഗിക പ്രൊഫൈലിലേക്ക് മാറുക"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"കോൺടാക്റ്റുകൾ"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"നിങ്ങളുടെ കോൺടാക്റ്റുകൾ ആക്സസ്സ് ചെയ്യുക"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"ലൊക്കേഷൻ"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"അനുമതികളൊന്നും ആവശ്യമില്ല"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"ഇത് നിങ്ങൾക്ക് പണച്ചെലവിനിടയാക്കാം"</string>
<string name="dlg_ok" msgid="7376953167039865701">"ശരി"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"ചാർജ്ജിംഗിനായുള്ള USB"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"ഈ ഉപകരണം USB ചാർജുചെയ്യുന്നു"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"ഘടിപ്പിച്ചിട്ടുള്ള ഉപകരണത്തിന് USB വൈദ്യുതി നൽകുന്നു"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"ഫയൽ കൈമാറ്റത്തിനുള്ള USB"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"ഫോട്ടോ കൈമാറ്റത്തിനായുള്ള USB"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI-യ്ക്കായുള്ള USB"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"പങ്കിടുക"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"നിരസിക്കുക"</string>
<string name="select_input_method" msgid="8547250819326693584">"കീബോഡ് മാറ്റുക"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"മറ്റ് കീബോർഡുകൾ"</string>
<string name="show_ime" msgid="2506087537466597099">"ഫിസിക്കൽ കീബോർഡ് സജീവമായിരിക്കുമ്പോൾ സ്ക്രീനിൽ നിലനിർത്തുക"</string>
<string name="hardware" msgid="194658061510127999">"വെർച്വൽ കീബോർഡ് കാണിക്കുക"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"കീബോർഡ് ലേഔട്ട് തിരഞ്ഞെടുക്കുക"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"ഒരു കീബോർഡ് ലേഔട്ട് തിരഞ്ഞെടുക്കാൻ സ്പർശിക്കുക."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"ഫിസിക്കൽ കീബോർഡ് കോൺഫിഗർ ചെയ്യുക"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"ഭാഷയും ലേഔട്ടും തിരഞ്ഞെടുക്കുന്നതിന് ടാപ്പുചെയ്യുക"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"കാൻഡിഡേറ്റുകൾ"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"കൂടുതൽ ഓപ്ഷനുകള്"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"പങ്കിട്ട ആന്തരിക സ്റ്റോറേജ്"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD കാർഡ്"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD കാർഡ്"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB ഡ്രൈവ്"</string>
diff --git a/core/res/res/values-mn-rMN/strings.xml b/core/res/res/values-mn-rMN/strings.xml
index ef34b46..75105e6 100644
--- a/core/res/res/values-mn-rMN/strings.xml
+++ b/core/res/res/values-mn-rMN/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Удирдамжийн дагуу нуусан агуулга"</string>
<string name="safeMode" msgid="2788228061547930246">"Аюулгүй горим"</string>
<string name="android_system_label" msgid="6577375335728551336">"Андройд систем"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Хувийн"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Ажил"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"\"Хувийн\" руу шилжих"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"\"Ажлын\" руу шилжих"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Харилцагчдын хаяг"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"харилцагч руугаа хандах"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Байршил"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Зөвшөөрөл шаардахгүй"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"Энэ таныг төлбөрт оруулж болзошгүй"</string>
<string name="dlg_ok" msgid="7376953167039865701">"Тийм"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB цэнэглэгч"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Энэ төхөөрөмжийг USB цэнэглэж байна"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Залгасан төхөөрөмжөөс USB цэнэг авч байна"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"Файл шилжүүлэх USB"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"Фото зураг шилжүүлэх USB"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI-ийн USB"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"ХУВААЛЦАХ"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ТАТГАЛЗАХ"</string>
<string name="select_input_method" msgid="8547250819326693584">"Гарыг өөрчлөх"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Бусад гар"</string>
<string name="show_ime" msgid="2506087537466597099">"Бодит гар идэвхтэй үед үүнийг дэлгэцэнд харуулна уу"</string>
<string name="hardware" msgid="194658061510127999">"Хийсвэр гарыг харуулах"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Гарын схемийг сонгох"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Гарын схемийг сонгох бол хүрнэ үү."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Биет гарыг хэлбэрт оруулах"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Хэл болон бүдүүвчийг сонгохын тулд дарна уу"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"нэр дэвшигч"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Нэмэлт сонголтууд"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Дотоод хуваалцсан санах ой"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD карт"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD карт"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB диск"</string>
diff --git a/core/res/res/values-mr-rIN/strings.xml b/core/res/res/values-mr-rIN/strings.xml
index 7baa543..8a3e564 100644
--- a/core/res/res/values-mr-rIN/strings.xml
+++ b/core/res/res/values-mr-rIN/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"धोरणाद्वारे सामग्री लपविली"</string>
<string name="safeMode" msgid="2788228061547930246">"सुरक्षित मोड"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android सिस्टम"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"वैयक्तिक"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"कार्य"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"वैयक्तिकवर स्विच करा"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"कार्यावर स्विच करा"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"संपर्क"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"आपल्या संपर्कांवर प्रवेश"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"स्थान"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"परवानग्या आवश्यक नाहीत"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"यासाठी आपले पैसे खर्च होऊ शकतात"</string>
<string name="dlg_ok" msgid="7376953167039865701">"ठीक"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"चार्जिंगसाठी USB"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"USB हे डिव्हाइस चार्ज करीत आहे"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB संलग्न केलेल्या डिव्हाइसला पॉवरचा पुरवठा करीत आहे"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"स्थानांतरणासाठी USB"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"फोटो स्थानांतरणासाठी USB"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI साठी USB"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"सामायिक करा"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"नकार द्या"</string>
<string name="select_input_method" msgid="8547250819326693584">"कीबोर्ड बदला"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"इतर कीबोर्ड"</string>
<string name="show_ime" msgid="2506087537466597099">"भौतिक कीबोर्ड सक्रिय असताना त्यास स्क्रीनवर ठेवा"</string>
<string name="hardware" msgid="194658061510127999">"व्हर्च्युअल कीबोर्ड दर्शवा"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"कीबोर्ड लेआउट निवडा"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"कीबोर्ड लेआउट निवडण्यासाठी स्पर्श करा."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"वास्तविक कीबोर्ड कॉन्फिगर करा"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"भाषा आणि लेआउट निवडण्यासाठी टॅप करा"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"उमेदवार"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"अधिक पर्याय"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"अंतर्गत सामायिक केलेला संचय"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD कार्ड"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD कार्ड"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB ड्राइव्ह"</string>
diff --git a/core/res/res/values-ms-rMY/strings.xml b/core/res/res/values-ms-rMY/strings.xml
index c2e3bfe..5db9c77 100644
--- a/core/res/res/values-ms-rMY/strings.xml
+++ b/core/res/res/values-ms-rMY/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Kandungan disembunyikan oleh dasar"</string>
<string name="safeMode" msgid="2788228061547930246">"Mod selamat"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistem Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Peribadi"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Tempat Kerja"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Beralih kepada Peribadi"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Beralih kepada Kerja"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kenalan"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"mengakses kenalan anda"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Lokasi"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Tiada kebenaran diperlukan"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"anda mungkin dikenakan bayaran"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB untuk pengecasan"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Mengecas peranti ini melalui USB"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Membekalkan kuasa kepada peranti tersambung melalui USB"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB untuk pemindahan fail"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB untuk pemindahan foto"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB untuk MIDI"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"KONGSI"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"TOLAK"</string>
<string name="select_input_method" msgid="8547250819326693584">"Tukar papan kekunci"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Papan kekunci lain"</string>
<string name="show_ime" msgid="2506087537466597099">"Pastikannya pada skrin, semasa papan kekunci fizikal aktif"</string>
<string name="hardware" msgid="194658061510127999">"Tunjukkan papan kekunci maya"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Pilih susun atur papan kekunci"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Sentuh untuk memilih susun atur papan kekunci."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Konfigurasikan papan kekunci fizikal"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Ketik untuk memilih bahasa dan susun atur"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"calon"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Lagi pilihan"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Storan kongsi dalaman"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Kad SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Kad SD <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Pemacu USB"</string>
diff --git a/core/res/res/values-my-rMM/strings.xml b/core/res/res/values-my-rMM/strings.xml
index 0c55986..5e5ec4e 100644
--- a/core/res/res/values-my-rMM/strings.xml
+++ b/core/res/res/values-my-rMM/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"မူဝါဒမှ အကြောင်းအရာများကို ဝှက်ထားသည်"</string>
<string name="safeMode" msgid="2788228061547930246">"အန္တရာယ်ကင်းမှု စနစ်(Safe mode)"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android စနစ်"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"ကိုယ်ရေး"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"အလုပ်"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"ကိုယ်ပိုင်သီးသန့်အဖြစ် ပြောင်းပါ"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"အလုပ်သို့ ပြောင်းပါ"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"အဆက်အသွယ်များ"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"သင့် အဆက်အသွယ်များအား ဝင်ရောက်သုံးရန်"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"တည်နေရာ"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"ခွင့်ပြုချက်မလိုအပ်ပါ"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"သင့်အတွက် ပိုက်ဆံကုန်ကျနိုင်ပါသည်"</string>
<string name="dlg_ok" msgid="7376953167039865701">"ကောင်းပြီ"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"အားသွင်းရန်အတွက် USB"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"USB ဖြင့်ဤစက်ပစ္စည်းကို အားသွင်းနေသည်"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"ချိတ်ဆက်ထားသည့် စက်ပစ္စည်းကို USB မှတစ်ဆင့် အားသွင်းနေသည်"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"ဖိုင်လွှဲပြောင်းရန်အတွက် USB"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"ဓာတ်ပုံလွှဲပြောင်းရန်အတွက် USB"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI အတွက် USB"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"မျှဝေပါ"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ငြင်းပယ်ပါ"</string>
<string name="select_input_method" msgid="8547250819326693584">"ကီးဘုတ် ပြောင်းလဲရန်"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"အခြားကီးဘုတ်များ"</string>
<string name="show_ime" msgid="2506087537466597099">"စက်၏ကီးဘုတ်ကိုအသုံးပြုနေစဉ် ၎င်းကိုမျက်နှာပြင်ပေါ်တွင် ထားပါ"</string>
<string name="hardware" msgid="194658061510127999">"ကီးဘုတ်အတုပြရန်"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"လက်ကွက် အပြင်အဆင်ရွေးရန်"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"လက်ကွက် အပြင်အဆင်ရွေးရန် တို့ထိပါ"</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"ရုပ်ပိုင်းဆိုင်ရာ အသွင်အပြင်ကို ပြင်ဆင်သတ်မှတ်ပါ"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"ဘာသာစကားနှင့် အသွင်အပြင်ရွေးချယ်ရန် တို့ပါ"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"ရွေးချယ်ခံမည့်သူ"</u></string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 7817321..cfccdeb 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Innholdet er skjult i henhold til retningslinjene"</string>
<string name="safeMode" msgid="2788228061547930246">"Sikkermodus"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android-system"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Personlig"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Jobb"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Bytt til den personlige profilen"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Bytt til jobbprofilen"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontakter"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"se kontaktene dine"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Posisjon"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Trenger ingen rettigheter"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"dette kan koste deg penger"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB for lading"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Enheten lades via USB"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Den tilkoblede enheten får strøm via USB"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB for filoverføring"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB for bildeoverføring"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB for MIDI"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"DEL"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"AVSLÅ"</string>
<string name="select_input_method" msgid="8547250819326693584">"Endre tastatur"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Andre tastaturoppsett"</string>
<string name="show_ime" msgid="2506087537466597099">"Ha den på skjermen mens det fysiske tastaturet er aktivt"</string>
<string name="hardware" msgid="194658061510127999">"Vis det virtuelle tastaturet"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Velg tastaturoppsett"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Trykk for å velge et tastaturoppsett"</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Konfigurer et fysisk tastatur"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Trykk for å velge språk og layout"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZÆØÅ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZÆØÅ"</string>
<string name="candidates_style" msgid="4333913089637062257">"TAG_FONT"<u>"kandidater"</u>"CLOSE_FONT"</string>
diff --git a/core/res/res/values-ne-rNP/strings.xml b/core/res/res/values-ne-rNP/strings.xml
index 459eccd..fb817b5 100644
--- a/core/res/res/values-ne-rNP/strings.xml
+++ b/core/res/res/values-ne-rNP/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"नीतिद्वारा लुकाइएका सामग्री"</string>
<string name="safeMode" msgid="2788228061547930246">"सुरक्षित मोड"</string>
<string name="android_system_label" msgid="6577375335728551336">"एन्ड्रोइड प्रणाली"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"व्यक्तिगत"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"काम"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"व्यक्तिगत प्रोफाइलमा स्विच गर्नुहोस्"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"कार्य प्रोफाइलमा स्विच गर्नुहोस्"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"सम्पर्कहरू"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"तपाईँको सम्पर्कमा पहुँच गर्नुहोस्"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"स्थान"</string>
@@ -1051,7 +1051,8 @@
<string name="no_permissions" msgid="7283357728219338112">"कुनै अनुमति आवश्यक छैन"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"सायद तपाईँलाई पैसा पर्न सक्छ।"</string>
<string name="dlg_ok" msgid="7376953167039865701">"ठिक छ"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"चार्जका लागि USB"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"यस यन्त्रलाई USB मार्फत चार्ज गर्दै"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"संलग्न गरिएको यन्त्रमा USB मार्फत पावर आपूर्ति गरिँदै"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"फाइल स्थानान्तरणको लागि USB"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"तस्बिर स्थानान्तरणको लागि USB"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI को लागि USB"</string>
@@ -1066,11 +1067,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"साझेदारी गर्नुहोस्"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"अस्वीकार गर्नुहोस्"</string>
<string name="select_input_method" msgid="8547250819326693584">"कुञ्जीपाटी परिवर्तन गर्नुहोस्"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"अन्य किबोर्डहरू"</string>
<string name="show_ime" msgid="2506087537466597099">"भौतिक किबोर्ड सक्रिय हुँदा यसलाई स्क्रिनमा राख्नुहोस्"</string>
<string name="hardware" msgid="194658061510127999">"भर्चुअल किबोर्ड देखाउनुहोस्"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"किबोर्ड रूपरेखा चयन गर्नुहोस्"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"किबोर्ड रूपरेखा चयन गर्न टच गर्नुहोस्।"</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"फिजिकल किबोर्डलाई कन्फिगर गर्नुहोस्"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"भाषा र लेआउट चयन गर्न ट्याप गर्नुहोस्"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"उम्मेदवार"</u></string>
@@ -1230,8 +1230,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"थप विकल्पहरू"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"साझेदारी गरिएको आन्तरिक भण्डारण"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD कार्ड"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD कार्ड"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB ड्राइभ"</string>
diff --git a/core/res/res/values-night/themes_material_daynight.xml b/core/res/res/values-night/themes_material_daynight.xml
deleted file mode 100644
index b344582..0000000
--- a/core/res/res/values-night/themes_material_daynight.xml
+++ /dev/null
@@ -1,117 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!--
-===============================================================
- PLEASE READ
-===============================================================
-
-The Material themes must not be modified in order to pass CTS.
-Many related themes and styles depend on other values defined in this file.
-If you would like to provide custom themes and styles for your device,
-please see themes_device_defaults.xml.
-
-===============================================================
- PLEASE READ
-===============================================================
- -->
-<resources>
-
- <!-- Material theme (day/night version) for activities. -->
- <style name="Theme.Material.DayNight" parent="Theme.Material" />
-
- <!-- Variant of Material.DayNight that has a solid (opaque) action bar
- with an inverse color profile. The dark action bar sharply stands out against
- the light content (when applicable). -->
- <style name="Theme.Material.DayNight.DarkActionBar" parent="Theme.Material" />
-
- <!-- Variant of Material.DayNight with no action bar. -->
- <style name="Theme.Material.DayNight.NoActionBar" parent="Theme.Material.NoActionBar" />
-
- <!-- Variant of Material.DayNight that has no title bar and fills
- the entire screen. This theme
- sets {@link android.R.attr#windowFullscreen} to true. -->
- <style name="Theme.Material.DayNight.NoActionBar.Fullscreen" parent="Theme.Material.NoActionBar.Fullscreen" />
-
- <!-- Variant of Material.DayNight that has no title bar and fills
- the entire screen and extends into the display overscan region. This theme
- sets {@link android.R.attr#windowFullscreen} and {@link android.R.attr#windowOverscan}
- to true. -->
- <style name="Theme.Material.DayNight.NoActionBar.Overscan" parent="Theme.Material.NoActionBar.Overscan" />
-
- <!-- Variant of Material.DayNight that has no title bar and translucent
- system decor. This theme sets {@link android.R.attr#windowTranslucentStatus} and
- {@link android.R.attr#windowTranslucentNavigation} to true. -->
- <style name="Theme.Material.DayNight.NoActionBar.TranslucentDecor" parent="Theme.Material.NoActionBar.TranslucentDecor" />
-
- <!-- Default Material.DayNight theme for panel windows. This removes all extraneous
- window decorations, so you basically have an empty rectangle in which
- to place your content. It makes the window floating, with a transparent
- background, and turns off dimming behind the window. -->
- <style name="Theme.Material.DayNight.Panel" parent="Theme.Material.Panel" />
-
- <!-- Material theme (day/night version) for dialog windows and activities,
- which is used by the {@link android.app.Dialog} class. This changes
- the window to be floating (not fill the entire screen), and puts a
- frame around its contents. You can set this theme on an activity if
- you would like to make an activity that looks like a Dialog. -->
- <style name="Theme.Material.DayNight.Dialog" parent="Theme.Material.DayNight.BaseDialog" />
- <style name="Theme.Material.DayNight.BaseDialog" parent="Theme.Material.BaseDialog" />
-
- <!-- Variant of Theme.Material.DayNight.Dialog that has a nice minimum width for
- a regular dialog. -->
- <style name="Theme.Material.DayNight.Dialog.MinWidth" parent="Theme.Material.Dialog.MinWidth" />
-
- <!-- Variant of Theme.Material.DayNight.Dialog that does not include a title bar. -->
- <style name="Theme.Material.DayNight.Dialog.NoActionBar" parent="Theme.Material.Dialog.NoActionBar" />
-
- <!-- Variant of Theme.Material.DayNight.Dialog.NoActionBar that has a nice minimum width for
- a regular dialog. -->
- <style name="Theme.Material.DayNight.Dialog.NoActionBar.MinWidth" parent="Theme.Material.Dialog.NoActionBar.MinWidth" />
-
- <!-- Variant of Theme.Material.DayNight.Dialog that has a fixed size. -->
- <style name="Theme.Material.DayNight.Dialog.FixedSize" parent="Theme.Material.Dialog.FixedSize" />
-
- <!-- Variant of Theme.Material.DayNight.Dialog.NoActionBar that has a fixed size. -->
- <style name="Theme.Material.DayNight.Dialog.NoActionBar.FixedSize" parent="Theme.Material.Dialog.NoActionBar.FixedSize" />
-
- <!-- Theme for a window that will be displayed either full-screen on
- smaller screens (small, normal) or as a dialog on larger screens
- (large, xlarge). -->
- <style name="Theme.Material.DayNight.DialogWhenLarge" parent="Theme.Material.DialogWhenLarge" />
-
- <!-- Theme for a window with a dark action bar that will be displayed
- either full-screen on smaller screens (small, normal) or as a dialog
- on larger screens (large, xlarge). -->
- <style name="Theme.Material.DayNight.DialogWhenLarge.DarkActionBar" parent="Theme.Material.DialogWhenLarge" />
-
- <!-- Theme for a window without an action bar that will be displayed either full-screen
- on smaller screens (small, normal) or as a dialog on larger screens
- (large, xlarge). -->
- <style name="Theme.Material.DayNight.DialogWhenLarge.NoActionBar" parent="Theme.Material.DialogWhenLarge.NoActionBar" />
-
- <!-- Theme for a presentation window on a secondary display. -->
- <style name="Theme.Material.DayNight.Dialog.Presentation" parent="Theme.Material.Dialog.Presentation" />
-
- <!-- Material user theme for alert dialog windows, which is used by the
- {@link android.app.AlertDialog} class. -->
- <style name="Theme.Material.DayNight.Dialog.Alert" parent="Theme.Material.DayNight.Dialog.BaseAlert" />
- <style name="Theme.Material.DayNight.Dialog.BaseAlert" parent="Theme.Material.Dialog.BaseAlert" />
-
- <style name="Theme.Material.DayNight.SearchBar" parent="Theme.Material.SearchBar" />
- <style name="Theme.Material.DayNight.CompactMenu" parent="Theme.Material.CompactMenu" />
-
-</resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index e8279af7..3de8496 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Content verborgen op basis van beleid"</string>
<string name="safeMode" msgid="2788228061547930246">"Veilige modus"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android-systeem"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Persoonlijk"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Werk"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Overschakelen naar persoonlijk profiel"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Overschakelen naar werkprofiel"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Contacten"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"toegang krijgen tot je contacten"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Locatie"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Geen machtigingen vereist"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"hieraan kunnen kosten zijn verbonden"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB voor opladen"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Dit apparaat wordt opgeladen via USB"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Het aangesloten apparaat wordt via USB van voeding voorzien"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB voor bestandsoverdacht"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB voor foto-overdracht"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB voor MIDI"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"DELEN"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"WEIGEREN"</string>
<string name="select_input_method" msgid="8547250819326693584">"Toetsenbord wijzigen"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Andere toetsenborden"</string>
<string name="show_ime" msgid="2506087537466597099">"Dit op het scherm weergeven terwijl het fysieke toetsenbord actief is"</string>
<string name="hardware" msgid="194658061510127999">"Virtueel toetsenbord tonen"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Toetsenbordindeling selecteren"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Tik om een toetsenbordindeling te selecteren."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Fysiek toetsenbord configureren"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Tik om een taal en indeling te selecteren"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"kandidaten"</u></string>
diff --git a/core/res/res/values-pa-rIN/strings.xml b/core/res/res/values-pa-rIN/strings.xml
index f74b93c..87f973f 100644
--- a/core/res/res/values-pa-rIN/strings.xml
+++ b/core/res/res/values-pa-rIN/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"ਨੀਤੀ ਦੁਆਰਾ ਸਮੱਗਰੀ ਲੁਕਾਈ ਗਈ"</string>
<string name="safeMode" msgid="2788228061547930246">"ਸੁਰੱਖਿਅਤ ਮੋਡ"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android System"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"ਨਿੱਜੀ"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"ਕੰਮ"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"ਨਿੱਜੀ \'ਤੇ ਸਵਿੱਚ ਕਰੋ"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"ਕੰਮ \'ਤੇ ਸਵਿੱਚ ਕਰੋ"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"ਸੰਪਰਕ"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"ਆਪਣੇ ਸੰਪਰਕਾਂ ਨੂੰ ਐਕਸੈਸ ਕਰੋ"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"ਨਿਰਧਾਰਿਤ ਸਥਾਨ"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"ਕੋਈ ਅਨੁਮਤੀਆਂ ਲੁੜੀਂਦੀਆਂ ਨਹੀਂ"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"ਇਸ ਨਾਲ ਤੁਹਾਨੂੰ ਖ਼ਰਚਾ ਪੈ ਸਕਦਾ ਹੈ"</string>
<string name="dlg_ok" msgid="7376953167039865701">"ਠੀਕ"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"ਚਾਰਜਿੰਗ ਲਈ USB"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"ਇਹ ਡੀਵਾਈਸ USB ਰਾਹੀਂ ਚਾਰਜ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"ਨੱਥੀ ਕੀਤੀ ਡੀਵਾਈਸ ਨੂੰ USB ਰਾਹੀਂ ਪਾਵਰ ਮਿਲ ਰਹੀ ਹੈ"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"ਫ਼ਾਈਲ ਟ੍ਰਾਂਸਫ਼ਰ ਲਈ USB"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"ਫੋਟੋ ਟ੍ਰਾਂਸਫ਼ਰ ਲਈ USB"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI ਲਈ USB"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"ਸਾਂਝੀ ਕਰੋ"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ਅਸਵੀਕਾਰ ਕਰੋ"</string>
<string name="select_input_method" msgid="8547250819326693584">"ਕੀਬੋਰਡ ਬਦਲੋ"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"ਹੋਰ ਕੀ-ਬੋਰਡ"</string>
<string name="show_ime" msgid="2506087537466597099">"ਭੌਤਿਕ ਕੀ-ਬੋਰਡ ਸਰਗਰਮ ਹੋਣ ਦੌਰਾਨ ਇਸ ਨੂੰ ਸਕ੍ਰੀਨ \'ਤੇ ਬਣਾਈ ਰੱਖੋ"</string>
<string name="hardware" msgid="194658061510127999">"ਵਰਚੁਅਲ ਕੀ-ਬੋਰਡ ਵਿਖਾਓ"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"ਕੀਬੋਰਡ ਲੇਆਊਟ ਚੁਣੋ"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"ਇੱਕ ਕੀਬੋਰਡ ਲੇਆਊਟ ਚੁਣਨ ਲਈ ਛੋਹਵੋ।"</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"ਭੌਤਿਕ ਕੀ-ਬੋਰਡ ਦਾ ਸੰਰੂਪਣ ਕਰੋ"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"ਭਾਸ਼ਾ ਅਤੇ ਖਾਕਾ ਚੁਣਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"ਉਮੀਦਵਾਰ"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"ਹੋਰ ਚੋਣਾਂ"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"ਅੰਦਰੂਨੀ ਸਾਂਝੀ ਕੀਤੀ ਗਈ ਸਟੋਰੇਜ"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD ਕਾਰਡ"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD ਕਾਰਡ"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB ਡ੍ਰਾਇਵ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 6e4091a..6be9a9d 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -237,8 +237,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Treść ukryta z powodu zasad"</string>
<string name="safeMode" msgid="2788228061547930246">"Tryb awaryjny"</string>
<string name="android_system_label" msgid="6577375335728551336">"System Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Osobiste"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Praca"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Włącz profil osobisty"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Włącz profil do pracy"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontakty"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"dostęp do kontaktów"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Lokalizacja"</string>
@@ -1061,7 +1061,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Nie są wymagane żadne uprawnienia"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"to może generować dodatkowe koszty"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB w trybie ładowania"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Ładowanie urządzenia przez USB"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Zasilanie urządzenia przez USB"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB w trybie przesyłania plików"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB w trybie przesyłania zdjęć"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB w trybie MIDI"</string>
@@ -1076,11 +1077,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"UDOSTĘPNIJ"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ODRZUĆ"</string>
<string name="select_input_method" msgid="8547250819326693584">"Zmień klawiaturę"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Inne klawiatury"</string>
<string name="show_ime" msgid="2506087537466597099">"Pozostaw na ekranie, gdy aktywna jest klawiatura fizyczna"</string>
<string name="hardware" msgid="194658061510127999">"Pokaż klawiaturę wirtualną"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Wybierz układ klawiatury"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Kliknij, by wybrać układ klawiatury."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Skonfiguruj klawiaturę fizyczną"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Kliknij, by wybrać język i układ"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" AĄBCĆDEĘFGHIJKLŁMNŃOÓPQRSŚTUVWXYZŹŻ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"kandydaci"</u></string>
@@ -1242,8 +1242,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Więcej opcji"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Wewnętrzna pamięć współdzielona"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Karta SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Karta SD (<xliff:g id="MANUFACTURER">%s</xliff:g>)"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Dysk USB"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 62ee01c..c8004bf 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Conteúdo ocultado pela política"</string>
<string name="safeMode" msgid="2788228061547930246">"Modo de segurança"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Pessoal"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Trabalho"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Alternar para \"Pessoal\""</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Alternar para \"Trabalho\""</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Contatos"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"acesse seus contatos"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Local"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Nenhuma permissão necessária"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"isso pode lhe custar dinheiro"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB para carregamento"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"USB carregando este dispositivo"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB fornecendo energia ao dispositivo conectado"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB para transferência de arquivos"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB para transferência de fotos"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB para MIDI"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"COMPARTILHAR"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"RECUSAR"</string>
<string name="select_input_method" msgid="8547250819326693584">"Alterar teclado"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Outros teclados"</string>
<string name="show_ime" msgid="2506087537466597099">"Manter na tela enquanto o teclado físico estiver ativo"</string>
<string name="hardware" msgid="194658061510127999">"Mostrar teclado virtual"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Selecione o layout de teclado"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Toque para selecionar um layout de teclado."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Configurar teclado físico"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Toque para selecionar o idioma e o layout"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"candidatos"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Mais opções"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Armazenamento interno compartilhado"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Cartão SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Cartão SD <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Drive USB"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 331cd9d..c2ba8ad 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Conteúdo ocultado pela política"</string>
<string name="safeMode" msgid="2788228061547930246">"Modo seguro"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Pessoal"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Trabalho"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Mudar para pessoal"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Mudar para trabalho"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Contactos"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"aceder aos contactos"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Localização"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Não são necessárias permissões"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"isto poderá estar sujeito a custos"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB para carregamento"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Carregamento deste dispositivo por USB"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Fornecimento de energia ao dispositivo ligado por USB"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB para transferência de ficheiros"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB para transferência de fotos"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB para MIDI"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"PARTILHAR"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"RECUSAR"</string>
<string name="select_input_method" msgid="8547250819326693584">"Alterar teclado"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Outros teclados"</string>
<string name="show_ime" msgid="2506087537466597099">"Manter no ecrã enquanto o teclado físico estiver ativo"</string>
<string name="hardware" msgid="194658061510127999">"Mostrar o teclado virtual"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Selecionar esquema de teclado"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Toque para selecionar um esquema de teclado."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Configurar teclado físico"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Toque para selecionar o idioma e o esquema"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"candidatos"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Mais opções"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Armazenamento interno partilhado"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Cartão SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Cartão SD <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Unidade USB"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 62ee01c..c8004bf 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Conteúdo ocultado pela política"</string>
<string name="safeMode" msgid="2788228061547930246">"Modo de segurança"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Pessoal"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Trabalho"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Alternar para \"Pessoal\""</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Alternar para \"Trabalho\""</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Contatos"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"acesse seus contatos"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Local"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Nenhuma permissão necessária"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"isso pode lhe custar dinheiro"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB para carregamento"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"USB carregando este dispositivo"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB fornecendo energia ao dispositivo conectado"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB para transferência de arquivos"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB para transferência de fotos"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB para MIDI"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"COMPARTILHAR"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"RECUSAR"</string>
<string name="select_input_method" msgid="8547250819326693584">"Alterar teclado"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Outros teclados"</string>
<string name="show_ime" msgid="2506087537466597099">"Manter na tela enquanto o teclado físico estiver ativo"</string>
<string name="hardware" msgid="194658061510127999">"Mostrar teclado virtual"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Selecione o layout de teclado"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Toque para selecionar um layout de teclado."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Configurar teclado físico"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Toque para selecionar o idioma e o layout"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"candidatos"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Mais opções"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Armazenamento interno compartilhado"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Cartão SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Cartão SD <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Drive USB"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index dd4faad..7485e65 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -235,8 +235,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Conținutul este ascuns conform politicii"</string>
<string name="safeMode" msgid="2788228061547930246">"Mod sigur"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistemul Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Personal"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Serviciu"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Comutați la Personal"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Comutați la Serviciu"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Persoane de contact"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"acceseze persoanele de contact"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Locație"</string>
@@ -1053,7 +1053,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Nu se solicită nicio permisiune"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"aceasta poate să genereze costuri"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"Conexiune USB pentru încărcare"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Dispozitivul se încarcă prin USB"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Dispozitivul atașat se încarcă prin USB"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"Conexiune USB pentru transferul fișierelor"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"Conexiune USB pentru transferul fotografiilor"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"Conexiune USB pentru MIDI"</string>
@@ -1068,11 +1069,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"TRIMITEȚI"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"REFUZAȚI"</string>
<string name="select_input_method" msgid="8547250819326693584">"Schimbați tastatura"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Alte tastaturi"</string>
<string name="show_ime" msgid="2506087537466597099">"Se păstrează pe ecran cât timp este activată tastatura fizică"</string>
<string name="hardware" msgid="194658061510127999">"Afișați tastatura virtuală"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Selectați aspectul tastaturii"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Atingeți pentru a selecta un aspect de tastatură."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Configurați tastatura fizică"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Atingeți pentru a selecta limba și aspectul"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"candidați"</u></string>
@@ -1233,8 +1233,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Mai multe opțiuni"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Memorie internă comună"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Card SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Card SD <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Unitate USB"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 4d618ac..5926bfd 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -237,8 +237,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Содержимое скрыто в соответствии с заданными правилами"</string>
<string name="safeMode" msgid="2788228061547930246">"Безопасный режим"</string>
<string name="android_system_label" msgid="6577375335728551336">"Система Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Личные данные"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Работа"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Перейти в личный профиль"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Перейти в рабочий профиль"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Контакты"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"доступ к контактам"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Местоположение"</string>
@@ -1061,7 +1061,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Не требуется разрешений"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"это может стоить вам денег!"</string>
<string name="dlg_ok" msgid="7376953167039865701">"ОК"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"Зарядка через USB"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Зарядка через USB"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Подача питания на подключенное устройство через USB"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"Передача файлов через USB"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"Передача фото через USB"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI через USB"</string>
@@ -1076,11 +1077,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"ПРЕДОСТАВИТЬ ДОСТУП"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ОТКЛОНИТЬ"</string>
<string name="select_input_method" msgid="8547250819326693584">"Выбор раскладки"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Другие клавиатуры"</string>
<string name="show_ime" msgid="2506087537466597099">"Показывать на экране, когда физическая клавиатура включена"</string>
<string name="hardware" msgid="194658061510127999">"Виртуальная клавиатура"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Выберите раскладку клавиатуры"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Нажмите, чтобы выбрать раскладку клавиатуры."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Настройка физической клавиатуры"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Нажмите, чтобы выбрать язык и раскладку"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"варианты"</u></string>
@@ -1242,8 +1242,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Ещё"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Внутренний общий накопитель"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD-карта"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"SD-карта <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB-накопитель"</string>
diff --git a/core/res/res/values-si-rLK/strings.xml b/core/res/res/values-si-rLK/strings.xml
index 5dbb922..6c76da0 100644
--- a/core/res/res/values-si-rLK/strings.xml
+++ b/core/res/res/values-si-rLK/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"ප්රතිපත්තිය විසින් අන්තර්ගතය සඟවන ලදී"</string>
<string name="safeMode" msgid="2788228061547930246">"ආරක්ෂිත ආකාරය"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android පද්ධතිය"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"පෞද්ගලික"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"කාර්යාලය"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"පුද්ගලික වෙත මාරු වන්න"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"කාර්යාලය වෙත මාරු වන්න"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"සම්බන්ධතා"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"ඔබේ සම්බන්ධතාවලට පිවිසෙන්න"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"ස්ථානය"</string>
@@ -1047,7 +1047,8 @@
<string name="no_permissions" msgid="7283357728219338112">"අවසර අවශ්ය නොමැත"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"මෙමඟින් ඔබට මුදල් වැය විය හැක"</string>
<string name="dlg_ok" msgid="7376953167039865701">"හරි"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"ආරෝපණය කිරීම සඳහා USB"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"මෙම උපාංගය USB වෙතින් ආරෝපණය"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"අමුණා ඇති උපාංගයට USB මඟින් බලය සපයමින්"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"ගොනු හුවමාරුව සඳහා USB"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"ඡායාරූප හුවමාරුව සඳහා USB"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI සඳහා USB"</string>
@@ -1062,11 +1063,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"බෙදා ගන්න"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ප්රතික්ෂේප කරන්න"</string>
<string name="select_input_method" msgid="8547250819326693584">"යතුරු පුවරු වෙනස් කිරීම"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"වෙනත් යතුරු පුවරු"</string>
<string name="show_ime" msgid="2506087537466597099">"භෞතික යතුරු පුවරුව සක්රිය අතරතුර එය තිරය මත තබා ගන්න"</string>
<string name="hardware" msgid="194658061510127999">"අතථ්ය යතුරු පුවරුව පෙන්වන්න"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"යතුරු පුවරුවට පිරිසැලැස්ම තෝරන්න"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"යතුරු පුවරුවට පිරිසැලැස්මක් තේරීමට ස්පර්ශ කරන්න."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"භෞතික යතුරු පුවරුව වින්යාස කරන්න"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"භාෂාව හා පිරිසැලසුම තේරීමට තට්ටු කරන්න"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"අපේක්ෂකයන්"</u></string>
@@ -1226,8 +1226,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"තවත් විකල්ප"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"අභ්යන්තර බෙදා ගත් ගබඩාව"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD පත"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD කාඩ්පත"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB ධාවකය"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 75a2129..ec45070 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -237,8 +237,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Obsah je na základe pravidiel skrytý"</string>
<string name="safeMode" msgid="2788228061547930246">"Núdzový režim"</string>
<string name="android_system_label" msgid="6577375335728551336">"Systém Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Osobné"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Práca"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Prepnúť na osobný"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Prepnúť na pracovný"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontakty"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"prístup k vašim kontaktom"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Poloha"</string>
@@ -1061,7 +1061,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Nevyžadujú sa žiadne oprávnenia."</string>
<string name="perm_costs_money" msgid="4902470324142151116">"môžu sa vám účtovať poplatky"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB na nabíjanie"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Prebieha nabíjanie tohto zariadenia pomocou USB"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Prebieha nabíjanie pripojeného zariadenia pomocou USB"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB na prenos súborov"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB na prenos fotiek"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB na pripojenie zariadenia MIDI"</string>
@@ -1076,11 +1077,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"ZDIEĽAŤ"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ODMIETNUŤ"</string>
<string name="select_input_method" msgid="8547250819326693584">"Zmeniť klávesnicu"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Ďalšie klávesnice"</string>
<string name="show_ime" msgid="2506087537466597099">"Ponechať na obrazovke, keď je aktívna fyzická klávesnica"</string>
<string name="hardware" msgid="194658061510127999">"Zobraziť virtuálnu klávesnicu"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Zvoľte rozloženie klávesnice"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Dotykom zvoľte rozloženie klávesnice."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Konfigurácia fyzickej klávesnice"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Klepnutím vyberte jazyk a rozloženie"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" AÁÄBCČDĎDZDŽEÉFGHCHIÍJKLĽMNŇOÓÔPRŔSŠTŤUÚVWXYÝZŽ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"kandidáti"</u></string>
@@ -1242,8 +1242,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Viac možností"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Interné zdieľané úložisko"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD karta"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"SD karta <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Disk USB"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 579880a..b2dba7e0 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -237,8 +237,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Pravilnik je skril vsebino"</string>
<string name="safeMode" msgid="2788228061547930246">"Varni način"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistem Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Osebno"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Služba"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Preklop na osebni profil"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Preklop na delovni profil"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Stiki"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"dostop do stikov"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Lokacija"</string>
@@ -1061,7 +1061,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Ni zahtevanih dovoljenj"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"to je lahko plačljivo"</string>
<string name="dlg_ok" msgid="7376953167039865701">"V redu"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB za polnjenje"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Polnjenje akumulatorja v napravi prek USB-ja"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Napajanje priključene naprave prek USB-ja"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB za prenos datotek"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB za prenos fotografij"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB za MIDI"</string>
@@ -1076,11 +1077,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"SKUPNA RABA"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"NE SPREJMEM"</string>
<string name="select_input_method" msgid="8547250819326693584">"Sprememba tipkovnice"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Druge tipkovnice"</string>
<string name="show_ime" msgid="2506087537466597099">"Ohrani na zaslonu, dokler je aktivna fizična tipkovnica"</string>
<string name="hardware" msgid="194658061510127999">"Pokaži navidezno tipkovnico"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Izberite razporeditev tipkovnice"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Dotaknite se, da izberete razporeditev tipkovnice"</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Konfiguriranje fizične tipkovnice"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Dotaknite se, če želite izbrati jezik in postavitev."</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"kandidati"</u></string>
@@ -1242,8 +1242,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Več možnosti"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Notranja shramba v skupni rabi"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Kartica SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Kartica SD <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Pogon USB"</string>
diff --git a/core/res/res/values-sq-rAL/strings.xml b/core/res/res/values-sq-rAL/strings.xml
index b39f297..1846399 100644
--- a/core/res/res/values-sq-rAL/strings.xml
+++ b/core/res/res/values-sq-rAL/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Përmbajtja është e fshehur për shkak të politikës"</string>
<string name="safeMode" msgid="2788228061547930246">"Modaliteti i sigurisë"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistemi \"android\""</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Personal"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Puna"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Ndryshoje te \"Personale\""</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Ndryshoje te \"Puna\""</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontaktet"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"qasu te kontaktet e tua"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Vendndodhja"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Nuk kërkohen leje"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"kjo mund të të kushtojë para"</string>
<string name="dlg_ok" msgid="7376953167039865701">"Në rregull"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB-ja për ngarkim"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Kjo pajisje ngarkohet me USB"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Pajisja e lidhur furnizohet me enrgji me USB"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB për transferimin e skedarëve"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB për transferimin e fotografive"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB për MIDI"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"SHPËRNDA"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"REFUZO"</string>
<string name="select_input_method" msgid="8547250819326693584">"Ndërro tastierë"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Tastierat e tjera"</string>
<string name="show_ime" msgid="2506087537466597099">"Mbaje në ekran ndërsa tastiera fizike është aktive"</string>
<string name="hardware" msgid="194658061510127999">"Shfaq tastierën virtuale"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Përzgjidh planin e tastierës"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Prek për të përzgjedhur tastierën."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Konfiguro tastierën fizike"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Trokit për të zgjedhur gjuhën dhe strukturën"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"kandidatë"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Opsione të tjera"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Hapësira ruajtëse e brendshme e ndarë"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Karta SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Karta SD nga <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB-ja"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 74fc273..2112dbb 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -235,8 +235,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Садржај је сакривен смерницама"</string>
<string name="safeMode" msgid="2788228061547930246">"Безбедни режим"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android систем"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Лично"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Посао"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Пређи на Лични профил"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Пређи на профил за Work"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Контакти"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"приступи контактима"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Локација"</string>
@@ -1053,7 +1053,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Није потребна ниједна дозвола"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"ово ће вам можда бити наплаћено"</string>
<string name="dlg_ok" msgid="7376953167039865701">"Потврди"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB за пуњење"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"USB пуни овај уређај"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB снабдева енергијом прикључени уређај"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB за пренос датотека"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB за пренос слика"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB за MIDI"</string>
@@ -1068,11 +1069,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"ДЕЛИ"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ОДБИЈ"</string>
<string name="select_input_method" msgid="8547250819326693584">"Промените тастатуру"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Друге тастатуре"</string>
<string name="show_ime" msgid="2506087537466597099">"Задржи га на екрану док је физичка тастатура активна"</string>
<string name="hardware" msgid="194658061510127999">"Прикажи виртуелну тастатуру"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Избор распореда тастатуре"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Додирните да бисте изабрали распоред тастатуре."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Конфигуришите физичку тастатуру"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Додирните да бисте изабрали језик и распоред"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"кандидати"</u></string>
@@ -1233,8 +1233,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Још опција"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Унутрашњи дељени меморијски простор"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD картица"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD картица"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB диск"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 610fa07..832d4a2 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Innehåll har dolts p.g.a. en policy"</string>
<string name="safeMode" msgid="2788228061547930246">"Säkert läge"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android-system"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Personligt"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Arbetet"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Byt till din personliga profil"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Byt till jobbprofilen"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontakter"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"få tillgång till dina kontakter"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Plats"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Inga behörigheter krävs"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"detta kan kosta pengar"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB för laddning"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Enheten laddas via USB"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"En ansluten enhet strömförsörjs via USB"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB för överföring av filer"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB för överföring av foton"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB för MIDI"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"DELA"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"AVVISA"</string>
<string name="select_input_method" msgid="8547250819326693584">"Byt tangentbord"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Andra tangentbord"</string>
<string name="show_ime" msgid="2506087537466597099">"Ha kvar den på skärmen när det fysiska tangentbordet används"</string>
<string name="hardware" msgid="194658061510127999">"Visa virtuellt tangentbord"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Välj en tangentbordslayout"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Välj en tangentbordslayout genom att trycka."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Konfigurera fysiskt tangentbord"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Tryck om du vill välja språk och layout"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"kandidater"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Fler alternativ"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Delat internt lagringsutrymme"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD-kort"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"SD-kort (<xliff:g id="MANUFACTURER">%s</xliff:g>)"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB-enhet"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 3d4bc60d..e0a32f6 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -235,8 +235,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Maudhui yamefichwa kulingana na sera"</string>
<string name="safeMode" msgid="2788228061547930246">"Mtindo salama"</string>
<string name="android_system_label" msgid="6577375335728551336">"Mfumo wa Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Binafsi"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Kazini"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Badili uweke wasifu wa Binafsi"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Badili uweke wasifu wa Kazini"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Anwani"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"ifikie anwani zako"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Mahali"</string>
@@ -1047,7 +1047,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Hakuna vibali vinavyohitajika"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"huenda hii ikakugharimu pesa"</string>
<string name="dlg_ok" msgid="7376953167039865701">"Sawa"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB kwa ajili ya kuchaji"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Unachaji kifaa hiki ukitumia USB"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB inachaji kifaa ulichounganisha"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB kwa ajili ya kuhamisha faili"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB kwa ajili ya kuhamisha picha"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB kwa ajili ya MIDI"</string>
@@ -1062,11 +1063,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"SHIRIKI"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"KATAA"</string>
<string name="select_input_method" msgid="8547250819326693584">"Badilisha kibodi"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Kibodi zingine"</string>
<string name="show_ime" msgid="2506087537466597099">"Iweke kwenye skrini wakati kibodi inapotumika"</string>
<string name="hardware" msgid="194658061510127999">"Onyesha kibodi pepe"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Teua mpangilio wa kibodi"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Gusa ili kuchagua mpangilio wa kibodi."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Sanidi kibodi halisi"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Gonga ili uchague lugha na muundo"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"wagombeaji"</u></string>
@@ -1226,8 +1226,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Chaguo zaidi"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Hifadhi ya ndani inayoshirikiwa"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Kadi ya SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Kadi ya SD iliyotengenezwa na <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Hifadhi ya USB"</string>
diff --git a/core/res/res/values-sw600dp/dimens_material.xml b/core/res/res/values-sw600dp/dimens_material.xml
index 3bbb352..1ec5c0f 100644
--- a/core/res/res/values-sw600dp/dimens_material.xml
+++ b/core/res/res/values-sw600dp/dimens_material.xml
@@ -23,6 +23,8 @@
<dimen name="action_bar_default_height_material">64dp</dimen>
<!-- Default content inset of an action bar. -->
<dimen name="action_bar_content_inset_material">24dp</dimen>
+ <!-- Default content inset of an action bar with navigation present. -->
+ <dimen name="action_bar_content_inset_with_nav">80dp</dimen>
<!-- Default start padding of an action bar. -->
<dimen name="action_bar_default_padding_start_material">8dp</dimen>
diff --git a/core/res/res/values-ta-rIN/strings.xml b/core/res/res/values-ta-rIN/strings.xml
index 9411f5b..773301f 100644
--- a/core/res/res/values-ta-rIN/strings.xml
+++ b/core/res/res/values-ta-rIN/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"கொள்கையின்படி உள்ளடக்கம் மறைக்கப்பட்டது"</string>
<string name="safeMode" msgid="2788228061547930246">"பாதுகாப்பு பயன்முறை"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android அமைப்பு"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"தனிப்பட்ட"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"பணியிடம்"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"தனிப்பட்ட சுயவிவரத்திற்கு மாறு"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"பணிச் சுயவிவரத்திற்கு மாறு"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"தொடர்புகள்"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"தொடர்புகளை அணுகும்"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"இருப்பிடம்"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"அனுமதிகள் தேவையில்லை"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"இதனால் நீங்கள் கட்டணம் செலுத்த வேண்டியிருக்கலாம்"</string>
<string name="dlg_ok" msgid="7376953167039865701">"சரி"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB, சார்ஜ் செய்வதற்கு மட்டும்"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"இந்தச் சாதனத்தை USB சார்ஜ் செய்கிறது"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"இணைத்துள்ள சாதனத்திற்கு USB சக்தி அளிக்கிறது"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB, கோப்புப் பரிமாற்றத்துக்கு மட்டும்"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB, படப் பரிமாற்றத்துக்கு மட்டும்"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB, MIDIக்கு மட்டும்"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"பகிர்"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"வேண்டாம்"</string>
<string name="select_input_method" msgid="8547250819326693584">"விசைப்பலகையை மாற்று"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"பிற விசைப்பலகைகள்"</string>
<string name="show_ime" msgid="2506087537466597099">"கைமுறை விசைப்பலகை இயக்கத்தில் இருக்கும் போது IMEஐ திரையில் வைத்திரு"</string>
<string name="hardware" msgid="194658061510127999">"விர்ச்சுவல் விசைப்பலகையை காட்டு"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"விசைப்பலகைத் தளவமைப்பைத் தேர்ந்தெடுக்கவும்"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"விசைப்பலகைத் தளவமைப்பைத் தேர்ந்தெடுக்க தொடவும்."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"கைமுறை விசைப்பலகையை உள்ளமைக்கவும்"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"மொழியையும் தளவமைப்பையும் தேர்ந்தெடுக்க, தட்டவும்"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"கேன்டிடேட்ஸ்"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"மேலும் விருப்பங்கள்"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"பகிர்ந்த சேமிப்பகம் (அகம்)"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD கார்டு"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD கார்டு"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB டிரைவ்"</string>
diff --git a/core/res/res/values-te-rIN/strings.xml b/core/res/res/values-te-rIN/strings.xml
index 9beb1e1..4809c98 100644
--- a/core/res/res/values-te-rIN/strings.xml
+++ b/core/res/res/values-te-rIN/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"విధానం ద్వారా కంటెంట్లు దాచబడ్డాయి"</string>
<string name="safeMode" msgid="2788228061547930246">"సురక్షిత మోడ్"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android సిస్టమ్"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"వ్యక్తిగతం"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"కార్యాలయం"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"వ్యక్తిగతానికి మార్చు"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"కార్యాలయానికి మార్చు"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"పరిచయాలు"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"మీ పరిచయాలను ప్రాప్యత చేయడానికి"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"స్థానం"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"అనుమతులు అవసరం లేదు"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"దీనికి మీకు డబ్బు ఖర్చు కావచ్చు"</string>
<string name="dlg_ok" msgid="7376953167039865701">"సరే"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"ఛార్జింగ్ కోసం USB"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"ఈ పరికరం USB మోడ్లో ఛార్జ్ అవుతోంది"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"జోడించిన పరికరానికి USB ద్వారా పవర్ సరఫరా అవుతోంది"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"ఫైల్ బదిలీ కోసం USB"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"ఫోటో బదిలీ కోసం USB"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI కోసం USB"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"భాగస్వామ్యం చేయి"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"తిరస్కరిస్తున్నాను"</string>
<string name="select_input_method" msgid="8547250819326693584">"కీబోర్డ్ను మార్చు"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"ఇతర కీబోర్డ్లు"</string>
<string name="show_ime" msgid="2506087537466597099">"దీన్ని భౌతిక కీబోర్డ్ సక్రియంగా ఉన్నప్పుడు స్క్రీన్పై ఉంచుతుంది"</string>
<string name="hardware" msgid="194658061510127999">"వర్చువల్ కీబోర్డ్ను చూపు"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"కీబోర్డ్ లేఅవుట్ను ఎంచుకోండి"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"కీబోర్డ్ లేఅవుట్ను ఎంచుకోవడానికి తాకండి."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"భౌతిక కీబోర్డుని కాన్ఫిగర్ చేయండి"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"భాష మరియు లేఅవుట్ను ఎంచుకోవడానికి నొక్కండి"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"క్యాండిడేట్లు"</u></string>
diff --git a/core/res/res/values-television/config.xml b/core/res/res/values-television/config.xml
index 58d3d91..c0716e9 100644
--- a/core/res/res/values-television/config.xml
+++ b/core/res/res/values-television/config.xml
@@ -26,12 +26,4 @@
<!-- Default bounds [left top right bottom] on screen for picture-in-picture windows. -->
<string translatable="false" name="config_defaultPictureInPictureBounds">"1328 54 1808 324"</string>
-
- <!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows, when the PIP
- is located in center. -->
- <string translatable="false" name="config_centeredPictureInPictureBounds">"596 280 1324 690"</string>
-
- <!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows,
- when the PIP is shown with Recents. -->
- <string translatable="false" name="config_pictureInPictureBoundsInRecents">"1484 96 1804 276"</string>
</resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 353da8a..0ea2e52 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"มีการซ่อนเนื้อหาโดยนโยบาย"</string>
<string name="safeMode" msgid="2788228061547930246">"โหมดปลอดภัย"</string>
<string name="android_system_label" msgid="6577375335728551336">"ระบบ Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"ส่วนตัว"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"ที่ทำงาน"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"เปลี่ยนไปใช้โปรไฟล์ส่วนตัว"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"เปลี่ยนไปใช้โปรไฟล์งาน"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"รายชื่อติดต่อ"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"เข้าถึงรายชื่อติดต่อ"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"ตำแหน่ง"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"ไม่ต้องการการอนุญาต"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"รายการนี้อาจมีการเรียกเก็บเงิน"</string>
<string name="dlg_ok" msgid="7376953167039865701">"ตกลง"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB สำหรับการชาร์จ"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"กำลังชาร์จอุปกรณ์นี้ด้วย USB"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"กำลังจ่ายไฟให้อุปกรณ์ที่เชื่อมต่ออยู่ผ่าน USB"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB สำหรับการโอนไฟล์"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB สำหรับการโอนรูปภาพ"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB สำหรับ MIDI"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"แชร์"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ปฏิเสธ"</string>
<string name="select_input_method" msgid="8547250819326693584">"เปลี่ยนแป้นพิมพ์"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"แป้นพิมพ์อื่นๆ"</string>
<string name="show_ime" msgid="2506087537466597099">"เปิดทิ้งไว้บนหน้าจอในระหว่างใช้งานแป้นพิมพ์จริง"</string>
<string name="hardware" msgid="194658061510127999">"แสดงแป้นพิมพ์เสมือน"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"เลือกรูปแบบแป้นพิมพ์"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"แตะเพื่อเลือกรูปแบบแป้นพิมพ์"</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"กำหนดค่าแป้นพิมพ์จริง"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"แตะเพื่อเลือกภาษาและรูปแบบ"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรลวศษสหฬอฮ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรลวศษสหฬอฮ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"ตัวเลือก"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"ตัวเลือกเพิ่มเติม"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"ที่จัดเก็บข้อมูลที่ใช้ร่วมกันภายใน"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"การ์ด SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"การ์ด SD ของ <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"ไดรฟ์ USB"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 41ada1c..00918a4 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Itinago ang mga content alinsunod sa patakaran"</string>
<string name="safeMode" msgid="2788228061547930246">"Safe mode"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android System"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Personal"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Trabaho"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Lumipat sa Personal"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Lumipat sa para sa Trabaho"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Mga Contact"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"ina-access ang iyong mga contact"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Lokasyon"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Walang mga kinakailangang pahintulot"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"maaari itong magdulot ng gastos sa iyo"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB para sa pagcha-charge"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"China-charge sa USB ang device na ito"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB ang nagbibigay ng power sa nakakabit na device"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB para sa paglipat ng file"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB para sa paglipat ng larawan"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB para sa MIDI"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"IBAHAGI"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"TANGGIHAN"</string>
<string name="select_input_method" msgid="8547250819326693584">"Baguhin ang keyboard"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Iba pang mga keyboard"</string>
<string name="show_ime" msgid="2506087537466597099">"Panatilihin ito sa screen habang aktibo ang pisikal na keyboard"</string>
<string name="hardware" msgid="194658061510127999">"Ipakita ang virtual keyboard"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Pumili ng layout ng keyboard"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Pindutin upang pumili ng layout ng keyboard."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"I-configure ang pisikal na keyboard"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"I-tap upang pumili ng wika at layout"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"mga kandidato"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Higit pang mga pagpipilian"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Internal na nakabahaging storage"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD card"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD card"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB drive"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 3a04463..4db8264 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"İçerikler politika nedeniyle gizlendi"</string>
<string name="safeMode" msgid="2788228061547930246">"Güvenli mod"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android Sistemi"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Kişisel"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"İş"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Kişisel Profile Geç"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"İş Profiline Geç"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kişiler"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"kişilerinize erişme"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Konum"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"İzin gerektirmez"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"bunun için sizden ücret alınabilir"</string>
<string name="dlg_ok" msgid="7376953167039865701">"Tamam"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"Şarj için USB"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Bu cihaz USB\'den şarj oluyor"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB, bağlı cihaza güç sağlıyor"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"Dosya aktarımı için USB"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"Fotoğraf aktarımı için USB"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI için USB"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"PAYLAŞ"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"REDDET"</string>
<string name="select_input_method" msgid="8547250819326693584">"Klavyeyi değiştir"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Diğer klavyeler"</string>
<string name="show_ime" msgid="2506087537466597099">"Fiziksel klavye etkin durumdayken ekranda tut"</string>
<string name="hardware" msgid="194658061510127999">"Sanal klavyeyi göster"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Klavye düzeni seçin"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Bir klavye düzeni seçmek için dokunun."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Fiziksel klavyeyi yapılandırın"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Dili ve düzeni seçmek için hafifçe dokunun"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"adaylar"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Diğer seçenekler"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Dahili olarak paylaşılan depolama alanı"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD kart"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD kartı"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB sürücü"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 8142aa0..fe7be12 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -237,8 +237,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Вміст сховано згідно з правилом"</string>
<string name="safeMode" msgid="2788228061547930246">"Безп. режим"</string>
<string name="android_system_label" msgid="6577375335728551336">"Система Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Особисті дані"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Службовий профіль"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Перейти в особистий профіль"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Перейти в робочий профіль"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Контакти"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"отримувати доступ до контактів"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Геодані"</string>
@@ -1061,7 +1061,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Дозвіл не потрібний"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"це платна послуга"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB для заряджання"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"USB-кабель, через який заряджається цей пристрій"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB-кабель, через який живиться під’єднаний пристрій"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB для перенесення файлів"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB для перенесення фотографій"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB для режиму MIDI"</string>
@@ -1076,11 +1077,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"ПОДІЛИТИСЯ"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ВІДХИЛИТИ"</string>
<string name="select_input_method" msgid="8547250819326693584">"Змінити клавіатуру"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Інші клавіатури"</string>
<string name="show_ime" msgid="2506087537466597099">"Утримуйте на екрані, коли активна фізична клавіатура"</string>
<string name="hardware" msgid="194658061510127999">"Показати віртуальну клавіатуру"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Виберіть розкладку клавіатури"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Торкніться, щоб вибрати розкладку клавіатури."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Налаштуйте фізичну клавіатуру"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Торкніться, щоб вибрати мову та розкладку"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" АБВГҐДЕЄЖЗИІЇЙКЛМНОПРСТУФХЦЧШЩЬЮЯ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789АБВГҐДЕЄЖЗИІЇЙКЛМНОПРСТУФХЦЧШЩЬЮЯ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"кандидати"</u></string>
diff --git a/core/res/res/values-ur-rPK/strings.xml b/core/res/res/values-ur-rPK/strings.xml
index 565cdd9..5236b7f 100644
--- a/core/res/res/values-ur-rPK/strings.xml
+++ b/core/res/res/values-ur-rPK/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"مواد پالیسی کے تحت مخفی ہے"</string>
<string name="safeMode" msgid="2788228061547930246">"حفاظتی وضع"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android سسٹم"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"ذاتی"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"دفتر"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"ذاتی پر سوئچ کریں"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"کام پر سوئچ کریں"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"رابطے"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"اپنے رابطوں تک رسائی حاصل کریں"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"مقام"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"کوئی اجازتیں درکار نہیں ہیں"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"اس میں آپ کا پیسہ خرچ ہو سکتا ہے"</string>
<string name="dlg_ok" msgid="7376953167039865701">"ٹھیک ہے"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"چارجنگ کیلئے USB"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"USB اس آلے کو چارج کر رہی ہے"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"منسلکہ آلے کو USB پاور سپلائی کر رہی ہے"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"فائل کی منتقلی کیلئے USB"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"تصویر کی منتقلی کیلئے USB"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI کیلئے USB"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"اشتراک کریں"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"مسترد کریں"</string>
<string name="select_input_method" msgid="8547250819326693584">"کی بورڈ تبدیل کریں"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"دیگر کی بورڈز"</string>
<string name="show_ime" msgid="2506087537466597099">"جب فزیکل کی بورڈ فعال ہو تو IME کو اسکرین پر رکھیں"</string>
<string name="hardware" msgid="194658061510127999">"ورچوئل کی بورڈ دکھائیں"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"کی بورڈ کا خاکہ منتخب کریں"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"ایک کی بورڈ کا خاکہ منتخب کرنے کیلئے چھوئیں۔"</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"فزیکل کی بورڈ کنفیگر کریں"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"زبان اور لے آؤٹ منتخب کرنے کیلئے تھپتھپائیں"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"امیدواران"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"مزید اختیارات"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"اندرونی اشتراک کردہ اسٹوریج"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD کارڈ"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD کارڈ"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB ڈرائیو"</string>
diff --git a/core/res/res/values-uz-rUZ/strings.xml b/core/res/res/values-uz-rUZ/strings.xml
index 1a81245..6cb65a3 100644
--- a/core/res/res/values-uz-rUZ/strings.xml
+++ b/core/res/res/values-uz-rUZ/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Qoidaga muvofiq kontent yashirilgan"</string>
<string name="safeMode" msgid="2788228061547930246">"Xavfsiz usul"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android tizimi"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Shaxsiy"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Ish"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Shaxsiy profilga o‘tish"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Ishchi profilga o‘tish"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontaktlar"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"kontaktlarga kirish"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Joylashuv"</string>
@@ -900,7 +900,7 @@
<string name="whichSendApplication" msgid="6902512414057341668">"Ulashish…"</string>
<string name="whichSendApplicationNamed" msgid="2799370240005424391">"“%1$s” orqali ulashish"</string>
<string name="whichSendToApplication" msgid="8272422260066642057">"Ilovani tanlang"</string>
- <string name="whichSendToApplicationNamed" msgid="7768387871529295325">"%1$s yordamida yuborish"</string>
+ <string name="whichSendToApplicationNamed" msgid="7768387871529295325">"%1$s orqali yuborish"</string>
<string name="whichHomeApplication" msgid="4307587691506919691">"Bosh ilovani tanlash"</string>
<string name="whichHomeApplicationNamed" msgid="4493438593214760979">"%1$s: Bosh ilova sifatida foydalanish"</string>
<string name="alwaysUse" msgid="4583018368000610438">"Ushbu amaldan standart sifatida foydalanish"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Hech qanday ruxsat talab qilinmaydi"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"buning uchun sizdan haq olinishi mumkin"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB orqali quvvatlash"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"USB orqali quvvatlash"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"USB orqali ulangan qurilma quvvatlanmoqda"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB orqali fayl o‘tkazish"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB orqali rasm o‘tkazish"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB orqali MIDI"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"ULASHISH"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"RAD ETISH"</string>
<string name="select_input_method" msgid="8547250819326693584">"Klaviaturani o‘zgartirish"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Boshqa klaviaturalar"</string>
<string name="show_ime" msgid="2506087537466597099">"Tashqi klaviaturadan foydalanilayotganda buni ekranda saqlab turish"</string>
<string name="hardware" msgid="194658061510127999">"Virtual klaviatura ko‘rsatilsin"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Tugmalar tartibini tanlash"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Tugmalar tartibini tanlash uchun bosing."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Tashqi klaviaturani sozlash"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Til va sxemani belgilash uchun bosing"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"nomzodlar"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Ko‘proq sozlamalar"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Ichki umumiy xotira"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD karta"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD kartasi"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB xotira"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 7302f7a..29eef92 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Nội dung bị ẩn theo chính sách"</string>
<string name="safeMode" msgid="2788228061547930246">"Chế độ an toàn"</string>
<string name="android_system_label" msgid="6577375335728551336">"Hệ thống Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Cá nhân"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Cơ quan"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Chuyển sang Cá nhân"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Chuyển sang Công việc"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Danh bạ"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"truy cập vào danh bạ của bạn"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Vị trí"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Không yêu cầu quyền"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"bạn có thể mất tiền vì điều này"</string>
<string name="dlg_ok" msgid="7376953167039865701">"OK"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB để sạc"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"Sạc qua USB thiết bị này"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"Nguồn cấp điện qua USB cho thiết bị được kết nối"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB để truyền tệp"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB để truyền ảnh"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB cho MIDI"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"CHIA SẺ"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"TỪ CHỐI"</string>
<string name="select_input_method" msgid="8547250819326693584">"Thay đổi bàn phím"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Bàn phím khác"</string>
<string name="show_ime" msgid="2506087537466597099">"Tiếp tục sử dụng ứng dụng trên màn hình trong khi bàn phím thực đang hoạt động"</string>
<string name="hardware" msgid="194658061510127999">"Hiển thị bàn phím ảo"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Chọn bố cục bàn phím"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Chạm để chọn bố cục bàn phím."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Định cấu hình bàn phím thực"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Nhấn để chọn ngôn ngữ và bố cục"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"ứng viên"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Tùy chọn khác"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Bộ nhớ trong dùng chung"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Thẻ SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"Thẻ SD <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Ổ USB"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 4c90e1c..3b26e46 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"内容已隐藏(根据政策规定)"</string>
<string name="safeMode" msgid="2788228061547930246">"安全模式"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android 系统"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"个人"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"工作"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"切换到“个人”"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"切换到“工作”"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"通讯录"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"访问您的通讯录"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"位置信息"</string>
@@ -899,7 +899,7 @@
<string name="whichEditApplicationNamed" msgid="1775815530156447790">"使用%1$s编辑"</string>
<string name="whichSendApplication" msgid="6902512414057341668">"分享方式"</string>
<string name="whichSendApplicationNamed" msgid="2799370240005424391">"使用%1$s分享"</string>
- <string name="whichSendToApplication" msgid="8272422260066642057">"通过以下应用发送:"</string>
+ <string name="whichSendToApplication" msgid="8272422260066642057">"通过以下应用发送"</string>
<string name="whichSendToApplicationNamed" msgid="7768387871529295325">"通过1$s发送"</string>
<string name="whichHomeApplication" msgid="4307587691506919691">"选择主屏幕应用"</string>
<string name="whichHomeApplicationNamed" msgid="4493438593214760979">"将“%1$s”设为主屏幕应用"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"不需要任何权限"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"这可能会产生费用"</string>
<string name="dlg_ok" msgid="7376953167039865701">"确定"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"正在通过 USB 充电"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"正在通过 USB 为此设备充电"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"正在通过 USB 为连接的设备充电"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"正在通过 USB 传输文件"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"正在通过 USB 传输照片"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"正在通过 USB 连接到 MIDI 接口"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"分享"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"拒绝"</string>
<string name="select_input_method" msgid="8547250819326693584">"更改键盘"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"其他键盘"</string>
<string name="show_ime" msgid="2506087537466597099">"连接到实体键盘时使其在屏幕上保持显示状态"</string>
<string name="hardware" msgid="194658061510127999">"显示虚拟键盘"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"选择键盘布局"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"触摸可选择键盘布局。"</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"配置实体键盘"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"点按即可选择语言和布局"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"候选"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"更多选项"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s:%2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s - %2$s:%3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"内部共享存储空间"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD卡"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD 卡"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"U 盘"</string>
@@ -1474,7 +1473,7 @@
<string name="package_installed_device_owner" msgid="8420696545959087545">"已由管理员安装"</string>
<string name="package_updated_device_owner" msgid="8856631322440187071">"由您单位的管理员更新"</string>
<string name="package_deleted_device_owner" msgid="7650577387493101353">"已被管理员删除"</string>
- <string name="battery_saver_description" msgid="1960431123816253034">"为了延长电池的续航时间,节电助手会降低设备的性能,并限制振动、位置信息服务和大部分后台流量。对于电子邮件、聊天工具等依赖于同步功能的应用,可能要打开这类应用时才能收到新信息。\n\n节电助手会在设备充电时自动关闭。"</string>
+ <string name="battery_saver_description" msgid="1960431123816253034">"为了延长电池的续航时间,省电模式会降低设备的性能,并限制振动、位置信息服务和大部分后台流量。对于电子邮件、聊天工具等依赖于同步功能的应用,可能要打开这类应用时才能收到新信息。\n\n省电模式会在设备充电时自动关闭。"</string>
<plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
<item quantity="other">%1$d 分钟(到<xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
<item quantity="one">1 分钟(到<xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index e15aaf2..28f2bac 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"已根據政策隱藏內容"</string>
<string name="safeMode" msgid="2788228061547930246">"安全模式"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android 系統"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"個人"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"公司"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"切換至個人設定檔"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"切換至工作設定檔"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"通訊錄"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"存取您的通訊錄"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"位置"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"不需授權"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"這可能需要付費"</string>
<string name="dlg_ok" msgid="7376953167039865701">"確定"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"USB 充電"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"正在透過 USB 為此裝置充電"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"正在透過 USB 為已連接的裝置供電"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB 檔案傳輸"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB 相片傳輸"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB MIDI"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"分享"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"拒絕"</string>
<string name="select_input_method" msgid="8547250819326693584">"變更鍵盤"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"其他鍵盤"</string>
<string name="show_ime" msgid="2506087537466597099">"在實體鍵盤處於連接狀態時保持顯示"</string>
<string name="hardware" msgid="194658061510127999">"顯示虛擬鍵盤"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"選取鍵盤配置"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"輕觸即可選取鍵盤配置。"</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"設定實體鍵盤"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"輕按即可選取語言和鍵盤配置"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"待選項目"</u></string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index a782d40..dfc7890 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"內容已依據政策隱藏"</string>
<string name="safeMode" msgid="2788228061547930246">"安全模式"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android 系統"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"個人"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"公司"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"切換至個人設定檔"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"切換至公司設定檔"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"聯絡人"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"存取您的聯絡人"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"位置"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"無須許可"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"這可能需要付費"</string>
<string name="dlg_ok" msgid="7376953167039865701">"確定"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"正在透過 USB 充電"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"正在透過 USB 為這個裝置充電"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"正在透過 USB 為連接的裝置供電"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"USB 檔案傳輸"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"USB 相片傳輸"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"USB MIDI"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"分享"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"拒絕"</string>
<string name="select_input_method" msgid="8547250819326693584">"變更鍵盤"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"其他鍵盤"</string>
<string name="show_ime" msgid="2506087537466597099">"有連接的實體鍵盤時保持顯示"</string>
<string name="hardware" msgid="194658061510127999">"顯示虛擬鍵盤"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"選取鍵盤配置"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"輕觸即可選取鍵盤配置。"</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"設定實體鍵盤"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"輕觸即可選取語言和版面配置"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"待選項目"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"更多選項"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s:%2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s - %2$s:%3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"內部共用儲存空間"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"SD 卡"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD 卡"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"USB 隨身碟"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 23d72dc..e32f26b 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -233,8 +233,8 @@
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Okuqukethwe kufihlwe inqubomgomo"</string>
<string name="safeMode" msgid="2788228061547930246">"Imodi ephephile"</string>
<string name="android_system_label" msgid="6577375335728551336">"Uhlelo lwe-Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Okomuntu siqu"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Umsebenzi"</string>
+ <string name="user_owner_label" msgid="1119010402169916617">"Shintshela komuntu siqu"</string>
+ <string name="managed_profile_label" msgid="5289992269827577857">"Shintshela kumsebenzi"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Oxhumana nabo"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"finyelela koxhumana nabo"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Indawo"</string>
@@ -1045,7 +1045,8 @@
<string name="no_permissions" msgid="7283357728219338112">"Ayikho imvume edingekayo"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"lokhu kungakudlela imali"</string>
<string name="dlg_ok" msgid="7376953167039865701">"KULUNGILE"</string>
- <string name="usb_charging_notification_title" msgid="4004114449249406402">"I-USB yokushaja"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"I-USB ishaja le divayisi"</string>
+ <string name="usb_supplying_notification_title" msgid="5310642257296510271">"I-USB inikeza amandla kudivayisi enamathiselwe"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"I-USB yokudluliswa kwefayela"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"I-USB yokudluliswa kwesithombe"</string>
<string name="usb_midi_notification_title" msgid="4850904915889144654">"I-USB ye-MIDI"</string>
@@ -1060,11 +1061,10 @@
<string name="share_remote_bugreport_action" msgid="6249476773913384948">"YABELANA"</string>
<string name="decline_remote_bugreport_action" msgid="6230987241608770062">"YENQABA"</string>
<string name="select_input_method" msgid="8547250819326693584">"Shintsha ikhibhodi"</string>
- <string name="configure_input_methods" msgid="5673193194563164021">"Amanye amakhibhodi"</string>
<string name="show_ime" msgid="2506087537466597099">"Yigcine kusikrini ngenkathi kusebenza ikhibhodi ephathekayo"</string>
<string name="hardware" msgid="194658061510127999">"Bonisa ikhibhodi ebonakalayo"</string>
- <string name="select_keyboard_layout_notification_title" msgid="1407367017263030773">"Khetha isendlalelo sekhibhodi"</string>
- <string name="select_keyboard_layout_notification_message" msgid="4465907700449257063">"Thinta ukuze ukhethe isendlalelo sekhibhodi."</string>
+ <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Lungisa ikhibhodi yoqobo"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Thepha ukuze ukhethe ulimi nesakhiwo"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"abahlanganyeli"</u></string>
@@ -1224,8 +1224,7 @@
<string name="action_menu_overflow_description" msgid="2295659037509008453">"Izinketho ezingaphezulu"</string>
<string name="action_bar_home_description_format" msgid="7965984360903693903">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="6985546530471780727">"%1$s, %2$s, %3$s"</string>
- <!-- no translation found for storage_internal (3570990907910199483) -->
- <skip />
+ <string name="storage_internal" msgid="3570990907910199483">"Isitoreji esabiwe sangaphakathi"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"Ikhadi le-SD"</string>
<string name="storage_sd_card_label" msgid="6347111320774379257">"<xliff:g id="MANUFACTURER">%s</xliff:g> ikhadi le-SD"</string>
<string name="storage_usb_drive" msgid="6261899683292244209">"Idrayivu ye-USB"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index a52c4e5..0ed1f13 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2846,6 +2846,11 @@
<!-- Pointer icon of a hand sign while grabbing something. -->
<enum name="grabbing" value="1021" />
</attr>
+
+ <!-- Whether this view has elements that may overlap when drawn. See
+ {@link android.view.View#forceHasOverlappingRendering(boolean)}. -->
+ <attr name="forceHasOverlappingRendering" format="boolean" />
+
</declare-styleable>
<!-- Attributes that can be assigned to a tag for a particular View. -->
@@ -3523,6 +3528,13 @@
This setting implies fastScrollEnabled. -->
<attr name="fastScrollAlwaysVisible" format="boolean" />
</declare-styleable>
+ <!-- @hide -->
+ <declare-styleable name="RecycleListView">
+ <!-- Bottom padding to use when no buttons are present. -->
+ <attr name="paddingBottomNoButtons" format="dimension" />
+ <!-- Top padding to use when no title is present. -->
+ <attr name="paddingTopNoTitle" format="dimension" />
+ </declare-styleable>
<declare-styleable name="AbsSpinner">
<!-- Reference to an array resource that will populate the Spinner. For static content,
this is simpler than populating the Spinner programmatically. -->
@@ -4379,8 +4391,7 @@
<attr name="autoLink" />
<!-- If set to false, keeps the movement method from being set
to the link movement method even if autoLink causes links
- to be found or the input text contains a
- {@link android.text.style.ClickableSpan ClickableSpan}. -->
+ to be found. -->
<attr name="linksClickable" format="boolean" />
<!-- If set, specifies that this TextView has a numeric input method.
The default is false.
@@ -7639,6 +7650,12 @@
<!-- Minimum inset for content views within a bar. Navigation buttons and
menu views are excepted. Only valid for some themes and configurations. -->
<attr name="contentInsetRight" format="dimension" />
+ <!-- Minimum inset for content views within a bar when a navigation button
+ is present, such as the Up button. Only valid for some themes and configurations. -->
+ <attr name="contentInsetStartWithNavigation" format="dimension" />
+ <!-- Minimum inset for content views within a bar when actions from a menu
+ are present. Only valid for some themes and configurations. -->
+ <attr name="contentInsetEndWithActions" format="dimension" />
<!-- Elevation for the action bar itself -->
<attr name="elevation" />
<!-- Reference to a theme that should be used to inflate popups
@@ -8006,6 +8023,8 @@
<attr name="contentInsetEnd" />
<attr name="contentInsetLeft" />
<attr name="contentInsetRight" />
+ <attr name="contentInsetStartWithNavigation" />
+ <attr name="contentInsetEndWithActions" />
<attr name="maxButtonHeight" format="dimension" />
<attr name="navigationButtonStyle" format="reference" />
<attr name="buttonGravity">
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 5716823..5b4364d 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -225,6 +225,9 @@
granted any application pre-installed on the system image (not just privileged
apps). -->
<flag name="preinstalled" value="0x400" />
+ <!-- Additional flag from base permission type: this permission can be automatically
+ granted to the setup wizard app -->
+ <flag name="setup" value="0x800" />
</attr>
<!-- Flags indicating more context for a permission group. -->
@@ -2262,13 +2265,20 @@
<!-- Where to initially position the activity inside the available space. Uses constants
defined in {@link android.view.Gravity}. -->
<attr name="gravity" />
+ <!-- Minimal width of the activity.
+
+ <p><strong>NOTE:</strong> A task's root activity value is applied to all additional
+ activities launched in the task. That is if the root activity of a task set minimal width,
+ then the system will set the same minimal width on all other activities in the task. It
+ will also ignore any other minimal width attributes of non-root activities. -->
+ <attr name="minimalWidth" format="dimension" />
<!-- Minimal height of the activity.
<p><strong>NOTE:</strong> A task's root activity value is applied to all additional
- activities launched in the task. That is if the root activity of a task set minimal size,
- then the system will set the same minimal size on all other activities in the task. It will
- also ignore any other minimal size attributes of non-root activities. -->
- <attr name="minimalSize" format="dimension" />
+ activities launched in the task. That is if the root activity of a task set minimal height,
+ then the system will set the same minimal height on all other activities in the task. It
+ will also ignore any other minimal height attributes of non-root activities. -->
+ <attr name="minimalHeight" format="dimension" />
</declare-styleable>
</resources>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 48aa440..bddd225 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -180,4 +180,7 @@
<!-- Status bar color for semi transparent mode. -->
<color name="system_bar_background_semi_transparent">#66000000</color> <!-- 40% black -->
+
+ <color name="resize_shadow_start_color">#2a000000</color>
+ <color name="resize_shadow_end_color">#00000000</color>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 01b2c47..b4371c1 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -578,6 +578,9 @@
Software implementation will be used if config_hardware_auto_brightness_available is not set -->
<bool name="config_automatic_brightness_available">false</bool>
+ <!-- Fast brightness animation ramp rate -->
+ <integer translatable="false" name="config_brightness_ramp_rate_fast">200</integer>
+
<!-- Don't name config resources like this. It should look like config_annoyDianne -->
<bool name="config_annoy_dianne">true</bool>
@@ -2462,14 +2465,6 @@
<!-- Default bounds [left top right bottom] on screen for picture-in-picture windows. -->
<string translatable="false" name="config_defaultPictureInPictureBounds">"0 0 100 100"</string>
- <!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows, when the PIP
- is located in center. -->
- <string translatable="false" name="config_centeredPictureInPictureBounds">"0 0 300 300"</string>
-
- <!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows,
- when the PIP is shown with Recents. -->
- <string translatable="false" name="config_pictureInPictureBoundsInRecents">"0 0 100 100"</string>
-
<!-- Controls the snap mode for the docked stack divider
0 - 3 snap targets: left/top has 16:9 ratio, 1:1, and right/bottom has 16:9 ratio
1 - 3 snap targets: fixed ratio, 1:1, (1 - fixed ratio)
@@ -2505,4 +2500,24 @@
<!-- True if the device supports at least one form of multi-window.
E.g. freeform, split-screen, picture-in-picture. -->
<bool name="config_supportsMultiWindow">true</bool>
+
+ <!-- True if the device requires AppWidgetService even if it does not have
+ the PackageManager.FEATURE_APP_WIDGETS feature -->
+ <bool name="config_enableAppWidgetService">false</bool>
+
+ <!-- True if the device supports Sustained Performance Mode-->
+ <bool name="config_sustainedPerformanceModeSupported">false</bool>
+
+ <!-- Controls how we deal with externally connected physical keyboards.
+ 0 - When using this device, it is not clear for users to recognize when the physical
+ keyboard is (should be) connected and when it is (should be) disconnected. Most of
+ phones and tablets with Bluetooth keyboard would fall into this category because the
+ connected Bluetooth keyboard may or may not be nearby the host device.
+ 1 - When using this device, it is clear for users to recognize when the physical
+ keyboard is (should be) connected and when it is (should be) disconnected.
+ Devices with wired USB keyboard is one clear example. Some 2-in-1 convertible
+ tablets with dedicated keyboards may have the same affordance to wired USB keyboard.
+ -->
+ <integer name="config_externalHardKeyboardBehavior">0</integer>
+
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 081d613..a21f276 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -22,6 +22,8 @@
<dimen name="thumbnail_width">192dp</dimen>
<!-- The height that is used when creating thumbnails of applications. -->
<dimen name="thumbnail_height">192dp</dimen>
+ <!-- The amount to scale a fullscreen screenshot thumbnail. -->
+ <item name="thumbnail_fullscreen_scale" type="fraction">60%</item>
<!-- The standard size (both width and height) of an application icon that
will be displayed in the app launcher and elsewhere. -->
<dimen name="app_icon_size">48dip</dimen>
@@ -448,6 +450,9 @@
<item type="dimen" format="integer" name="time_picker_column_end_material">1</item>
<item type="dimen" name="aerr_padding_list_top">15dp</item>
+ <item type="dimen" name="aerr_padding_list_bottom">8dp</item>
<item type="fraction" name="docked_stack_divider_fixed_ratio">34.15%</item>
+
+ <dimen name="resize_shadow_size">5dp</dimen>
</resources>
diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml
index 2fe4f66..00e48a0 100644
--- a/core/res/res/values/dimens_material.xml
+++ b/core/res/res/values/dimens_material.xml
@@ -41,6 +41,8 @@
<dimen name="action_bar_default_padding_end_material">0dp</dimen>
<!-- Default content inset of an action bar. -->
<dimen name="action_bar_content_inset_material">16dp</dimen>
+ <!-- Default content inset of an action bar when a navigation button is present. -->
+ <dimen name="action_bar_content_inset_with_nav">72dp</dimen>
<!-- Vertical padding around action bar icons. -->
<dimen name="action_bar_icon_vertical_padding_material">16dp</dimen>
<!-- Top margin for action bar subtitles -->
@@ -115,13 +117,13 @@
<dimen name="dialog_padding_material">24dp</dimen>
<dimen name="dialog_padding_top_material">18dp</dimen>
+ <dimen name="dialog_title_divider_material">8dp</dimen>
+ <dimen name="dialog_list_padding_top_no_title">8dp</dimen>
+ <dimen name="dialog_list_padding_bottom_no_buttons">8dp</dimen>
<!-- Dialog padding minus control padding, used to fix alignment. -->
<dimen name="select_dialog_padding_start_material">20dp</dimen>
- <!-- Padding above and below selection dialog lists. -->
- <dimen name="dialog_list_padding_vertical_material">8dp</dimen>
-
<dimen name="seekbar_track_background_height_material">2dp</dimen>
<dimen name="seekbar_track_progress_height_material">2dp</dimen>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 7b85c7e..6ff7139 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2664,7 +2664,7 @@
<public type="attr" name="subMenuArrow" />
<public type="attr" name="defaultWidth" />
<public type="attr" name="defaultHeight" />
- <public type="attr" name="minimalSize" />
+ <public type="attr" name="minimalWidth" />
<public type="attr" name="resizeableActivity" />
<public type="attr" name="supportsPictureInPicture" />
<public type="attr" name="titleMargin" />
@@ -2709,9 +2709,15 @@
<public type="attr" name="fillType" />
<public type="attr" name="popupEnterTransition" />
<public type="attr" name="popupExitTransition" />
+ <public type="attr" name="minimalHeight" />
+ <public type="attr" name="forceHasOverlappingRendering" />
+ <public type="attr" name="contentInsetStartWithNavigation" />
+ <public type="attr" name="contentInsetEndWithActions" />
<public type="style" name="Theme.Material.Light.DialogWhenLarge.DarkActionBar" />
<public type="style" name="Widget.Material.SeekBar.Discrete" />
+ <public type="style" name="Widget.Material.CompoundButton.Switch" />
+ <public type="style" name="Widget.Material.Light.CompoundButton.Switch" />
<public type="id" name="accessibilityActionSetProgress" />
<public type="id" name="icon_frame" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 74ce753..b4f95dc 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -563,11 +563,11 @@
<!-- Label for the Android system components when they are shown to the user. -->
<string name="android_system_label">Android System</string>
- <!-- Label for the user owner in the intent forwarding app. [CHAR LIMIT=15] -->
- <string name="user_owner_label">Personal</string>
+ <!-- Label for the user owner in the intent forwarding app. -->
+ <string name="user_owner_label">Switch to Personal</string>
- <!-- Label for a corporate profile in the intent forwarding app. [CHAR LIMIT=15] -->
- <string name="managed_profile_label">Work</string>
+ <!-- Label for a corporate profile in the intent forwarding app. -->
+ <string name="managed_profile_label">Switch to Work</string>
<!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permgrouplab_contacts">Contacts</string>
@@ -2904,7 +2904,9 @@
<string name="dlg_ok">OK</string>
<!-- USB_PREFERENCES: Notification for when the user connected to the charger only. This is the title -->
- <string name="usb_charging_notification_title">USB for charging</string>
+ <string name="usb_charging_notification_title">USB charging this device</string>
+ <!-- USB_PREFERENCES: Notification for when the user connects the phone to supply power to attached device. This is the title -->
+ <string name="usb_supplying_notification_title">USB supplying power to attached device</string>
<!-- USB_PREFERENCES: Notification for when the user connects the phone to a computer via USB in MTP mode. This is the title -->
<string name="usb_mtp_notification_title">USB for file transfer</string>
<!-- USB_PREFERENCES: Notification for when the user connects the phone to a computer via USB in PTP mode. This is the title -->
@@ -2943,18 +2945,17 @@
<!-- Title of the pop-up dialog in which the user switches keyboard, also known as input method. -->
<string name="select_input_method">Change keyboard</string>
- <!-- Title of a button to open the settings to enable or disable other soft keyboards (also known as input methods) [CHAR LIMIT=30] -->
- <string name="configure_input_methods">Other keyboards</string>
<!-- Summary text of a toggle switch to enable/disable use of the IME while a physical
keyboard is connected -->
<string name="show_ime">Keep it on screen while physical keyboard is active</string>
<!-- Title of the physical keyboard category in the input method selector [CHAR LIMIT=30] -->
<string name="hardware">Show virtual keyboard</string>
- <!-- Title of the notification to prompt the user to select a keyboard layout. -->
- <string name="select_keyboard_layout_notification_title">Select keyboard layout</string>
- <!-- Message of the notification to prompt the user to select a keyboard layout. -->
- <string name="select_keyboard_layout_notification_message">Touch to select a keyboard layout.</string>
+ <!-- Title of the notification to prompt the user to configure physical keyboard settings. -->
+ <string name="select_keyboard_layout_notification_title">Configure physical keyboard</string>
+ <!-- Message of the notification to prompt the user to configure physical keyboard settings
+ where the user can associate language with physical keyboard layout. -->
+ <string name="select_keyboard_layout_notification_message">Tap to select language and layout</string>
<string name="fast_scroll_alphabet">\u0020ABCDEFGHIJKLMNOPQRSTUVWXYZ</string>
<string name="fast_scroll_numeric_alphabet">\u00200123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ</string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 86b9f1d..790dcfa 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1234,6 +1234,7 @@
<item name="collapseIcon">?attr/homeAsUpIndicator</item>
<item name="collapseContentDescription">@string/toolbar_collapse_description</item>
<item name="contentInsetStart">16dp</item>
+ <item name="contentInsetStartWithNavigation">@dimen/action_bar_content_inset_with_nav</item>
<item name="touchscreenBlocksFocus">true</item>
</style>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index e636bc0..2420c1a 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -311,7 +311,7 @@
<style name="TextAppearance.Material.Widget.PopupMenu.Header">
<item name="fontFamily">@string/font_family_title_material</item>
<item name="textSize">@dimen/text_size_menu_header_material</item>
- <item name="textColor">?attr/textColorSecondary</item>
+ <item name="textColor">?attr/colorAccent</item>
</style>
<style name="TextAppearance.Material.Widget.DropDownHint" parent="TextAppearance.Material.Menu" />
@@ -944,6 +944,7 @@
<item name="homeLayout">@layout/action_bar_home_material</item>
<item name="gravity">center_vertical</item>
<item name="contentInsetStart">@dimen/action_bar_content_inset_material</item>
+ <item name="contentInsetStartWithNavigation">@dimen/action_bar_content_inset_with_nav</item>
<item name="contentInsetEnd">@dimen/action_bar_content_inset_material</item>
<item name="elevation">@dimen/action_bar_elevation_material</item>
<item name="popupTheme">?attr/actionBarPopupTheme</item>
diff --git a/core/res/res/values/styles_micro.xml b/core/res/res/values/styles_micro.xml
index 7dde5f8..341a0a4c 100644
--- a/core/res/res/values/styles_micro.xml
+++ b/core/res/res/values/styles_micro.xml
@@ -17,27 +17,27 @@
<style name="Animation.Micro"/>
<style name="Animation.Micro.Activity" parent="Animation.Material.Activity">
- <item name="activityOpenEnterAnimation">@anim/slide_in_micro</item>
- <item name="activityOpenRemoteViewsEnterAnimation">@anim/slide_in_micro</item>
- <item name="activityOpenExitAnimation">@null</item>
+ <item name="activityOpenEnterAnimation">@anim/slide_in_enter_micro</item>
+ <item name="activityOpenRemoteViewsEnterAnimation">@anim/slide_in_enter_micro</item>
+ <item name="activityOpenExitAnimation">@anim/slide_in_exit_micro</item>
<item name="activityCloseEnterAnimation">@null</item>
<item name="activityCloseExitAnimation">@anim/slide_out_micro</item>
- <item name="taskOpenEnterAnimation">@anim/slide_in_micro</item>
- <item name="taskOpenExitAnimation">@null</item>
+ <item name="taskOpenEnterAnimation">@anim/slide_in_enter_micro</item>
+ <item name="taskOpenExitAnimation">@anim/slide_in_exit_micro</item>
<item name="taskCloseEnterAnimation">@null</item>
<item name="taskCloseExitAnimation">@anim/slide_out_micro</item>
- <item name="taskToFrontEnterAnimation">@anim/slide_in_micro</item>
- <item name="taskToFrontExitAnimation">@null</item>
+ <item name="taskToFrontEnterAnimation">@anim/slide_in_enter_micro</item>
+ <item name="taskToFrontExitAnimation">@anim/slide_in_exit_micro</item>
<item name="taskToBackEnterAnimation">@null</item>
<item name="taskToBackExitAnimation">@anim/slide_out_micro</item>
<item name="wallpaperOpenEnterAnimation">@null</item>
<item name="wallpaperOpenExitAnimation">@anim/slide_out_micro</item>
- <item name="wallpaperCloseEnterAnimation">@anim/slide_in_micro</item>
- <item name="wallpaperCloseExitAnimation">@null</item>
+ <item name="wallpaperCloseEnterAnimation">@anim/slide_in_enter_micro</item>
+ <item name="wallpaperCloseExitAnimation">@anim/slide_in_exit_micro</item>
<item name="wallpaperIntraOpenEnterAnimation">@null</item>
<item name="wallpaperIntraOpenExitAnimation">@anim/slide_out_micro</item>
- <item name="wallpaperIntraCloseEnterAnimation">@anim/slide_in_micro</item>
- <item name="wallpaperIntraCloseExitAnimation">@null</item>
+ <item name="wallpaperIntraCloseEnterAnimation">@anim/slide_in_enter_micro</item>
+ <item name="wallpaperIntraCloseExitAnimation">@anim/slide_in_exit_micro</item>
</style>
<style name="AlertDialog.Micro" parent="AlertDialog.Material.Light">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index ce7d80b..2215bd4 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -307,9 +307,8 @@
<java-symbol type="bool" name="config_supportsMultiWindow" />
<java-symbol type="bool" name="config_guestUserEphemeral" />
<java-symbol type="bool" name="config_localDisplaysMirrorContent" />
+ <java-symbol type="bool" name="config_enableAppWidgetService" />
<java-symbol type="string" name="config_defaultPictureInPictureBounds" />
- <java-symbol type="string" name="config_centeredPictureInPictureBounds" />
- <java-symbol type="string" name="config_pictureInPictureBoundsInRecents" />
<java-symbol type="integer" name="config_wifi_framework_5GHz_preference_boost_threshold" />
<java-symbol type="integer" name="config_wifi_framework_5GHz_preference_boost_factor" />
<java-symbol type="integer" name="config_wifi_framework_5GHz_preference_penalty_threshold" />
@@ -1513,6 +1512,10 @@
<java-symbol type="dimen" name="docked_stack_minimize_thickness" />
<java-symbol type="integer" name="config_dockedStackDividerSnapMode" />
<java-symbol type="fraction" name="docked_stack_divider_fixed_ratio" />
+ <java-symbol type="fraction" name="thumbnail_fullscreen_scale" />
+ <java-symbol type="dimen" name="resize_shadow_size" />
+ <java-symbol type="color" name="resize_shadow_start_color" />
+ <java-symbol type="color" name="resize_shadow_end_color" />
<java-symbol type="dimen" name="navigation_bar_height" />
<java-symbol type="dimen" name="navigation_bar_height_landscape" />
<java-symbol type="dimen" name="navigation_bar_width" />
@@ -1741,6 +1744,7 @@
<java-symbol type="integer" name="config_defaultNotificationLedOff" />
<java-symbol type="integer" name="config_defaultNotificationLedOn" />
<java-symbol type="integer" name="config_deskDockKeepsScreenOn" />
+ <java-symbol type="integer" name="config_externalHardKeyboardBehavior" />
<java-symbol type="integer" name="config_lightSensorWarmupTime" />
<java-symbol type="integer" name="config_lowBatteryCloseWarningBump" />
<java-symbol type="integer" name="config_lowBatteryWarningLevel" />
@@ -1763,6 +1767,7 @@
<java-symbol type="integer" name="config_shutdownBatteryTemperature" />
<java-symbol type="integer" name="config_undockedHdmiRotation" />
<java-symbol type="integer" name="config_virtualKeyQuietTimeMillis" />
+ <java-symbol type="integer" name="config_brightness_ramp_rate_fast" />
<java-symbol type="layout" name="am_compat_mode_dialog" />
<java-symbol type="layout" name="launch_warning" />
<java-symbol type="layout" name="safe_mode" />
@@ -1807,7 +1812,6 @@
<java-symbol type="string" name="config_wimaxServiceClassname" />
<java-symbol type="string" name="config_wimaxServiceJarLocation" />
<java-symbol type="string" name="config_wimaxStateTrackerClassname" />
- <java-symbol type="string" name="configure_input_methods" />
<java-symbol type="string" name="data_usage_3g_limit_snoozed_title" />
<java-symbol type="string" name="data_usage_3g_limit_title" />
<java-symbol type="string" name="data_usage_4g_limit_snoozed_title" />
@@ -1861,6 +1865,7 @@
<java-symbol type="string" name="usb_notification_message" />
<java-symbol type="string" name="usb_ptp_notification_title" />
<java-symbol type="string" name="usb_midi_notification_title" />
+ <java-symbol type="string" name="usb_supplying_notification_title" />
<java-symbol type="string" name="vpn_text" />
<java-symbol type="string" name="vpn_text_long" />
<java-symbol type="string" name="vpn_title" />
@@ -2415,13 +2420,11 @@
<java-symbol type="string" name="notification_hidden_text" />
<java-symbol type="string" name="notification_hidden_by_policy_text" />
<java-symbol type="id" name="app_name_text" />
- <java-symbol type="id" name="header_sub_text" />
+ <java-symbol type="id" name="header_text" />
<java-symbol type="id" name="expand_button" />
<java-symbol type="id" name="notification_header" />
- <java-symbol type="id" name="header_content_info" />
<java-symbol type="id" name="time_divider" />
- <java-symbol type="id" name="sub_text_divider" />
- <java-symbol type="id" name="content_info_divider" />
+ <java-symbol type="id" name="header_text_divider" />
<java-symbol type="id" name="text_line_1" />
<java-symbol type="drawable" name="ic_expand_notification" />
<java-symbol type="drawable" name="ic_collapse_notification" />
@@ -2448,24 +2451,6 @@
<java-symbol type="id" name="aerr_close" />
<java-symbol type="id" name="aerr_mute" />
- <!-- Framework-private Material.DayNight styles. -->
- <java-symbol type="style" name="Theme.Material.DayNight" />
- <java-symbol type="style" name="Theme.Material.DayNight.DarkActionBar" />
- <java-symbol type="style" name="Theme.Material.DayNight.Dialog" />
- <java-symbol type="style" name="Theme.Material.DayNight.Dialog.Alert" />
- <java-symbol type="style" name="Theme.Material.DayNight.Dialog.MinWidth" />
- <java-symbol type="style" name="Theme.Material.DayNight.Dialog.NoActionBar" />
- <java-symbol type="style" name="Theme.Material.DayNight.Dialog.NoActionBar.MinWidth" />
- <java-symbol type="style" name="Theme.Material.DayNight.Dialog.Presentation" />
- <java-symbol type="style" name="Theme.Material.DayNight.DialogWhenLarge" />
- <java-symbol type="style" name="Theme.Material.DayNight.DialogWhenLarge.NoActionBar" />
- <java-symbol type="style" name="Theme.Material.DayNight.NoActionBar" />
- <java-symbol type="style" name="Theme.Material.DayNight.NoActionBar.Fullscreen" />
- <java-symbol type="style" name="Theme.Material.DayNight.NoActionBar.Overscan" />
- <java-symbol type="style" name="Theme.Material.DayNight.NoActionBar.TranslucentDecor" />
- <java-symbol type="style" name="Theme.Material.DayNight.Panel" />
- <java-symbol type="style" name="Theme.Material.DayNight.DialogWhenLarge.DarkActionBar" />
-
<java-symbol type="string" name="status_bar_rotate" />
<java-symbol type="string" name="status_bar_headset" />
<java-symbol type="string" name="status_bar_data_saver" />
@@ -2553,4 +2538,9 @@
<!-- WallpaperManager config -->
<java-symbol type="string" name="config_wallpaperCropperPackage" />
+
+ <java-symbol type="id" name="textSpacerNoTitle" />
+ <java-symbol type="id" name="titleDividerNoCustom" />
+
+ <java-symbol type="bool" name="config_sustainedPerformanceModeSupported" />
</resources>
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index a361eda..6611eb1 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -1294,7 +1294,7 @@
</style>
<!-- Default theme for Settings and activities launched from Settings. -->
- <style name="Theme.Material.Settings" parent="Theme.Material.DayNight.DarkActionBar">
+ <style name="Theme.Material.Settings" parent="Theme.Material.Light.DarkActionBar">
<item name="colorPrimary">@color/material_blue_grey_900</item>
<item name="colorPrimaryDark">@color/material_blue_grey_950</item>
@@ -1304,7 +1304,7 @@
</style>
<!-- Default theme for Settings and activities launched from Settings. -->
- <style name="Theme.Material.Settings.NoActionBar" parent="Theme.Material.DayNight.NoActionBar">
+ <style name="Theme.Material.Settings.NoActionBar" parent="Theme.Material.Light.NoActionBar">
<item name="colorPrimary">@color/material_blue_grey_900</item>
<item name="colorPrimaryDark">@color/material_blue_grey_950</item>
@@ -1313,41 +1313,41 @@
<item name="panelMenuListTheme">@style/Theme.Material.Settings.CompactMenu</item>
</style>
- <style name="Theme.Material.Settings.BaseDialog" parent="Theme.Material.DayNight.BaseDialog">
+ <style name="Theme.Material.Settings.BaseDialog" parent="Theme.Material.Light.BaseDialog">
<item name="colorPrimary">@color/material_blue_grey_900</item>
<item name="colorPrimaryDark">@color/material_blue_grey_950</item>
</style>
<style name="Theme.Material.Settings.Dialog" parent="Theme.Material.Settings.BaseDialog" />
- <style name="Theme.Material.Settings.Dialog.BaseAlert" parent="Theme.Material.DayNight.Dialog.BaseAlert">
+ <style name="Theme.Material.Settings.Dialog.BaseAlert" parent="Theme.Material.Light.Dialog.BaseAlert">
<item name="colorPrimary">@color/material_blue_grey_900</item>
<item name="colorPrimaryDark">@color/material_blue_grey_950</item>
</style>
<style name="Theme.Material.Settings.Dialog.Alert" parent="Theme.Material.Settings.Dialog.BaseAlert" />
- <style name="Theme.Material.Settings.DialogWhenLarge" parent="Theme.Material.DayNight.DialogWhenLarge.DarkActionBar">
+ <style name="Theme.Material.Settings.DialogWhenLarge" parent="Theme.Material.Light.DialogWhenLarge.DarkActionBar">
<item name="colorPrimary">@color/material_blue_grey_900</item>
<item name="colorPrimaryDark">@color/material_blue_grey_950</item>
</style>
- <style name="Theme.Material.Settings.DialogWhenLarge.NoActionBar" parent="Theme.Material.DayNight.DialogWhenLarge.NoActionBar">
+ <style name="Theme.Material.Settings.DialogWhenLarge.NoActionBar" parent="Theme.Material.Light.DialogWhenLarge.NoActionBar">
<item name="colorPrimary">@color/material_blue_grey_900</item>
<item name="colorPrimaryDark">@color/material_blue_grey_950</item>
</style>
- <style name="Theme.Material.Settings.Dialog.Presentation" parent="Theme.Material.DayNight.Dialog.Presentation">
+ <style name="Theme.Material.Settings.Dialog.Presentation" parent="Theme.Material.Light.Dialog.Presentation">
<item name="colorPrimary">@color/material_blue_grey_900</item>
<item name="colorPrimaryDark">@color/material_blue_grey_950</item>
</style>
- <style name="Theme.Material.Settings.SearchBar" parent="Theme.Material.DayNight.SearchBar">
+ <style name="Theme.Material.Settings.SearchBar" parent="Theme.Material.Light.SearchBar">
<item name="colorPrimary">@color/material_blue_grey_900</item>
<item name="colorPrimaryDark">@color/material_blue_grey_950</item>
</style>
- <style name="Theme.Material.Settings.CompactMenu" parent="Theme.Material.DayNight.CompactMenu">
+ <style name="Theme.Material.Settings.CompactMenu" parent="Theme.Material.Light.CompactMenu">
<item name="colorPrimary">@color/material_blue_grey_900</item>
<item name="colorPrimaryDark">@color/material_blue_grey_950</item>
</style>
diff --git a/core/res/res/values/themes_material_daynight.xml b/core/res/res/values/themes_material_daynight.xml
deleted file mode 100644
index 4ecca6b..0000000
--- a/core/res/res/values/themes_material_daynight.xml
+++ /dev/null
@@ -1,117 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!--
-===============================================================
- PLEASE READ
-===============================================================
-
-The Material themes must not be modified in order to pass CTS.
-Many related themes and styles depend on other values defined in this file.
-If you would like to provide custom themes and styles for your device,
-please see themes_device_defaults.xml.
-
-===============================================================
- PLEASE READ
-===============================================================
- -->
-<resources>
-
- <!-- Material theme (day/night vesion) for activities. -->
- <style name="Theme.Material.DayNight" parent="Theme.Material.Light" />
-
- <!-- Variant of Material.DayNight that has a solid (opaque) action bar
- with an inverse color profile. The dark action bar sharply stands out against
- the light content (when applicable). -->
- <style name="Theme.Material.DayNight.DarkActionBar" parent="Theme.Material.Light.DarkActionBar" />
-
- <!-- Variant of Material.DayNight with no action bar. -->
- <style name="Theme.Material.DayNight.NoActionBar" parent="Theme.Material.Light.NoActionBar" />
-
- <!-- Variant of Material.DayNight that has no title bar and fills
- the entire screen. This theme
- sets {@link android.R.attr#windowFullscreen} to true. -->
- <style name="Theme.Material.DayNight.NoActionBar.Fullscreen" parent="Theme.Material.Light.NoActionBar.Fullscreen" />
-
- <!-- Variant of Material.DayNight that has no title bar and fills
- the entire screen and extends into the display overscan region. This theme
- sets {@link android.R.attr#windowFullscreen} and {@link android.R.attr#windowOverscan}
- to true. -->
- <style name="Theme.Material.DayNight.NoActionBar.Overscan" parent="Theme.Material.Light.NoActionBar.Overscan" />
-
- <!-- Variant of Material.DayNight that has no title bar and translucent
- system decor. This theme sets {@link android.R.attr#windowTranslucentStatus} and
- {@link android.R.attr#windowTranslucentNavigation} to true. -->
- <style name="Theme.Material.DayNight.NoActionBar.TranslucentDecor" parent="Theme.Material.Light.NoActionBar.TranslucentDecor" />
-
- <!-- Default Material.DayNight theme for panel windows. This removes all extraneous
- window decorations, so you basically have an empty rectangle in which
- to place your content. It makes the window floating, with a transparent
- background, and turns off dimming behind the window. -->
- <style name="Theme.Material.DayNight.Panel" parent="Theme.Material.Light.Panel" />
-
- <!-- Material theme (day/night vesion) for dialog windows and activities,
- which is used by the {@link android.app.Dialog} class. This changes
- the window to be floating (not fill the entire screen), and puts a
- frame around its contents. You can set this theme on an activity if
- you would like to make an activity that looks like a Dialog. -->
- <style name="Theme.Material.DayNight.Dialog" parent="Theme.Material.DayNight.BaseDialog" />
- <style name="Theme.Material.DayNight.BaseDialog" parent="Theme.Material.Light.BaseDialog" />
-
- <!-- Variant of Theme.Material.DayNight.Dialog that has a nice minimum width for
- a regular dialog. -->
- <style name="Theme.Material.DayNight.Dialog.MinWidth" parent="Theme.Material.Light.Dialog.MinWidth" />
-
- <!-- Variant of Theme.Material.DayNight.Dialog that does not include a title bar. -->
- <style name="Theme.Material.DayNight.Dialog.NoActionBar" parent="Theme.Material.Light.Dialog.NoActionBar" />
-
- <!-- Variant of Theme.Material.DayNight.Dialog.NoActionBar that has a nice minimum width for
- a regular dialog. -->
- <style name="Theme.Material.DayNight.Dialog.NoActionBar.MinWidth" parent="Theme.Material.Light.Dialog.NoActionBar.MinWidth" />
-
- <!-- Variant of Theme.Material.DayNight.Dialog that has a fixed size. -->
- <style name="Theme.Material.DayNight.Dialog.FixedSize" parent="Theme.Material.Light.Dialog.FixedSize" />
-
- <!-- Variant of Theme.Material.DayNight.Dialog.NoActionBar that has a fixed size. -->
- <style name="Theme.Material.DayNight.Dialog.NoActionBar.FixedSize" parent="Theme.Material.Light.Dialog.NoActionBar.FixedSize" />
-
- <!-- Theme for a window that will be displayed either full-screen on
- smaller screens (small, normal) or as a dialog on larger screens
- (large, xlarge). -->
- <style name="Theme.Material.DayNight.DialogWhenLarge" parent="Theme.Material.Light.DialogWhenLarge" />
-
- <!-- Theme for a window with a dark action bar that will be displayed
- either full-screen on smaller screens (small, normal) or as a dialog
- on larger screens (large, xlarge). -->
- <style name="Theme.Material.DayNight.DialogWhenLarge.DarkActionBar" parent="Theme.Material.Light.DialogWhenLarge.DarkActionBar" />
-
- <!-- Theme for a window without an action bar that will be displayed either full-screen
- on smaller screens (small, normal) or as a dialog on larger screens
- (large, xlarge). -->
- <style name="Theme.Material.DayNight.DialogWhenLarge.NoActionBar" parent="Theme.Material.Light.DialogWhenLarge.NoActionBar" />
-
- <!-- Theme for a presentation window on a secondary display. -->
- <style name="Theme.Material.DayNight.Dialog.Presentation" parent="Theme.Material.Light.Dialog.Presentation" />
-
- <!-- Material user theme for alert dialog windows, which is used by the
- {@link android.app.AlertDialog} class. -->
- <style name="Theme.Material.DayNight.Dialog.Alert" parent="Theme.Material.DayNight.Dialog.BaseAlert" />
- <style name="Theme.Material.DayNight.Dialog.BaseAlert" parent="Theme.Material.Light.Dialog.BaseAlert" />
-
- <style name="Theme.Material.DayNight.SearchBar" parent="Theme.Material.Light.SearchBar" />
- <style name="Theme.Material.DayNight.CompactMenu" parent="Theme.Material.Light.CompactMenu" />
-
-</resources>
diff --git a/core/res/res/xml/power_profile.xml b/core/res/res/xml/power_profile.xml
index 76b5fe1..2e593a3 100644
--- a/core/res/res/xml/power_profile.xml
+++ b/core/res/res/xml/power_profile.xml
@@ -94,11 +94,27 @@
<!-- This is the battery capacity in mAh (measured at nominal voltage) -->
<item name="battery.capacity">1000</item>
- <array name="wifi.batchedscan"> <!-- mA -->
- <value>.0002</value> <!-- 1-8/hr -->
- <value>.002</value> <!-- 9-64/hr -->
- <value>.02</value> <!-- 65-512/hr -->
- <value>.2</value> <!-- 513-4,096/hr -->
- <value>2</value> <!-- 4097-/hr -->
+ <!-- Wifi related values. -->
+ <!-- Idle Receive current for wifi radio in mA. 0 by default-->
+ <item name="wifi.controller.idle">0</item>
+ <!-- Rx current for wifi radio in mA. 0 by default-->
+ <item name="wifi.controller.rx">0</item>
+ <!-- Tx current for wifi radio in mA. 0 by default-->
+ <item name="wifi.controller.tx">0</item>
+ <!-- Current at each of the wifi Tx levels in mA. The number of tx levels varies per device
+ and is available only of wifi chipsets which support the tx level reporting. Use
+ wifi.tx for other chipsets. none by default -->
+ <array name="wifi.controller.tx_levels"> <!-- mA -->
</array>
+ <!-- Operating volatage for wifi radio in mV. 0 by default-->
+ <item name="wifi.controller.voltage">0</item>
+
+ <array name="wifi.batchedscan"> <!-- mA -->
+ <value>.0002</value> <!-- 1-8/hr -->
+ <value>.002</value> <!-- 9-64/hr -->
+ <value>.02</value> <!-- 65-512/hr -->
+ <value>.2</value> <!-- 513-4,096/hr -->
+ <value>2</value> <!-- 4097-/hr -->
+ </array>
+
</device>
diff --git a/core/tests/benchmarks/src/android/text/util/LinkifyBenchmark.java b/core/tests/benchmarks/src/android/text/util/LinkifyBenchmark.java
new file mode 100644
index 0000000..a6e433f
--- /dev/null
+++ b/core/tests/benchmarks/src/android/text/util/LinkifyBenchmark.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text.util;
+
+import com.google.caliper.AfterExperiment;
+import com.google.caliper.BeforeExperiment;
+import com.google.caliper.Benchmark;
+import com.google.caliper.Param;
+
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.util.Patterns;
+
+import java.util.regex.Pattern;
+
+public class LinkifyBenchmark {
+ private static final String MATCHING_STR = " http://user:pass@host.com:5432/path?k=v#f " +
+ "host.com:5432/path?k=v#f ";
+
+ private static final String NONMATCHING_STR = " Neque porro quisquam est qui dolorem ipsum " +
+ "quia dolor sit amet, consectetur, adipisci velit ";
+
+ // this pattern does not recognize strings without http scheme therefore is expected to be
+ // faster in MATCHING_STR case.
+ private static final Pattern BASIC_PATTERN = Pattern.compile(
+ "(?:\\b|$|^)http://[a-zA-Z0-9:\\.@\\?=#/]+(?:\\b|$|^)");
+
+ @Param({"1", "4", "16", "64", "256"})
+ private String mParamCopyAmount;
+
+ @Param({MATCHING_STR, NONMATCHING_STR})
+ private String mParamBasicText;
+
+ private Spannable mTestSpannable;
+
+ @BeforeExperiment
+ protected void setUp() throws Exception {
+ int copyAmount = Integer.valueOf(mParamCopyAmount);
+ StringBuilder strBuilder = new StringBuilder();
+ for (int i = 0; i < copyAmount; i++) {
+ strBuilder.append(mParamBasicText);
+ }
+ mTestSpannable = new SpannableString(strBuilder.toString());
+ }
+
+ @AfterExperiment
+ protected void tearDown() {
+ mTestSpannable = null;
+ }
+
+ @Benchmark
+ public void timeNewRegEx(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ Linkify.addLinks(mTestSpannable, Patterns.AUTOLINK_WEB_URL, "http://",
+ new String[]{"http://", "https://", "rtsp://"}, null, null);
+ }
+ }
+
+ @Benchmark
+ public void timeOldRegEx(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ Linkify.addLinks(mTestSpannable, Patterns.WEB_URL, "http://",
+ new String[]{"http://", "https://", "rtsp://"}, null, null);
+ }
+ }
+
+ @Benchmark
+ public void timeBasicRegEx(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ Linkify.addLinks(mTestSpannable, BASIC_PATTERN, "http://",
+ new String[]{"http://", "https://", "rtsp://"}, null, null);
+ }
+ }
+
+}
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index b780778..b7926cf 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1093,7 +1093,12 @@
</intent-filter>
</activity>
-
+ <activity android:name="android.view.ViewCaptureTestActivity" android:label="ViewCaptureTestActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
<!-- Activity-level metadata -->
<meta-data android:name="com.android.frameworks.coretests.isApp" android:value="true" />
@@ -1251,6 +1256,15 @@
<service android:name="android.os.BinderThreadPriorityService"
android:process=":BinderThreadPriorityService" />
+ <!-- Used by ApplyOverrideConfigurationTest -->
+ <activity android:name="android.app.activity.ApplyOverrideConfigurationActivity"
+ android:configChanges="orientation|screenSize">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
+
<!-- Application components used for search manager tests -->
<activity android:name="android.app.activity.SearchableActivity"
diff --git a/core/tests/coretests/res/drawable-nodpi/view_capture_test_no_children_golden.png b/core/tests/coretests/res/drawable-nodpi/view_capture_test_no_children_golden.png
new file mode 100644
index 0000000..52092c4
--- /dev/null
+++ b/core/tests/coretests/res/drawable-nodpi/view_capture_test_no_children_golden.png
Binary files differ
diff --git a/core/tests/coretests/res/drawable-nodpi/view_capture_test_with_children_golden.png b/core/tests/coretests/res/drawable-nodpi/view_capture_test_with_children_golden.png
new file mode 100644
index 0000000..3bcbd9a
--- /dev/null
+++ b/core/tests/coretests/res/drawable-nodpi/view_capture_test_with_children_golden.png
Binary files differ
diff --git a/core/tests/coretests/res/layout/view_capture_snapshot.xml b/core/tests/coretests/res/layout/view_capture_snapshot.xml
new file mode 100644
index 0000000..0b589a4
--- /dev/null
+++ b/core/tests/coretests/res/layout/view_capture_snapshot.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2016, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout android:id="@+id/capture"
+ android:background="#00f"
+ android:orientation="vertical"
+ android:layout_width="100px"
+ android:layout_height="100px">
+
+ <View android:id="@+id/child1"
+ android:background="#f00"
+ android:layout_width="match_parent"
+ android:layout_height="25px"/>
+
+ <View android:id="@+id/child2"
+ android:background="#fff"
+ android:layout_width="match_parent"
+ android:layout_height="25px"
+ android:visibility="invisible"/>
+
+ <View android:id="@+id/child3"
+ android:background="#ff0"
+ android:layout_width="match_parent"
+ android:layout_height="25px"
+ android:visibility="gone"/>
+
+ <View android:id="@+id/child4"
+ android:background="#0ff"
+ android:layout_width="match_parent"
+ android:layout_height="25px" />
+ </LinearLayout>
+</FrameLayout>
diff --git a/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java b/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java
index 4a53852..d1a5d28 100644
--- a/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java
@@ -284,20 +284,30 @@
Uri uri = getServerUri(DEFAULT_FILENAME);
enqueueResponse(buildResponse(HTTP_PARTIAL_CONTENT));
- doErrorTest(uri, DownloadManager.ERROR_UNHANDLED_HTTP_CODE);
+ doErrorTest(uri, DownloadManager.ERROR_CANNOT_RESUME);
}
/**
* Tests the download failure error from an unhandled HTTP status code
*/
@LargeTest
- public void testErrorHttpDataError_invalidRedirect() throws Exception {
+ public void testRelativeRedirect() throws Exception {
Uri uri = getServerUri(DEFAULT_FILENAME);
final MockResponse resp = buildResponse(HTTP_REDIRECT);
- resp.setHeader("Location", "://blah.blah.blah.com");
+ resp.setHeader("Location", ":" + uri.getSchemeSpecificPart());
enqueueResponse(resp);
- doErrorTest(uri, DownloadManager.ERROR_HTTP_DATA_ERROR);
+ byte[] blobData = generateData(DEFAULT_FILE_SIZE, DataType.TEXT);
+ enqueueResponse(buildResponse(HTTP_OK, blobData));
+
+ Request request = new Request(uri);
+ request.setTitle(DEFAULT_FILENAME);
+
+ long dlRequest = mDownloadManager.enqueue(request);
+ waitForDownloadOrTimeout(dlRequest);
+
+ verifyAndCleanupSingleFileDownload(dlRequest, blobData);
+ assertEquals(1, mReceiver.numDownloadsCompleted());
}
/**
diff --git a/core/tests/coretests/src/android/app/activity/ApplyOverrideConfigurationActivity.java b/core/tests/coretests/src/android/app/activity/ApplyOverrideConfigurationActivity.java
new file mode 100644
index 0000000..3df522d
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/ApplyOverrideConfigurationActivity.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.app.activity;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.Configuration;
+
+public class ApplyOverrideConfigurationActivity extends Activity {
+
+ @Override
+ protected void attachBaseContext(Context newBase) {
+ super.attachBaseContext(newBase);
+
+ Configuration overrideConfig = new Configuration();
+ overrideConfig.smallestScreenWidthDp = ApplyOverrideConfigurationTest.OVERRIDE_WIDTH;
+ applyOverrideConfiguration(overrideConfig);
+ }
+}
diff --git a/core/tests/coretests/src/android/app/activity/ApplyOverrideConfigurationTest.java b/core/tests/coretests/src/android/app/activity/ApplyOverrideConfigurationTest.java
new file mode 100644
index 0000000..15ed77e
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/ApplyOverrideConfigurationTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.app.activity;
+
+import android.app.UiAutomation;
+import android.content.res.Configuration;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.ActivityInstrumentationTestCase2;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ApplyOverrideConfigurationTest extends
+ ActivityInstrumentationTestCase2<ApplyOverrideConfigurationActivity> {
+
+ public static final int OVERRIDE_WIDTH = 9999;
+
+ public ApplyOverrideConfigurationTest() {
+ super(ApplyOverrideConfigurationActivity.class);
+ }
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ injectInstrumentation(InstrumentationRegistry.getInstrumentation());
+ getInstrumentation().getUiAutomation().setRotation(UiAutomation.ROTATION_FREEZE_0);
+ }
+
+ @Test
+ public void testConfigurationIsOverriden() throws Exception {
+ Configuration config = getActivity().getResources().getConfiguration();
+ assertEquals(OVERRIDE_WIDTH, config.smallestScreenWidthDp);
+
+ getInstrumentation().getUiAutomation().setRotation(UiAutomation.ROTATION_FREEZE_90);
+
+ config = getActivity().getResources().getConfiguration();
+ assertEquals(OVERRIDE_WIDTH, config.smallestScreenWidthDp);
+ }
+
+ @After
+ @Override
+ public void tearDown() throws Exception {
+ getInstrumentation().getUiAutomation().setRotation(UiAutomation.ROTATION_UNFREEZE);
+ super.tearDown();
+ }
+}
diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
index 3cadbf6..d4bb0f3 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
@@ -18,7 +18,7 @@
import android.annotation.NonNull;
import android.app.ResourcesManager;
import android.os.Binder;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.support.test.filters.SmallTest;
import android.util.DisplayMetrics;
import android.util.LocaleList;
import android.util.TypedValue;
@@ -58,7 +58,7 @@
}
@Override
- protected DisplayMetrics getDisplayMetricsLocked(int displayId) {
+ protected DisplayMetrics getDisplayMetrics(int displayId) {
return mDisplayMetrics;
}
};
@@ -173,25 +173,12 @@
// The implementations should be the same.
assertSame(resources1.getImpl(), resources2.getImpl());
-
- final Configuration overrideConfig = new Configuration();
- overrideConfig.orientation = Configuration.ORIENTATION_LANDSCAPE;
- Resources resources3 = mResourcesManager.getResources(
- activity2, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY,
- overrideConfig, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
-
- // Since we requested new resources for activity2, the resource should be the same
- // as the one returned before for activity2.
- assertSame(resources2, resources3);
-
- // But the implementation has changed.
- assertNotSame(resources1.getImpl(), resources2.getImpl());
}
@SmallTest
public void testThemesGetUpdatedWithNewImpl() {
Binder activity1 = new Binder();
- Resources resources1 = mResourcesManager.getResources(
+ Resources resources1 = mResourcesManager.createBaseActivityResources(
activity1, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
assertNotNull(resources1);
@@ -207,16 +194,59 @@
final Configuration overrideConfig = new Configuration();
overrideConfig.orientation = Configuration.ORIENTATION_LANDSCAPE;
- Resources resources2 = mResourcesManager.getResources(
- activity1, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY,
- overrideConfig, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
- assertNotNull(resources2);
- assertSame(resources1, resources2);
- assertSame(resources2, theme.getResources());
+ mResourcesManager.updateResourcesForActivity(activity1, overrideConfig);
+ assertSame(resources1, theme.getResources());
// Make sure we can still access the data.
assertTrue(theme.resolveAttribute(android.R.attr.windowNoTitle, value, true));
assertEquals(TypedValue.TYPE_INT_BOOLEAN, value.type);
assertTrue(value.data != 0);
}
+
+ @SmallTest
+ public void testMultipleResourcesForOneActivityGetUpdatedWhenActivityBaseUpdates() {
+ Binder activity1 = new Binder();
+
+ // Create a Resources for the Activity.
+ Configuration config1 = new Configuration();
+ config1.densityDpi = 280;
+ Resources resources1 = mResourcesManager.createBaseActivityResources(
+ activity1, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, config1,
+ CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
+ assertNotNull(resources1);
+
+ // Create a Resources based on the Activity.
+ Configuration config2 = new Configuration();
+ config2.screenLayout |= Configuration.SCREENLAYOUT_ROUND_YES;
+ Resources resources2 = mResourcesManager.getResources(
+ activity1, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, config2,
+ CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
+ assertNotNull(resources2);
+
+ assertNotSame(resources1, resources2);
+ assertNotSame(resources1.getImpl(), resources2.getImpl());
+
+ final Configuration expectedConfig1 = new Configuration();
+ expectedConfig1.setLocales(LocaleList.getAdjustedDefault());
+ expectedConfig1.densityDpi = 280;
+ assertEquals(expectedConfig1, resources1.getConfiguration());
+
+ // resources2 should be based on the Activity's override config, so the density should
+ // be the same as resources1.
+ final Configuration expectedConfig2 = new Configuration();
+ expectedConfig2.setLocales(LocaleList.getAdjustedDefault());
+ expectedConfig2.densityDpi = 280;
+ expectedConfig2.screenLayout |= Configuration.SCREENLAYOUT_ROUND_YES;
+ assertEquals(expectedConfig2, resources2.getConfiguration());
+
+ // Now update the Activity base override, and both resources should update.
+ config1.orientation = Configuration.ORIENTATION_LANDSCAPE;
+ mResourcesManager.updateResourcesForActivity(activity1, config1);
+
+ expectedConfig1.orientation = Configuration.ORIENTATION_LANDSCAPE;
+ assertEquals(expectedConfig1, resources1.getConfiguration());
+
+ expectedConfig2.orientation = Configuration.ORIENTATION_LANDSCAPE;
+ assertEquals(expectedConfig2, resources2.getConfiguration());
+ }
}
diff --git a/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
index 9ab62cc..e7aca78 100644
--- a/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
+++ b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
@@ -23,10 +23,10 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
import android.net.NetworkScorerAppManager.NetworkScorerAppData;
import android.os.UserHandle;
import android.test.InstrumentationTestCase;
-import android.util.Pair;
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
@@ -58,25 +58,26 @@
public void testGetAllValidScorers() throws Exception {
// Package 1 - Valid scorer.
- Pair<ResolveInfo, ResolveInfo> package1 = buildResolveInfo("package1", 1, true, true,
- false);
+ ResolveInfoHolder package1 = buildResolveInfo("package1", 1, true, true, false, false);
// Package 2 - Receiver does not have BROADCAST_NETWORK_PRIVILEGED permission.
- Pair<ResolveInfo, ResolveInfo> package2 = buildResolveInfo("package2", 2, false, true,
- false);
+ ResolveInfoHolder package2 = buildResolveInfo("package2", 2, false, true, false, false);
// Package 3 - App does not have SCORE_NETWORKS permission.
- Pair<ResolveInfo, ResolveInfo> package3 = buildResolveInfo("package3", 3, true, false,
- false);
+ ResolveInfoHolder package3 = buildResolveInfo("package3", 3, true, false, false, false);
// Package 4 - Valid scorer w/ optional config activity.
- Pair<ResolveInfo, ResolveInfo> package4 = buildResolveInfo("package4", 4, true, true, true);
+ ResolveInfoHolder package4 = buildResolveInfo("package4", 4, true, true, true, false);
- List<Pair<ResolveInfo, ResolveInfo>> scorers = new ArrayList<>();
+ // Package 5 - Valid scorer w/ optional service to bind to.
+ ResolveInfoHolder package5 = buildResolveInfo("package5", 5, true, true, false, true);
+
+ List<ResolveInfoHolder> scorers = new ArrayList<>();
scorers.add(package1);
scorers.add(package2);
scorers.add(package3);
scorers.add(package4);
+ scorers.add(package5);
setScorers(scorers);
Iterator<NetworkScorerAppData> result =
@@ -94,14 +95,20 @@
assertEquals(4, next.mPackageUid);
assertEquals(".ConfigActivity", next.mConfigurationActivityClassName);
+ assertTrue(result.hasNext());
+ next = result.next();
+ assertEquals("package5", next.mPackageName);
+ assertEquals(5, next.mPackageUid);
+ assertEquals(".ScoringService", next.mScoringServiceClassName);
+
assertFalse(result.hasNext());
}
- private void setScorers(List<Pair<ResolveInfo, ResolveInfo>> scorers) {
+ private void setScorers(List<ResolveInfoHolder> scorers) {
List<ResolveInfo> receivers = new ArrayList<>();
- for (final Pair<ResolveInfo, ResolveInfo> scorer : scorers) {
- receivers.add(scorer.first);
- if (scorer.second != null) {
+ for (final ResolveInfoHolder scorer : scorers) {
+ receivers.add(scorer.scorerResolveInfo);
+ if (scorer.configActivityResolveInfo != null) {
// This scorer has a config activity.
Mockito.when(mMockPm.queryIntentActivities(
Mockito.argThat(new ArgumentMatcher<Intent>() {
@@ -110,10 +117,26 @@
Intent intent = (Intent) object;
return NetworkScoreManager.ACTION_CUSTOM_ENABLE.equals(
intent.getAction())
- && scorer.first.activityInfo.packageName.equals(
+ && scorer.scorerResolveInfo.activityInfo.packageName.equals(
intent.getPackage());
}
- }), Mockito.eq(0))).thenReturn(Collections.singletonList(scorer.second));
+ }), Mockito.eq(0))).thenReturn(
+ Collections.singletonList(scorer.configActivityResolveInfo));
+ }
+
+ if (scorer.serviceResolveInfo != null) {
+ // This scorer has a service to bind to
+ Mockito.when(mMockPm.resolveService(
+ Mockito.argThat(new ArgumentMatcher<Intent>() {
+ @Override
+ public boolean matches(Object object) {
+ Intent intent = (Intent) object;
+ return NetworkScoreManager.ACTION_SCORE_NETWORKS.equals(
+ intent.getAction())
+ && scorer.scorerResolveInfo.activityInfo.packageName.equals(
+ intent.getPackage());
+ }
+ }), Mockito.eq(0))).thenReturn(scorer.serviceResolveInfo);
}
}
@@ -128,9 +151,9 @@
.thenReturn(receivers);
}
- private Pair<ResolveInfo, ResolveInfo> buildResolveInfo(String packageName, int packageUid,
- boolean hasReceiverPermission, boolean hasScorePermission, boolean hasConfigActivity)
- throws Exception {
+ private ResolveInfoHolder buildResolveInfo(String packageName, int packageUid,
+ boolean hasReceiverPermission, boolean hasScorePermission, boolean hasConfigActivity,
+ boolean hasServiceInfo) throws Exception {
Mockito.when(mMockPm.checkPermission(permission.SCORE_NETWORKS, packageName))
.thenReturn(hasScorePermission ?
PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED);
@@ -150,6 +173,27 @@
configActivityInfo.activityInfo = new ActivityInfo();
configActivityInfo.activityInfo.name = ".ConfigActivity";
}
- return Pair.create(resolveInfo, configActivityInfo);
+
+ ResolveInfo serviceInfo = null;
+ if (hasServiceInfo) {
+ serviceInfo = new ResolveInfo();
+ serviceInfo.serviceInfo = new ServiceInfo();
+ serviceInfo.serviceInfo.name = ".ScoringService";
+ }
+
+ return new ResolveInfoHolder(resolveInfo, configActivityInfo, serviceInfo);
+ }
+
+ private static class ResolveInfoHolder {
+ final ResolveInfo scorerResolveInfo;
+ final ResolveInfo configActivityResolveInfo;
+ final ResolveInfo serviceResolveInfo;
+
+ public ResolveInfoHolder(ResolveInfo scorerResolveInfo,
+ ResolveInfo configActivityResolveInfo, ResolveInfo serviceResolveInfo) {
+ this.scorerResolveInfo = scorerResolveInfo;
+ this.configActivityResolveInfo = configActivityResolveInfo;
+ this.serviceResolveInfo = serviceResolveInfo;
+ }
}
}
diff --git a/core/tests/coretests/src/android/print/BasePrintTest.java b/core/tests/coretests/src/android/print/BasePrintTest.java
index c9bc8aa..d56a405 100644
--- a/core/tests/coretests/src/android/print/BasePrintTest.java
+++ b/core/tests/coretests/src/android/print/BasePrintTest.java
@@ -28,6 +28,7 @@
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
import android.os.SystemClock;
import android.print.PrintAttributes;
@@ -281,7 +282,8 @@
}
if (onRequestCustomPrinterIcon != null) {
doAnswer(onRequestCustomPrinterIcon).when(callbacks).onRequestCustomPrinterIcon(
- any(PrinterId.class), any(CustomPrinterIconCallback.class));
+ any(PrinterId.class), any(CancellationSignal.class),
+ any(CustomPrinterIconCallback.class));
}
if (onStopPrinterStateTracking != null) {
doAnswer(onStopPrinterStateTracking).when(callbacks).onStopPrinterStateTracking(
diff --git a/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java b/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
index ec8cd71..d491ec4 100644
--- a/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
+++ b/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
@@ -31,6 +31,7 @@
import android.print.PrintAttributes.MediaSize;
import android.print.PrintAttributes.Resolution;
import android.printservice.PrintServiceInfo;
+import android.printservice.recommendation.IRecommendationsChangeListener;
import android.print.mockservice.MockPrintService;
import android.print.mockservice.PrintServiceCallbacks;
@@ -181,6 +182,17 @@
new Handler(Looper.getMainLooper()));
}
+ /**
+ * Create a IPrintServiceRecommendationsChangeListener object.
+ *
+ * @return the object
+ * @throws Exception if the object could not be created.
+ */
+ private IRecommendationsChangeListener
+ createMockIPrintServiceRecommendationsChangeListener() throws Exception {
+ return new PrintManager.PrintServiceRecommendationsChangeListenerWrapper(null,
+ new Handler(Looper.getMainLooper()));
+ }
/**
* Create a IPrinterDiscoveryObserver object.
@@ -503,7 +515,7 @@
public void testGetPrintServices() throws Exception {
List<PrintServiceInfo> printServices = mIPrintManager.getPrintServices(
PrintManager.ALL_SERVICES, mUserId);
- assertTrue(printServices.size() >= 2);
+ assertTrue(printServices.size() >= 1);
printServices = mIPrintManager.getPrintServices(0, mUserId);
assertEquals(printServices, null);
@@ -559,6 +571,61 @@
}
/**
+ * test IPrintManager.addPrintServiceRecommendationsChangeListener
+ */
+ @MediumTest
+ public void testAddPrintServiceRecommendationsChangeListener() throws Exception {
+ final IRecommendationsChangeListener listener =
+ createMockIPrintServiceRecommendationsChangeListener();
+
+ mIPrintManager.addPrintServiceRecommendationsChangeListener(listener, mUserId);
+
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.addPrintServiceRecommendationsChangeListener(null, mUserId);
+ }
+ }, NullPointerException.class);
+
+ // Cannot test bad user Id as these tests are allowed to call across users
+ }
+
+ /**
+ * test IPrintManager.removePrintServicesChangeListener
+ */
+ @MediumTest
+ public void testRemovePrintServiceRecommendationsChangeListener() throws Exception {
+ final IRecommendationsChangeListener listener =
+ createMockIPrintServiceRecommendationsChangeListener();
+
+ mIPrintManager.addPrintServiceRecommendationsChangeListener(listener, mUserId);
+ mIPrintManager.removePrintServiceRecommendationsChangeListener(listener, mUserId);
+
+ // Removing unknown listeners is a no-op
+ mIPrintManager.removePrintServiceRecommendationsChangeListener(listener, mUserId);
+
+ mIPrintManager.addPrintServiceRecommendationsChangeListener(listener, mUserId);
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.removePrintServiceRecommendationsChangeListener(null, mUserId);
+ }
+ }, NullPointerException.class);
+
+ // Cannot test bad user Id as these tests are allowed to call across users
+ }
+
+ /**
+ * test IPrintManager.getPrintServiceRecommendations
+ */
+ @MediumTest
+ public void testGetPrintServiceRecommendations() throws Exception {
+ mIPrintManager.getPrintServiceRecommendations(mUserId);
+
+ // Cannot test bad user Id as these tests are allowed to call across users
+ }
+
+ /**
* test IPrintManager.createPrinterDiscoverySession
*/
@MediumTest
diff --git a/core/tests/coretests/src/android/print/mockservice/PrinterDiscoverySessionCallbacks.java b/core/tests/coretests/src/android/print/mockservice/PrinterDiscoverySessionCallbacks.java
index 26b7cae..be002e2 100644
--- a/core/tests/coretests/src/android/print/mockservice/PrinterDiscoverySessionCallbacks.java
+++ b/core/tests/coretests/src/android/print/mockservice/PrinterDiscoverySessionCallbacks.java
@@ -16,6 +16,7 @@
package android.print.mockservice;
+import android.os.CancellationSignal;
import android.print.PrinterId;
import android.printservice.CustomPrinterIconCallback;
@@ -42,7 +43,7 @@
public abstract void onStartPrinterStateTracking(PrinterId printerId);
public abstract void onRequestCustomPrinterIcon(PrinterId printerId,
- CustomPrinterIconCallback callback);
+ CancellationSignal cancellationSignal, CustomPrinterIconCallback callback);
public abstract void onStopPrinterStateTracking(PrinterId printerId);
diff --git a/core/tests/coretests/src/android/print/mockservice/StubbablePrinterDiscoverySession.java b/core/tests/coretests/src/android/print/mockservice/StubbablePrinterDiscoverySession.java
index 04683f2..e132d79 100644
--- a/core/tests/coretests/src/android/print/mockservice/StubbablePrinterDiscoverySession.java
+++ b/core/tests/coretests/src/android/print/mockservice/StubbablePrinterDiscoverySession.java
@@ -16,6 +16,7 @@
package android.print.mockservice;
+import android.os.CancellationSignal;
import android.print.PrinterId;
import android.printservice.CustomPrinterIconCallback;
import android.printservice.PrintService;
@@ -70,9 +71,9 @@
@Override
public void onRequestCustomPrinterIcon(PrinterId printerId,
- CustomPrinterIconCallback callback) {
+ CancellationSignal cancellationSignal, CustomPrinterIconCallback callback) {
if (mCallbacks != null) {
- mCallbacks.onRequestCustomPrinterIcon(printerId, callback);
+ mCallbacks.onRequestCustomPrinterIcon(printerId, cancellationSignal, callback);
}
}
diff --git a/core/tests/coretests/src/android/view/ViewCaptureTest.java b/core/tests/coretests/src/android/view/ViewCaptureTest.java
new file mode 100644
index 0000000..15cfe23
--- /dev/null
+++ b/core/tests/coretests/src/android/view/ViewCaptureTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.view;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.SparseIntArray;
+
+import com.android.frameworks.coretests.R;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertTrue;
+
+@RunWith(AndroidJUnit4.class)
+public class ViewCaptureTest {
+
+ private static final SparseIntArray EXPECTED_CHILDREN_VISIBILITY = new SparseIntArray();
+ static {
+ EXPECTED_CHILDREN_VISIBILITY.append(R.id.child1, View.VISIBLE);
+ EXPECTED_CHILDREN_VISIBILITY.append(R.id.child2, View.INVISIBLE);
+ EXPECTED_CHILDREN_VISIBILITY.append(R.id.child3, View.GONE);
+ EXPECTED_CHILDREN_VISIBILITY.append(R.id.child4, View.VISIBLE);
+ }
+
+ @Rule
+ public ActivityTestRule<ViewCaptureTestActivity> mActivityRule = new ActivityTestRule<>(
+ ViewCaptureTestActivity.class);
+
+ private Activity mActivity;
+ private ViewGroup mViewToCapture;
+
+ @Before
+ public void setUp() throws Exception {
+ mActivity = mActivityRule.getActivity();
+ mViewToCapture = (ViewGroup) mActivity.findViewById(R.id.capture);
+ }
+
+ @Test
+ @SmallTest
+ public void testCreateSnapshot() {
+ assertChildrenVisibility();
+ testCreateSnapshot(true, R.drawable.view_capture_test_no_children_golden);
+ assertChildrenVisibility();
+ testCreateSnapshot(false, R.drawable.view_capture_test_with_children_golden);
+ assertChildrenVisibility();
+ }
+
+ private void testCreateSnapshot(boolean skipChildren, int goldenResId) {
+ Bitmap result = mViewToCapture.createSnapshot(Bitmap.Config.ARGB_8888, 0, skipChildren);
+ Bitmap golden = BitmapFactory.decodeResource(mActivity.getResources(), goldenResId);
+ assertTrue(golden.sameAs(result));
+ }
+
+ private void assertChildrenVisibility() {
+ for (int i = 0; i < EXPECTED_CHILDREN_VISIBILITY.size(); i++) {
+ int id = EXPECTED_CHILDREN_VISIBILITY.keyAt(i);
+ View child = mViewToCapture.findViewById(id);
+ Assert.assertEquals(EXPECTED_CHILDREN_VISIBILITY.get(id), child.getVisibility());
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/view/ViewCaptureTestActivity.java b/core/tests/coretests/src/android/view/ViewCaptureTestActivity.java
new file mode 100644
index 0000000..20e3eb5
--- /dev/null
+++ b/core/tests/coretests/src/android/view/ViewCaptureTestActivity.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.view;
+
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.os.Bundle;
+import com.android.frameworks.coretests.R;
+
+public class ViewCaptureTestActivity extends Activity {
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.view_capture_snapshot);
+ }
+}
diff --git a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
index a37abf1..eafe427 100644
--- a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
+++ b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
@@ -16,12 +16,35 @@
package android.widget;
-import android.app.Activity;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.Espresso.pressBack;
+import static android.support.test.espresso.action.ViewActions.clearText;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.action.ViewActions.replaceText;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static android.widget.espresso.DragHandleUtils.onHandleView;
+import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarContainsItem;
+import static android.widget.espresso.FloatingToolbarEspressoUtils.clickFloatingToolbarItem;
+import static android.widget.espresso.FloatingToolbarEspressoUtils.sleepForFloatingToolbarPopup;
+import static android.widget.espresso.SuggestionsPopupwindowUtils.assertSuggestionsPopupContainsItem;
+import static android.widget.espresso.SuggestionsPopupwindowUtils.assertSuggestionsPopupIsDisplayed;
+import static android.widget.espresso.SuggestionsPopupwindowUtils.assertSuggestionsPopupIsNotDisplayed;
+import static android.widget.espresso.SuggestionsPopupwindowUtils.clickSuggestionsPopupItem;
+import static android.widget.espresso.SuggestionsPopupwindowUtils.onSuggestionsPopup;
+import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
+import static android.widget.espresso.TextViewActions.longPressOnTextAtIndex;
+import static org.hamcrest.Matchers.is;
import android.content.res.TypedArray;
+import android.support.test.espresso.NoMatchingViewException;
+import android.support.test.espresso.ViewAssertion;
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.Suppress;
import android.text.Selection;
-import android.text.SpannableStringBuilder;
+import android.text.Spannable;
import android.text.Spanned;
import android.text.TextPaint;
import android.text.style.SuggestionSpan;
@@ -41,54 +64,215 @@
super(TextViewActivity.class);
}
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ getActivity();
+ }
+
+ private void setSuggestionSpan(SuggestionSpan span, int start, int end) {
+ final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
+ textView.post(
+ () -> {
+ final Spannable text = (Spannable) textView.getText();
+ text.setSpan(span, start, end, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+ Selection.setSelection(text, (start + end) / 2);
+ });
+ getInstrumentation().waitForIdleSync();
+ }
+
+ @SmallTest
+ public void testOnTextContextMenuItem() {
+ final String text = "abc def ghi";
+
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(replaceText(text));
+
+ final SuggestionSpan suggestionSpan = new SuggestionSpan(getActivity(),
+ new String[]{"DEF", "Def"}, SuggestionSpan.FLAG_AUTO_CORRECTION);
+ setSuggestionSpan(suggestionSpan, text.indexOf('d'), text.indexOf('f') + 1);
+
+ final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
+ textView.post(() -> textView.onTextContextMenuItem(TextView.ID_REPLACE));
+ getInstrumentation().waitForIdleSync();
+
+ assertSuggestionsPopupIsDisplayed();
+ }
+
+ @SmallTest
+ public void testSelectionActionMode() {
+ final String text = "abc def ghi";
+
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(replaceText(text));
+
+ final SuggestionSpan suggestionSpan = new SuggestionSpan(getActivity(),
+ new String[]{"DEF", "Def"}, SuggestionSpan.FLAG_AUTO_CORRECTION);
+ setSuggestionSpan(suggestionSpan, text.indexOf('d'), text.indexOf('f') + 1);
+
+ onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('e')));
+ sleepForFloatingToolbarPopup();
+ assertFloatingToolbarContainsItem(
+ getActivity().getString(com.android.internal.R.string.replace));
+ sleepForFloatingToolbarPopup();
+ clickFloatingToolbarItem(
+ getActivity().getString(com.android.internal.R.string.replace));
+
+ assertSuggestionsPopupIsDisplayed();
+ }
+
+ @SmallTest
+ public void testInsertionActionMode() {
+ final String text = "abc def ghi";
+
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(replaceText(text));
+
+ final SuggestionSpan suggestionSpan = new SuggestionSpan(getActivity(),
+ new String[]{"DEF", "Def"}, SuggestionSpan.FLAG_AUTO_CORRECTION);
+ setSuggestionSpan(suggestionSpan, text.indexOf('d'), text.indexOf('f') + 1);
+
+ onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.indexOf('e')));
+ onHandleView(com.android.internal.R.id.insertion_handle).perform(click());
+ sleepForFloatingToolbarPopup();
+ assertFloatingToolbarContainsItem(
+ getActivity().getString(com.android.internal.R.string.replace));
+ clickFloatingToolbarItem(
+ getActivity().getString(com.android.internal.R.string.replace));
+
+ assertSuggestionsPopupIsDisplayed();
+ }
+
+ private void showSuggestionsPopup() {
+ final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
+ textView.post(() -> textView.onTextContextMenuItem(TextView.ID_REPLACE));
+ getInstrumentation().waitForIdleSync();
+ assertSuggestionsPopupIsDisplayed();
+ }
+
+ @SmallTest
+ public void testSuggestionItems() {
+ final String text = "abc def ghi";
+
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(replaceText(text));
+
+ final SuggestionSpan suggestionSpan = new SuggestionSpan(getActivity(),
+ new String[]{"DEF", "Def"}, SuggestionSpan.FLAG_AUTO_CORRECTION);
+ setSuggestionSpan(suggestionSpan, text.indexOf('d'), text.indexOf('f') + 1);
+
+ showSuggestionsPopup();
+
+ assertSuggestionsPopupIsDisplayed();
+ assertSuggestionsPopupContainsItem("DEF");
+ assertSuggestionsPopupContainsItem("Def");
+ assertSuggestionsPopupContainsItem(
+ getActivity().getString(com.android.internal.R.string.delete));
+
+ // Select an item.
+ clickSuggestionsPopupItem("DEF");
+ assertSuggestionsPopupIsNotDisplayed();
+ onView(withId(R.id.textview)).check(matches(withText("abc DEF ghi")));
+
+ showSuggestionsPopup();
+ assertSuggestionsPopupIsDisplayed();
+ assertSuggestionsPopupContainsItem("def");
+ assertSuggestionsPopupContainsItem("Def");
+ assertSuggestionsPopupContainsItem(
+ getActivity().getString(com.android.internal.R.string.delete));
+
+ // Delete
+ clickSuggestionsPopupItem(
+ getActivity().getString(com.android.internal.R.string.delete));
+ assertSuggestionsPopupIsNotDisplayed();
+ onView(withId(R.id.textview)).check(matches(withText("abc ghi")));
+ }
+
+ @SmallTest
+ public void testMisspelled() {
+ final String text = "abc def ghi";
+
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(replaceText(text));
+
+ final SuggestionSpan suggestionSpan = new SuggestionSpan(getActivity(),
+ new String[]{"DEF", "Def"}, SuggestionSpan.FLAG_MISSPELLED);
+ setSuggestionSpan(suggestionSpan, text.indexOf('d'), text.indexOf('f') + 1);
+
+ showSuggestionsPopup();
+
+ assertSuggestionsPopupIsDisplayed();
+ assertSuggestionsPopupContainsItem("DEF");
+ assertSuggestionsPopupContainsItem("Def");
+ assertSuggestionsPopupContainsItem(
+ getActivity().getString(com.android.internal.R.string.addToDictionary));
+ assertSuggestionsPopupContainsItem(
+ getActivity().getString(com.android.internal.R.string.delete));
+
+ // Click "Add to dictionary".
+ clickSuggestionsPopupItem(
+ getActivity().getString(com.android.internal.R.string.addToDictionary));
+ // TODO: Check if add to dictionary dialog is displayed.
+ }
+
+ @SmallTest
+ public void testEasyCorrect() {
+ final String text = "abc def ghi";
+
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(replaceText(text));
+
+ final SuggestionSpan suggestionSpan = new SuggestionSpan(getActivity(),
+ new String[]{"DEF", "Def"},
+ SuggestionSpan.FLAG_EASY_CORRECT | SuggestionSpan.FLAG_MISSPELLED);
+ setSuggestionSpan(suggestionSpan, text.indexOf('d'), text.indexOf('f') + 1);
+
+ onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.indexOf('e')));
+
+ assertSuggestionsPopupIsDisplayed();
+ assertSuggestionsPopupContainsItem("DEF");
+ assertSuggestionsPopupContainsItem("Def");
+ assertSuggestionsPopupContainsItem(
+ getActivity().getString(com.android.internal.R.string.delete));
+
+ // Select an item.
+ clickSuggestionsPopupItem("DEF");
+ assertSuggestionsPopupIsNotDisplayed();
+ onView(withId(R.id.textview)).check(matches(withText("abc DEF ghi")));
+
+ onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.indexOf('e')));
+ assertSuggestionsPopupIsNotDisplayed();
+
+ showSuggestionsPopup();
+ assertSuggestionsPopupIsDisplayed();
+ assertSuggestionsPopupContainsItem("def");
+ assertSuggestionsPopupContainsItem("Def");
+ assertSuggestionsPopupContainsItem(
+ getActivity().getString(com.android.internal.R.string.delete));
+ }
+
@SmallTest
public void testTextAppearanceInSuggestionsPopup() {
- final Activity activity = getActivity();
+ final String text = "abc def ghi";
- final String sampleText = "abc def ghi";
final String[] singleWordCandidates = {"DEF", "Def"};
- final SuggestionSpan singleWordSuggestionSpan = new SuggestionSpan(activity,
- singleWordCandidates, SuggestionSpan.FLAG_AUTO_CORRECTION);
- final int singleWordSpanStart = 4;
- final int singleWordSpanEnd = 7;
-
+ final SuggestionSpan suggestionSpan = new SuggestionSpan(getActivity(),
+ singleWordCandidates, SuggestionSpan.FLAG_MISSPELLED);
final String[] multiWordCandidates = {"ABC DEF GHI", "Abc Def Ghi"};
- final SuggestionSpan multiWordSuggestionSpan = new SuggestionSpan(activity,
- multiWordCandidates, SuggestionSpan.FLAG_AUTO_CORRECTION);
- final int multiWordSpanStart = 0;
- final int multiWordSpanEnd = 11;
+ final SuggestionSpan multiWordSuggestionSpan = new SuggestionSpan(getActivity(),
+ multiWordCandidates, SuggestionSpan.FLAG_MISSPELLED);
- TypedArray array = activity.obtainStyledAttributes(com.android.internal.R.styleable.Theme);
- int id = array.getResourceId(
+ final TypedArray array =
+ getActivity().obtainStyledAttributes(com.android.internal.R.styleable.Theme);
+ final int id = array.getResourceId(
com.android.internal.R.styleable.Theme_textEditSuggestionHighlightStyle, 0);
array.recycle();
-
- TextAppearanceSpan expectedSpan = new TextAppearanceSpan(activity, id);
- TextPaint tmpTp = new TextPaint();
+ final TextAppearanceSpan expectedSpan = new TextAppearanceSpan(getActivity(), id);
+ final TextPaint tmpTp = new TextPaint();
expectedSpan.updateDrawState(tmpTp);
final int expectedHighlightTextColor = tmpTp.getColor();
final float expectedHighlightTextSize = tmpTp.getTextSize();
-
- final EditText editText = (EditText) activity.findViewById(R.id.textview);
- final Editor editor = editText.getEditorForTesting();
- assertNotNull(editor);
-
- // Request to show SuggestionsPopupWindow.
- Runnable showSuggestionWindowRunner = new Runnable() {
- @Override
- public void run() {
- SpannableStringBuilder ssb = new SpannableStringBuilder();
- ssb.append(sampleText);
- ssb.setSpan(singleWordSuggestionSpan, singleWordSpanStart, singleWordSpanEnd,
- Spanned.SPAN_INCLUSIVE_INCLUSIVE);
- ssb.setSpan(multiWordSuggestionSpan, multiWordSpanStart, multiWordSpanEnd,
- Spanned.SPAN_INCLUSIVE_INCLUSIVE);
- editText.setText(ssb);
-
- Selection.setSelection(editText.getText(), singleWordSpanStart, singleWordSpanEnd);
- editText.onTextContextMenuItem(TextView.ID_REPLACE);
- }
- };
+ final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
// In this test, the SuggestionsPopupWindow looks like
// abc def ghi
@@ -101,96 +285,74 @@
// | DELETE |
// -----------------
// *XX* means that XX is highlighted.
- Runnable popupVaridator = new Runnable() {
- @Override
- public void run() {
- Editor.SuggestionsPopupWindow popupWindow =
- editor.getSuggestionsPopupWindowForTesting();
- assertNotNull(popupWindow);
+ for (int i = 0; i < 2; i++) {
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(replaceText(text));
+ setSuggestionSpan(suggestionSpan, text.indexOf('d'), text.indexOf('f') + 1);
+ setSuggestionSpan(multiWordSuggestionSpan, 0, text.length());
- LinearLayout linearLayout = (LinearLayout) popupWindow.getContentViewForTesting();
- assertNotNull(linearLayout);
+ showSuggestionsPopup();
+ assertSuggestionsPopupIsDisplayed();
+ assertSuggestionsPopupContainsItem("abc DEF ghi");
+ assertSuggestionsPopupContainsItem("abc Def ghi");
+ assertSuggestionsPopupContainsItem("ABC DEF GHI");
+ assertSuggestionsPopupContainsItem("Abc Def Ghi");
+ assertSuggestionsPopupContainsItem(
+ getActivity().getString(com.android.internal.R.string.delete));
- ListView listView = (ListView)linearLayout.findViewById(
- com.android.internal.R.id.suggestionContainer);
- assertNotNull(listView);
+ onSuggestionsPopup().check(new ViewAssertion() {
+ @Override
+ public void check(View view, NoMatchingViewException e) {
+ final ListView listView = (ListView) view.findViewById(
+ com.android.internal.R.id.suggestionContainer);
+ assertNotNull(listView);
+ final int childNum = listView.getChildCount();
+ assertEquals(singleWordCandidates.length + multiWordCandidates.length,
+ childNum);
- int childNum = listView.getChildCount();
- assertEquals(singleWordCandidates.length + multiWordCandidates.length, childNum);
+ for (int j = 0; j < childNum; j++) {
+ final TextView suggestion = (TextView) listView.getChildAt(j);
+ assertNotNull(suggestion);
+ final Spanned spanned = (Spanned) suggestion.getText();
+ assertNotNull(spanned);
- for (int i = 0; i < singleWordCandidates.length; ++i) {
- TextView textView = (TextView) listView.getChildAt(i);
- assertNotNull(textView);
+ // Check that the suggestion item order is kept.
+ final String expectedText;
+ if (j < singleWordCandidates.length) {
+ expectedText = "abc " + singleWordCandidates[j] + " ghi";
+ } else {
+ expectedText = multiWordCandidates[j - singleWordCandidates.length];
+ }
+ assertEquals(expectedText, spanned.toString());
- Spanned spanned = (Spanned) textView.getText();
- assertNotNull(spanned);
+ // Check that the text is highlighted with correct color and text size.
+ final TextAppearanceSpan[] taSpan = spanned.getSpans(
+ text.indexOf('d'), text.indexOf('f') + 1, TextAppearanceSpan.class);
+ assertEquals(1, taSpan.length);
+ TextPaint tp = new TextPaint();
+ taSpan[0].updateDrawState(tp);
+ assertEquals(expectedHighlightTextColor, tp.getColor());
+ assertEquals(expectedHighlightTextSize, tp.getTextSize());
- // Check that the suggestion item order is kept.
- String expectedText = "abc " + singleWordCandidates[i] + " ghi";
- assertEquals(expectedText, spanned.toString());
-
- // Check that the text is highlighted with correct color and text size.
- TextAppearanceSpan[] taSpan = spanned.getSpans(singleWordSpanStart,
- singleWordSpanEnd, TextAppearanceSpan.class);
- assertEquals(1, taSpan.length);
- TextPaint tp = new TextPaint();
- taSpan[0].updateDrawState(tp);
- assertEquals(expectedHighlightTextColor, tp.getColor());
- assertEquals(expectedHighlightTextSize, tp.getTextSize());
-
- // Check only center word is highlighted.
- assertEquals(singleWordSpanStart, spanned.getSpanStart(taSpan[0]));
- assertEquals(singleWordSpanEnd, spanned.getSpanEnd(taSpan[0]));
+ // Check the correct part of the text is highlighted.
+ final int expectedStart;
+ final int expectedEnd;
+ if (j < singleWordCandidates.length) {
+ expectedStart = text.indexOf('d');
+ expectedEnd = text.indexOf('f') + 1;
+ } else {
+ expectedStart = 0;
+ expectedEnd = text.length();
+ }
+ assertEquals(expectedStart, spanned.getSpanStart(taSpan[0]));
+ assertEquals(expectedEnd, spanned.getSpanEnd(taSpan[0]));
+ }
}
-
- for (int i = 0; i < multiWordCandidates.length; ++i) {
- int indexInListView = singleWordCandidates.length + i;
- TextView textView = (TextView) listView.getChildAt(indexInListView);
- assertNotNull(textView);
-
- Spanned spanned = (Spanned) textView.getText();
- assertNotNull(spanned);
-
- // Check that the suggestion item order is kept.
- assertEquals(multiWordCandidates[i], spanned.toString());
-
- // Check that the text is highlighted with correct color and text size.
- TextAppearanceSpan[] taSpan = spanned.getSpans(
- 0, multiWordCandidates[i].length(), TextAppearanceSpan.class);
- assertEquals(1, taSpan.length);
- TextPaint tp = new TextPaint();
- taSpan[0].updateDrawState(tp);
- assertEquals(expectedHighlightTextColor, tp.getColor());
- assertEquals(expectedHighlightTextSize, tp.getTextSize());
-
- // Check the whole text is highlighted.
- assertEquals(multiWordSpanStart, spanned.getSpanStart(taSpan[0]));
- assertEquals(multiWordSpanEnd, spanned.getSpanEnd(taSpan[0]));
- }
-
- TextView deleteButton = (TextView)linearLayout.findViewById(
- com.android.internal.R.id.deleteButton);
- assertEquals(View.VISIBLE, deleteButton.getWindowVisibility());
- }
- };
-
- // Show the SuggestionWindow and verify the contents.
- activity.runOnUiThread(showSuggestionWindowRunner);
- getInstrumentation().waitForIdleSync();
- activity.runOnUiThread(popupVaridator);
-
- // Request to hide the SuggestionPopupWindow and wait until it is hidden.
- activity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- editText.setText("");
- }
- });
- getInstrumentation().waitForIdleSync();
-
- // Show and verify the contents again.
- activity.runOnUiThread(showSuggestionWindowRunner);
- getInstrumentation().waitForIdleSync();
- activity.runOnUiThread(popupVaridator);
+ });
+ pressBack();
+ onView(withId(R.id.textview))
+ .inRoot(withDecorView(is(getActivity().getWindow().getDecorView())))
+ .perform(clearText());
+ }
}
}
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
index 3fbc16a..edb749b95 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
@@ -16,6 +16,10 @@
package android.widget;
+
+import static android.widget.espresso.ContextMenuUtils.assertContextMenuContainsItemDisabled;
+import static android.widget.espresso.ContextMenuUtils.assertContextMenuContainsItemEnabled;
+import static android.widget.espresso.ContextMenuUtils.assertContextMenuIsNotDisplayed;
import static android.widget.espresso.DragHandleUtils.assertNoSelectionHandles;
import static android.widget.espresso.DragHandleUtils.onHandleView;
import static android.widget.espresso.TextViewActions.mouseClickOnTextAtIndex;
@@ -41,12 +45,9 @@
import com.android.frameworks.coretests.R;
-import android.support.test.espresso.Espresso;
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.SmallTest;
-import android.test.suitebuilder.annotation.Suppress;
import android.view.MotionEvent;
-import android.widget.espresso.ContextMenuUtils;
/**
* Tests mouse interaction of the TextView widget from an Activity
@@ -58,7 +59,8 @@
}
@Override
- public void setUp() {
+ public void setUp() throws Exception {
+ super.setUp();
getActivity();
}
@@ -98,34 +100,33 @@
}
@SmallTest
- @Suppress
public void testContextMenu() throws Exception {
final String text = "abc def ghi.";
onView(withId(R.id.textview)).perform(click());
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
- ContextMenuUtils.assertContextMenuIsNotDisplayed();
+ assertContextMenuIsNotDisplayed();
onView(withId(R.id.textview)).perform(
mouseClickOnTextAtIndex(text.indexOf("d"), MotionEvent.BUTTON_SECONDARY));
- ContextMenuUtils.assertContextMenuContainsItemDisabled(
+ assertContextMenuContainsItemDisabled(
getActivity().getString(com.android.internal.R.string.copy));
- ContextMenuUtils.assertContextMenuContainsItemEnabled(
+ assertContextMenuContainsItemEnabled(
getActivity().getString(com.android.internal.R.string.undo));
// Hide context menu.
pressBack();
- ContextMenuUtils.assertContextMenuIsNotDisplayed();
+ assertContextMenuIsNotDisplayed();
onView(withId(R.id.textview)).perform(
mouseDragOnText(text.indexOf("c"), text.indexOf("h")));
onView(withId(R.id.textview)).perform(
mouseClickOnTextAtIndex(text.indexOf("d"), MotionEvent.BUTTON_SECONDARY));
- ContextMenuUtils.assertContextMenuContainsItemEnabled(
+ assertContextMenuContainsItemEnabled(
getActivity().getString(com.android.internal.R.string.copy));
- ContextMenuUtils.assertContextMenuContainsItemEnabled(
+ assertContextMenuContainsItemEnabled(
getActivity().getString(com.android.internal.R.string.undo));
// Hide context menu.
@@ -135,9 +136,9 @@
onView(withId(R.id.textview)).perform(
mouseClickOnTextAtIndex(text.indexOf("i"), MotionEvent.BUTTON_SECONDARY));
- ContextMenuUtils.assertContextMenuContainsItemDisabled(
+ assertContextMenuContainsItemDisabled(
getActivity().getString(com.android.internal.R.string.copy));
- ContextMenuUtils.assertContextMenuContainsItemEnabled(
+ assertContextMenuContainsItemEnabled(
getActivity().getString(com.android.internal.R.string.undo));
// Hide context menu.
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 91d57e7..67ffd2b 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -19,7 +19,6 @@
import static android.support.test.espresso.action.ViewActions.longClick;
import static android.widget.espresso.DragHandleUtils.assertNoSelectionHandles;
import static android.widget.espresso.DragHandleUtils.onHandleView;
-import static android.widget.espresso.FloatingToolbarEspressoUtils.onFloatingToolBarItem;
import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
import static android.widget.espresso.TextViewActions.doubleTapAndDragOnText;
import static android.widget.espresso.TextViewActions.doubleClickOnTextAtIndex;
@@ -31,9 +30,10 @@
import static android.widget.espresso.TextViewAssertions.hasSelection;
import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarIsDisplayed;
import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarIsNotDisplayed;
-import static android.widget.espresso.FloatingToolbarEspressoUtils.sleepForFloatingToolbarPopup;
import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarContainsItem;
import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarDoesNotContainItem;
+import static android.widget.espresso.FloatingToolbarEspressoUtils.clickFloatingToolbarItem;
+import static android.widget.espresso.FloatingToolbarEspressoUtils.sleepForFloatingToolbarPopup;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.action.ViewActions.pressKey;
@@ -67,7 +67,8 @@
}
@Override
- public void setUp() {
+ public void setUp() throws Exception {
+ super.setUp();
getActivity();
}
@@ -256,7 +257,8 @@
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView("test"));
onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(1));
- onFloatingToolBarItem(withText(com.android.internal.R.string.cut)).perform(click());
+ clickFloatingToolbarItem(
+ getActivity().getString(com.android.internal.R.string.cut));
onView(withId(R.id.textview)).perform(longClick());
sleepForFloatingToolbarPopup();
@@ -385,6 +387,51 @@
}
@SmallTest
+ public void testSelectionHandles_bidi() throws Exception {
+ final String text = "abc \u0621\u0622\u0623 def";
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(replaceText(text));
+
+ assertNoSelectionHandles();
+
+ onView(withId(R.id.textview)).perform(doubleClickOnTextAtIndex(text.indexOf('\u0622')));
+
+ onHandleView(com.android.internal.R.id.selection_start_handle)
+ .check(matches(isDisplayed()));
+ onHandleView(com.android.internal.R.id.selection_end_handle)
+ .check(matches(isDisplayed()));
+
+ onView(withId(R.id.textview)).check(hasSelection("\u0621\u0622\u0623"));
+
+ final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
+ onHandleView(com.android.internal.R.id.selection_start_handle)
+ .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('f')));
+ onView(withId(R.id.textview)).check(hasSelection("\u0621\u0622\u0623"));
+
+ onHandleView(com.android.internal.R.id.selection_end_handle)
+ .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('a')));
+ onView(withId(R.id.textview)).check(hasSelection("\u0621\u0622\u0623"));
+
+ onHandleView(com.android.internal.R.id.selection_start_handle)
+ .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('\u0623') + 1,
+ false));
+ onView(withId(R.id.textview)).check(hasSelection("\u0623"));
+
+ onHandleView(com.android.internal.R.id.selection_start_handle)
+ .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('\u0621'),
+ false));
+ onView(withId(R.id.textview)).check(hasSelection("\u0621\u0622\u0623"));
+
+ onHandleView(com.android.internal.R.id.selection_start_handle)
+ .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('a')));
+ onView(withId(R.id.textview)).check(hasSelection("abc \u0621\u0622\u0623"));
+
+ onHandleView(com.android.internal.R.id.selection_end_handle)
+ .perform(dragHandle(textView, Handle.SELECTION_END, text.length()));
+ onView(withId(R.id.textview)).check(hasSelection("abc \u0621\u0622\u0623 def"));
+ }
+
+ @SmallTest
public void testSelectionHandles_multiLine() throws Exception {
final String text = "abcd\n" + "efg\n" + "hijk\n" + "lmn\n" + "opqr";
onView(withId(R.id.textview)).perform(click());
@@ -630,4 +677,26 @@
assertFloatingToolbarContainsItem(
getActivity().getString(com.android.internal.R.string.copy));
}
+
+ @SmallTest
+ public void testTransientState() throws Exception {
+ final String text = "abc def";
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(replaceText(text));
+
+ final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
+ assertFalse(textView.hasTransientState());
+
+ onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('b')));
+ // hasTransientState should return true when user generated selection is active.
+ assertTrue(textView.hasTransientState());
+ onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.indexOf('d')));
+ // hasTransientState should return false as the selection has been cleared.
+ assertFalse(textView.hasTransientState());
+ textView.post(
+ () -> Selection.setSelection((Spannable) textView.getText(), 0, text.length()));
+ getInstrumentation().waitForIdleSync();
+ // hasTransientState should return false when selection is created by API.
+ assertFalse(textView.hasTransientState());
+ }
}
diff --git a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
index 0f7f359..838f4db 100644
--- a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
+++ b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
@@ -17,6 +17,7 @@
package android.widget.espresso;
import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
@@ -24,6 +25,7 @@
import static android.support.test.espresso.matcher.ViewMatchers.isRoot;
import static android.support.test.espresso.matcher.ViewMatchers.withTagValue;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.is;
@@ -34,8 +36,6 @@
import android.support.test.espresso.UiController;
import android.support.test.espresso.ViewAction;
import android.support.test.espresso.ViewInteraction;
-import android.support.test.espresso.action.ViewActions;
-import android.support.test.espresso.matcher.ViewMatchers;
import android.view.View;
import com.android.internal.widget.FloatingToolbar;
@@ -90,7 +90,7 @@
final int id = com.android.internal.R.id.overflow;
onView(allOf(withId(id), isDisplayed()))
.inRoot(withDecorView(hasDescendant(withId(id))))
- .perform(ViewActions.click());
+ .perform(click());
onView(isRoot()).perform(SLEEP);
}
@@ -106,7 +106,7 @@
*/
public static void assertFloatingToolbarContainsItem(String itemLabel) {
try{
- onFloatingToolBar().check(matches(hasDescendant(ViewMatchers.withText(itemLabel))));
+ onFloatingToolBar().check(matches(hasDescendant(withText(itemLabel))));
} catch (AssertionError e) {
try{
toggleOverflow();
@@ -115,7 +115,7 @@
throw e;
}
try{
- onFloatingToolBar().check(matches(hasDescendant(ViewMatchers.withText(itemLabel))));
+ onFloatingToolBar().check(matches(hasDescendant(withText(itemLabel))));
} finally {
toggleOverflow();
}
@@ -138,6 +138,21 @@
}
/**
+ * Click specified item on the floating tool bar.
+ *
+ * @param itemLabel label of the item.
+ */
+ public static void clickFloatingToolbarItem(String itemLabel) {
+ try{
+ onFloatingToolBarItem(withText(itemLabel)).check(matches(isDisplayed()));
+ } catch (AssertionError e) {
+ // Try to find the item in the overflow menu.
+ toggleOverflow();
+ }
+ onFloatingToolBarItem(withText(itemLabel)).perform(click());
+ }
+
+ /**
* ViewAction to sleep to wait floating toolbar's animation.
*/
private static final ViewAction SLEEP = new ViewAction() {
diff --git a/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java b/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java
index b8ea2de..bec4180 100644
--- a/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java
+++ b/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java
@@ -21,7 +21,6 @@
import android.support.test.espresso.UiController;
import android.support.test.espresso.ViewAction;
import android.support.test.espresso.action.CoordinatesProvider;
-import android.support.test.espresso.action.GeneralClickAction;
import android.support.test.espresso.action.MotionEvents;
import android.support.test.espresso.action.MotionEvents.DownResultHolder;
import android.support.test.espresso.action.Press;
@@ -34,7 +33,7 @@
* ViewAction for performing an click on View by a mouse.
*/
public final class MouseClickAction implements ViewAction {
- private final GeneralClickAction mGeneralClickAction;
+ private final ViewClickAction mViewClickAction;
@MouseUiController.MouseButton
private final int mButton;
@@ -100,30 +99,22 @@
*/
public MouseClickAction(Tapper tapper, CoordinatesProvider coordinatesProvider,
@MouseUiController.MouseButton int button) {
- mGeneralClickAction = new GeneralClickAction(tapper, coordinatesProvider, Press.PINPOINT);
+ mViewClickAction = new ViewClickAction(tapper, coordinatesProvider, Press.PINPOINT);
mButton = button;
}
@Override
public Matcher<View> getConstraints() {
- return mGeneralClickAction.getConstraints();
+ return mViewClickAction.getConstraints();
}
@Override
public String getDescription() {
- return mGeneralClickAction.getDescription();
+ return mViewClickAction.getDescription();
}
@Override
public void perform(UiController uiController, View view) {
- mGeneralClickAction.perform(new MouseUiController(uiController, mButton), view);
- long doubleTapTimeout = ViewConfiguration.getDoubleTapTimeout();
- if (0 < doubleTapTimeout) {
- // Wait to avoid false gesture detection. Without this wait, consecutive clicks can be
- // detected as a triple click. e.g. 2 double clicks are detected as a triple click and
- // a single click because espresso isn't aware of triple click detection logic, which
- // is TextView specific gesture.
- uiController.loopMainThreadForAtLeast(doubleTapTimeout);
- }
+ mViewClickAction.perform(new MouseUiController(uiController, mButton), view);
}
}
diff --git a/core/tests/coretests/src/android/widget/espresso/SuggestionsPopupwindowUtils.java b/core/tests/coretests/src/android/widget/espresso/SuggestionsPopupwindowUtils.java
new file mode 100644
index 0000000..b5a96ae
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/espresso/SuggestionsPopupwindowUtils.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.widget.espresso;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
+import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+import org.hamcrest.Matcher;
+
+import android.support.test.espresso.NoMatchingRootException;
+import android.support.test.espresso.NoMatchingViewException;
+import android.support.test.espresso.UiController;
+import android.support.test.espresso.ViewAction;
+import android.support.test.espresso.ViewInteraction;
+import android.support.test.espresso.action.GeneralLocation;
+import android.support.test.espresso.action.Press;
+import android.support.test.espresso.action.Tap;
+import android.view.View;
+
+public final class SuggestionsPopupwindowUtils {
+ private static final int id = com.android.internal.R.id.suggestionWindowContainer;
+
+ private SuggestionsPopupwindowUtils() {};
+
+ public static ViewInteraction onSuggestionsPopup() {
+ return onView(withId(id)).inRoot(withDecorView(hasDescendant(withId(id))));
+ }
+
+ private static ViewInteraction onSuggestionsPopupItem(Matcher<View> matcher) {
+ return onView(matcher).inRoot(withDecorView(hasDescendant(withId(id))));
+ }
+
+ /**
+ * Asserts that the suggestions popup is displayed on screen.
+ *
+ * @throws AssertionError if the assertion fails
+ */
+ public static void assertSuggestionsPopupIsDisplayed() {
+ onSuggestionsPopup().check(matches(isDisplayed()));
+ }
+
+ /**
+ * Asserts that the suggestions popup is not displayed on screen.
+ *
+ * @throws AssertionError if the assertion fails
+ */
+ public static void assertSuggestionsPopupIsNotDisplayed() {
+ try {
+ onSuggestionsPopup().check(matches(isDisplayed()));
+ } catch (NoMatchingRootException | NoMatchingViewException | AssertionError e) {
+ return;
+ }
+ throw new AssertionError("Suggestions popup is displayed");
+ }
+
+ /**
+ * Asserts that the suggestions popup contains the specified item.
+ *
+ * @param itemLabel label of the item.
+ * @throws AssertionError if the assertion fails
+ */
+ public static void assertSuggestionsPopupContainsItem(String itemLabel) {
+ onSuggestionsPopupItem(withText(itemLabel)).check(matches(isDisplayed()));
+ }
+
+ /**
+ * Click on the specified item in the suggestions popup.
+ *
+ * @param itemLabel label of the item.
+ */
+ public static void clickSuggestionsPopupItem(String itemLabel) {
+ onSuggestionsPopupItem(withText(itemLabel)).perform(new SuggestionItemClickAction());
+ }
+
+ /**
+ * Click action to avoid checking ViewClickAction#getConstraints().
+ * TODO: Use Espresso.onData instead of this.
+ */
+ private static final class SuggestionItemClickAction implements ViewAction {
+ private final ViewClickAction mViewClickAction;
+
+ public SuggestionItemClickAction() {
+ mViewClickAction =
+ new ViewClickAction(Tap.SINGLE, GeneralLocation.VISIBLE_CENTER, Press.FINGER);
+ }
+
+ @Override
+ public Matcher<View> getConstraints() {
+ return isDisplayed();
+ }
+
+ @Override
+ public String getDescription() {
+ return mViewClickAction.getDescription();
+ }
+
+ @Override
+ public void perform(UiController uiController, View view) {
+ mViewClickAction.perform(uiController, view);
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
index 1dd6e17..335d021 100644
--- a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
+++ b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
@@ -21,7 +21,6 @@
import android.support.test.espresso.PerformException;
import android.support.test.espresso.ViewAction;
import android.support.test.espresso.action.CoordinatesProvider;
-import android.support.test.espresso.action.GeneralClickAction;
import android.support.test.espresso.action.Press;
import android.support.test.espresso.action.Tap;
import android.support.test.espresso.util.HumanReadables;
@@ -29,6 +28,7 @@
import android.view.MotionEvent;
import android.view.View;
import android.widget.Editor;
+import android.widget.Editor.HandleView;
import android.widget.TextView;
/**
@@ -50,7 +50,7 @@
*/
public static ViewAction clickOnTextAtIndex(int index) {
return actionWithAssertions(
- new GeneralClickAction(Tap.SINGLE, new TextCoordinates(index), Press.FINGER));
+ new ViewClickAction(Tap.SINGLE, new TextCoordinates(index), Press.FINGER));
}
/**
@@ -96,7 +96,7 @@
*/
public static ViewAction doubleClickOnTextAtIndex(int index) {
return actionWithAssertions(
- new GeneralClickAction(Tap.DOUBLE, new TextCoordinates(index), Press.FINGER));
+ new ViewClickAction(Tap.DOUBLE, new TextCoordinates(index), Press.FINGER));
}
/**
@@ -126,7 +126,7 @@
*/
public static ViewAction longPressOnTextAtIndex(int index) {
return actionWithAssertions(
- new GeneralClickAction(Tap.LONG, new TextCoordinates(index), Press.FINGER));
+ new ViewClickAction(Tap.LONG, new TextCoordinates(index), Press.FINGER));
}
/**
@@ -311,18 +311,87 @@
* @param endIndex The index of the TextView's text to end the drag at
*/
public static ViewAction dragHandle(TextView textView, Handle handleType, int endIndex) {
- final int currentOffset = handleType == Handle.SELECTION_START ?
- textView.getSelectionStart() : textView.getSelectionEnd();
+ return dragHandle(textView, handleType, endIndex, true);
+ }
+
+ /**
+ * Returns an action that tap then drags on the handle from the current position to endIndex on
+ * the TextView.<br>
+ * <br>
+ * View constraints:
+ * <ul>
+ * <li>must be a TextView's drag-handle displayed on screen
+ * <ul>
+ *
+ * @param textView TextView the handle is on
+ * @param handleType Type of the handle
+ * @param endIndex The index of the TextView's text to end the drag at
+ * @param primary whether to use primary direction to get coordinate form index when endIndex is
+ * at a direction boundary.
+ */
+ public static ViewAction dragHandle(TextView textView, Handle handleType, int endIndex,
+ boolean primary) {
return actionWithAssertions(
new DragAction(
DragAction.Drag.TAP,
- new HandleCoordinates(textView, handleType, currentOffset),
- new HandleCoordinates(textView, handleType, endIndex),
+ new CurrentHandleCoordinates(textView),
+ new HandleCoordinates(textView, handleType, endIndex, primary),
Press.FINGER,
Editor.HandleView.class));
}
/**
+ * A provider of the x, y coordinates of the handle dragging point.
+ */
+ private static final class CurrentHandleCoordinates implements CoordinatesProvider {
+ // Must be larger than Editor#LINE_SLOP_MULTIPLIER_FOR_HANDLEVIEWS.
+ private final TextView mTextView;
+ private final String mActionDescription;
+
+
+ public CurrentHandleCoordinates(TextView textView) {
+ mTextView = textView;
+ mActionDescription = "Could not locate handle.";
+ }
+
+ @Override
+ public float[] calculateCoordinates(View view) {
+ try {
+ return locateHandle(view);
+ } catch (StringIndexOutOfBoundsException e) {
+ throw new PerformException.Builder()
+ .withActionDescription(mActionDescription)
+ .withViewDescription(HumanReadables.describe(view))
+ .withCause(e)
+ .build();
+ }
+ }
+
+ private float[] locateHandle(View view) {
+ final Rect bounds = new Rect();
+ view.getBoundsOnScreen(bounds);
+ final Rect visibleDisplayBounds = new Rect();
+ mTextView.getWindowVisibleDisplayFrame(visibleDisplayBounds);
+ visibleDisplayBounds.right -= 1;
+ visibleDisplayBounds.bottom -= 1;
+ if (!visibleDisplayBounds.intersect(bounds)) {
+ throw new PerformException.Builder()
+ .withActionDescription(mActionDescription
+ + " The handle is entirely out of the visible display frame of"
+ + "the TextView's window.")
+ .withViewDescription(HumanReadables.describe(view))
+ .build();
+ }
+ final float dragPointX = Math.max(Math.min(bounds.centerX(),
+ visibleDisplayBounds.right), visibleDisplayBounds.left);
+ final float verticalOffset = bounds.height() * 0.7f;
+ final float dragPointY = Math.max(Math.min(bounds.top + verticalOffset,
+ visibleDisplayBounds.bottom), visibleDisplayBounds.top);
+ return new float[] {dragPointX, dragPointY};
+ }
+ }
+
+ /**
* A provider of the x, y coordinates of the handle that points the specified text index in a
* text view.
*/
@@ -332,14 +401,17 @@
private final TextView mTextView;
private final Handle mHandleType;
private final int mIndex;
+ private final boolean mPrimary;
private final String mActionDescription;
- public HandleCoordinates(TextView textView, Handle handleType, int index) {
+ public HandleCoordinates(TextView textView, Handle handleType, int index, boolean primary) {
mTextView = textView;
mHandleType = handleType;
mIndex = index;
+ mPrimary = primary;
mActionDescription = "Could not locate " + handleType.toString()
- + " handle that points text index: " + index;
+ + " handle that points text index: " + index
+ + " (" + (primary ? "primary" : "secondary" ) + ")";
}
@Override
@@ -356,17 +428,26 @@
}
private float[] locateHandlePointsTextIndex(View view) {
+ if (!(view instanceof HandleView)) {
+ throw new PerformException.Builder()
+ .withActionDescription(mActionDescription + " The view is not a HandleView")
+ .withViewDescription(HumanReadables.describe(view))
+ .build();
+ }
+ final HandleView handleView = (HandleView) view;
final int currentOffset = mHandleType == Handle.SELECTION_START ?
mTextView.getSelectionStart() : mTextView.getSelectionEnd();
final Layout layout = mTextView.getLayout();
+
final int currentLine = layout.getLineForOffset(currentOffset);
final int targetLine = layout.getLineForOffset(mIndex);
-
+ final float currentX = handleView.getHorizontal(layout, currentOffset);
+ final float currentY = layout.getLineTop(currentLine);
final float[] currentCoordinates =
- (new TextCoordinates(currentOffset)).calculateCoordinates(mTextView);
+ TextCoordinates.convertToScreenCoordinates(mTextView, currentX, currentY);
final float[] targetCoordinates =
- (new TextCoordinates(mIndex)).calculateCoordinates(mTextView);
+ (new TextCoordinates(mIndex, mPrimary)).calculateCoordinates(mTextView);
final Rect bounds = new Rect();
view.getBoundsOnScreen(bounds);
final Rect visibleDisplayBounds = new Rect();
@@ -403,17 +484,24 @@
private static final class TextCoordinates implements CoordinatesProvider {
private final int mIndex;
+ private final boolean mPrimary;
private final String mActionDescription;
public TextCoordinates(int index) {
+ this(index, true);
+ }
+
+ public TextCoordinates(int index, boolean primary) {
mIndex = index;
- mActionDescription = "Could not locate text at index: " + mIndex;
+ mPrimary = primary;
+ mActionDescription = "Could not locate text at index: " + mIndex
+ + " (" + (primary ? "primary" : "secondary" ) + ")";
}
@Override
public float[] calculateCoordinates(View view) {
try {
- return locateTextAtIndex((TextView) view, mIndex);
+ return locateTextAtIndex((TextView) view, mIndex, mPrimary);
} catch (ClassCastException e) {
throw new PerformException.Builder()
.withActionDescription(mActionDescription)
@@ -432,19 +520,30 @@
/**
* @throws StringIndexOutOfBoundsException
*/
- private float[] locateTextAtIndex(TextView textView, int index) {
+ private float[] locateTextAtIndex(TextView textView, int index, boolean primary) {
if (index < 0 || index > textView.getText().length()) {
throw new StringIndexOutOfBoundsException(index);
}
- final int[] xy = new int[2];
- textView.getLocationOnScreen(xy);
final Layout layout = textView.getLayout();
final int line = layout.getLineForOffset(index);
- final float x = textView.getTotalPaddingLeft() - textView.getScrollX()
- + layout.getPrimaryHorizontal(index);
- final float y = textView.getTotalPaddingTop() - textView.getScrollY()
- + layout.getLineTop(line);
- return new float[]{x + xy[0], y + xy[1]};
+ return convertToScreenCoordinates(textView,
+ (primary ? layout.getPrimaryHorizontal(index)
+ : layout.getSecondaryHorizontal(index)),
+ layout.getLineTop(line));
+ }
+
+ /**
+ * Convert TextView's local coordinates to on screen coordinates.
+ * @param textView the TextView
+ * @param x local horizontal coordinate
+ * @param y local vertical coordinate
+ * @return
+ */
+ public static float[] convertToScreenCoordinates(TextView textView, float x, float y) {
+ final int[] xy = new int[2];
+ textView.getLocationOnScreen(xy);
+ return new float[]{ x + textView.getTotalPaddingLeft() - textView.getScrollX() + xy[0],
+ y + textView.getTotalPaddingTop() - textView.getScrollY() + xy[1] };
}
}
}
diff --git a/core/tests/coretests/src/android/widget/espresso/ViewClickAction.java b/core/tests/coretests/src/android/widget/espresso/ViewClickAction.java
new file mode 100644
index 0000000..8bce1b0
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/espresso/ViewClickAction.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.widget.espresso;
+
+import org.hamcrest.Matcher;
+
+import android.support.test.espresso.UiController;
+import android.support.test.espresso.ViewAction;
+import android.support.test.espresso.action.CoordinatesProvider;
+import android.support.test.espresso.action.GeneralClickAction;
+import android.support.test.espresso.action.PrecisionDescriber;
+import android.support.test.espresso.action.Tapper;
+import android.view.View;
+import android.view.ViewConfiguration;
+
+public final class ViewClickAction implements ViewAction {
+ private final GeneralClickAction mGeneralClickAction;
+
+ public ViewClickAction(Tapper tapper, CoordinatesProvider coordinatesProvider,
+ PrecisionDescriber precisionDescriber) {
+ mGeneralClickAction = new GeneralClickAction(tapper, coordinatesProvider,
+ precisionDescriber);
+ }
+
+ @Override
+ public Matcher<View> getConstraints() {
+ return mGeneralClickAction.getConstraints();
+ }
+
+ @Override
+ public String getDescription() {
+ return mGeneralClickAction.getDescription();
+ }
+
+ @Override
+ public void perform(UiController uiController, View view) {
+ mGeneralClickAction.perform(uiController, view);
+ long doubleTapTimeout = ViewConfiguration.getDoubleTapTimeout();
+ if (0 < doubleTapTimeout) {
+ // Wait to avoid false gesture detection. Without this wait, consecutive clicks can be
+ // detected as a double click or triple click. e.g. 2 double clicks on TextView are
+ // detected as a triple click and a single click because espresso isn't aware of
+ // TextView specific gestures.
+ uiController.loopMainThreadForAtLeast(doubleTapTimeout);
+ }
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java
index 7519627..2a24881 100644
--- a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java
+++ b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java
@@ -22,7 +22,6 @@
import android.view.ActionMode;
import android.view.ActionMode.Callback;
import android.view.KeyEvent;
-import android.view.KeyboardShortcutGroup;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
diff --git a/core/tests/coretests/src/com/android/internal/util/ProgressReporterTest.java b/core/tests/coretests/src/com/android/internal/util/ProgressReporterTest.java
new file mode 100644
index 0000000..fbf5523
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/util/ProgressReporterTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import junit.framework.TestCase;
+
+public class ProgressReporterTest extends TestCase {
+ private ProgressReporter r;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ r = new ProgressReporter(0, null);
+ }
+
+ private void assertProgress(int expected) {
+ assertEquals(expected, r.getProgress());
+ }
+
+ private void assertRange(int start, int len) {
+ final int[] range = r.getSegmentRange();
+ assertEquals("start", start, range[0]);
+ assertEquals("len", len, range[1]);
+ }
+
+ public void testBasic() throws Exception {
+ assertProgress(0);
+
+ r.setProgress(20);
+ assertProgress(20);
+
+ r.setProgress(-20);
+ assertProgress(0);
+
+ r.setProgress(1024);
+ assertProgress(100);
+ }
+
+ public void testSegment() throws Exception {
+ r.setProgress(20);
+ assertProgress(20);
+
+ final int[] lastRange = r.startSegment(40);
+ {
+ assertProgress(20);
+
+ r.setProgress(50);
+ assertProgress(40);
+ }
+ r.endSegment(lastRange);
+ assertProgress(60);
+
+ r.setProgress(80);
+ assertProgress(80);
+ }
+
+ public void testSegmentOvershoot() throws Exception {
+ r.setProgress(20);
+ assertProgress(20);
+
+ final int[] lastRange = r.startSegment(40);
+ {
+ r.setProgress(-100, 2);
+ assertProgress(20);
+
+ r.setProgress(1, 2);
+ assertProgress(40);
+
+ r.setProgress(100, 2);
+ assertProgress(60);
+ }
+ r.endSegment(lastRange);
+ assertProgress(60);
+ }
+
+ public void testSegmentNested() throws Exception {
+ r.setProgress(20);
+ assertProgress(20);
+ assertRange(0, 100);
+
+ final int[] lastRange = r.startSegment(40);
+ assertRange(20, 40);
+ {
+ r.setProgress(50);
+ assertProgress(40);
+
+ final int[] lastRange2 = r.startSegment(25);
+ assertRange(40, 10);
+ {
+ r.setProgress(0);
+ assertProgress(40);
+
+ r.setProgress(50);
+ assertProgress(45);
+
+ r.setProgress(100);
+ assertProgress(50);
+ }
+ r.endSegment(lastRange2);
+ assertProgress(50);
+ }
+ r.endSegment(lastRange);
+ assertProgress(60);
+ }
+}
diff --git a/core/tests/utiltests/src/com/android/internal/util/FastXmlSerializerTest.java b/core/tests/utiltests/src/com/android/internal/util/FastXmlSerializerTest.java
index 5f36c2d..3cef336 100644
--- a/core/tests/utiltests/src/com/android/internal/util/FastXmlSerializerTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/FastXmlSerializerTest.java
@@ -16,16 +16,32 @@
package com.android.internal.util;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+import android.util.Xml;
+
import junit.framework.TestCase;
+import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;
+import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.nio.charset.StandardCharsets;
/**
* Tests for {@link FastXmlSerializer}
*/
+@SmallTest
public class FastXmlSerializerTest extends TestCase {
+ private static final String TAG = "FastXmlSerializerTest";
+
+ private static final boolean ENABLE_DUMP = false; // DO NOT SUBMIT WITH TRUE.
+
+ private static final String ROOT_TAG = "root";
+ private static final String ATTR = "attr";
+
public void testEmptyText() throws Exception {
final ByteArrayOutputStream stream = new ByteArrayOutputStream();
@@ -44,4 +60,93 @@
assertEquals("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ "<string name=\"meow\"></string>\n", stream.toString());
}
+
+ private boolean checkPreserved(String description, String str) {
+ boolean ok = true;
+ byte[] data;
+ try (final ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+ final XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(baos, StandardCharsets.UTF_16.name());
+ out.startDocument(null, true);
+
+ out.startTag(null, ROOT_TAG);
+ out.attribute(null, ATTR, str);
+ out.text(str);
+ out.endTag(null, ROOT_TAG);
+
+ out.endDocument();
+ baos.flush();
+ data = baos.toByteArray();
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to serialize: " + description, e);
+ return false;
+ }
+
+ if (ENABLE_DUMP) {
+ Log.d(TAG, "Dump:");
+ Log.d(TAG, new String(data));
+ }
+
+ try (final ByteArrayInputStream baos = new ByteArrayInputStream(data)) {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(baos, StandardCharsets.UTF_16.name());
+
+ int type;
+ String tag = null;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ if (type == XmlPullParser.START_TAG) {
+ tag = parser.getName();
+ if (ROOT_TAG.equals(tag)) {
+ String read = parser.getAttributeValue(null, ATTR);
+ if (!str.equals(read)) {
+ Log.e(TAG, "Attribute not preserved: " + description
+ + " input=\"" + str + "\", but read=\"" + read + "\"");
+ ok = false;
+ }
+ }
+ }
+ if (type == XmlPullParser.TEXT && ROOT_TAG.equals(tag)) {
+ String read = parser.getText();
+ if (!str.equals(parser.getText())) {
+ Log.e(TAG, "Text not preserved: " + description
+ + " input=\"" + str + "\", but read=\"" + read + "\"");
+ ok = false;
+ }
+ }
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to parse: " + description, e);
+ return false;
+ }
+ return ok;
+ }
+
+ private boolean check(String description, String str) throws Exception {
+ boolean ok = false;
+ ok |= checkPreserved(description, str);
+ ok |= checkPreserved(description + " wrapped with spaces" ," " + str + " ");
+ return ok;
+ }
+
+ @LargeTest
+ public void testAllCharacters() throws Exception {
+ boolean ok = true;
+ for (int i = 0; i < 0xffff; i++) {
+ if (0xd800 <= i && i <= 0xdfff) {
+ // Surrogate pair characters.
+ continue;
+ }
+ ok &= check("char: " + i, String.valueOf((char) i));
+ }
+ // Dangling surrogate pairs. We can't preserve them.
+ assertFalse(check("+ud800", "\ud800"));
+ assertFalse(check("+udc00", "\udc00"));
+
+ for (int i = 0xd800; i < 0xdc00; i ++) {
+ for (int j = 0xdc00; j < 0xe000; j++) {
+ ok &= check("char: " + i, String.valueOf((char) i) + String.valueOf((char) j));
+ }
+ }
+ assertTrue("Some tests failed. See logcat for details.", ok);
+ }
}
diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk
index fd28f64..f4c5b53 100644
--- a/data/fonts/Android.mk
+++ b/data/fonts/Android.mk
@@ -101,8 +101,13 @@
checkbuild: fontchain_lint
FONTCHAIN_LINTER := frameworks/base/tools/fonts/fontchain_lint.py
+ifeq ($(SMALLER_FONT_FOOTPRINT),true)
+CHECK_EMOJI := false
+else
+CHECK_EMOJI := true
+endif
.PHONY: fontchain_lint
-fontchain_lint: $(FONTCHAIN_LINTER) $(TARGET_OUT)/etc/fonts.xml
+fontchain_lint: $(FONTCHAIN_LINTER) $(TARGET_OUT)/etc/fonts.xml $(PRODUCT_OUT)/system.img
PYTHONPATH=$$PYTHONPATH:external/fonttools/Lib \
- python $(FONTCHAIN_LINTER) $(TARGET_OUT) external/unicode
+ python $(FONTCHAIN_LINTER) $(TARGET_OUT) $(CHECK_EMOJI) external/unicode
diff --git a/docs/docs-redirect-index.html b/docs/docs-redirect-index.html
index dc0bc72..ea5f186 100644
--- a/docs/docs-redirect-index.html
+++ b/docs/docs-redirect-index.html
@@ -1,8 +1,8 @@
-<html>
-<head>
-<meta http-equiv="refresh" content="0;url=framework/index.html">
-</head>
-<body>
-<a href="framework/index.html">click here if you are not redirected</a>
-</body>
-</html>
+<html>
+<head>
+<meta http-equiv="refresh" content="0;url=framework/index.html">
+</head>
+<body>
+<a href="framework/index.html">click here if you are not redirected</a>
+</body>
+</html>
diff --git a/docs/docs-redirect.html b/docs/docs-redirect.html
index 4e0743e..d83564c 100644
--- a/docs/docs-redirect.html
+++ b/docs/docs-redirect.html
@@ -1,8 +1,8 @@
-<html>
-<head>
-<meta http-equiv="refresh" content="0;url=docs/offline.html">
-</head>
-<body>
-<a href="docs/offline.html">click here if you are not redirected</a>
-</body>
-</html>
+<html>
+<head>
+<meta http-equiv="refresh" content="0;url=docs/offline.html">
+</head>
+<body>
+<a href="docs/offline.html">click here if you are not redirected</a>
+</body>
+</html>
diff --git a/docs/docs-samples-redirect.html b/docs/docs-samples-redirect.html
index 4e549e6..abefe6c 100644
--- a/docs/docs-samples-redirect.html
+++ b/docs/docs-samples-redirect.html
@@ -1,8 +1,8 @@
-<html>
-<head>
-<meta http-equiv="refresh" content="0;url=../../samples/">
-</head>
-<body>
-<a href="../../samples/">click here if you are not redirected</a>
-</body>
-</html>
+<html>
+<head>
+<meta http-equiv="refresh" content="0;url=../../samples/">
+</head>
+<body>
+<a href="../../samples/">click here if you are not redirected</a>
+</body>
+</html>
diff --git a/docs/html/guide/topics/renderscript/reference/overview.jd b/docs/html/guide/topics/renderscript/reference/overview.jd
index 5e824ee..017a713 100644
--- a/docs/html/guide/topics/renderscript/reference/overview.jd
+++ b/docs/html/guide/topics/renderscript/reference/overview.jd
@@ -611,7 +611,7 @@
</tr>
</tbody></table>
<h2>Conversion Functions</h2>
-<p> The functions below convert from a numerical vector type to another, of from one color
+<p> The functions below convert from a numerical vector type to another, or from one color
representation to another.
</p>
<table class='jd-sumtable'><tbody>
diff --git a/docs/html/guide/topics/renderscript/reference/rs_convert.jd b/docs/html/guide/topics/renderscript/reference/rs_convert.jd
index d4bf77fa..89fd040 100644
--- a/docs/html/guide/topics/renderscript/reference/rs_convert.jd
+++ b/docs/html/guide/topics/renderscript/reference/rs_convert.jd
@@ -4,7 +4,7 @@
<div class='renderscript'>
<h2>Overview</h2>
-<p> The functions below convert from a numerical vector type to another, of from one color
+<p> The functions below convert from a numerical vector type to another, or from one color
representation to another.
</p>
<h2>Summary</h2>
diff --git a/docs/html/guide/topics/resources/localization.jd b/docs/html/guide/topics/resources/localization.jd
index 0a96a15..e60ca9f 100644
--- a/docs/html/guide/topics/resources/localization.jd
+++ b/docs/html/guide/topics/resources/localization.jd
@@ -1,484 +1,484 @@
-page.title=Localizing with Resources
-parent.title=Application Resources
-page.tags="localizing","localization","resources", "formats", "l10n"
-parent.link=index.html
-@jd:body
-
-<div id="qv-wrapper">
- <div id="qv">
-
-<h2>Quickview</h2>
-
-<ul>
- <li>Use resource sets to create a localized app.</li>
- <li>Android loads the correct resource set for the user's language and locale.</li>
- <li>If localized resources are not available, Android loads your default resources.</li>
-</ul>
-
-<h2>In this document</h2>
-<ol>
- <li><a href="#resource-switching">Overview: Resource-Switching in Android</a></li>
-<li><a href="#using-framework">Using Resources for Localization</a></li>
-<li><a href="#strategies">Localization Tips</a></li>
-<li><a href="#testing">Testing Localized Applications</a></li>
-</ol>
-
-<h2>See also</h2>
- <ol>
- <li><a href="{@docRoot}distribute/tools/localization-checklist.html">Localization Checklist</a></li>
- <li><a href="{@docRoot}guide/topics/resources/providing-resources.html">Providing Resources</a></li>
- <li><a href="{@docRoot}guide/topics/ui/declaring-layout.html">Layouts</a></li>
- <li><a href="{@docRoot}reference/android/app/Activity.html#ActivityLifecycle">Activity Lifecycle</a></li>
-</ol>
-</div>
-</div>
-
-<p>Android will run on many devices in many regions. To reach the most users,
-your application should handle text, audio files, numbers, currency, and
-graphics in ways appropriate to the locales where your application will be used.
-</p>
-
-<p>This document describes best practices for localizing Android
-applications. The principles apply whether you are developing your application
-using ADT with Eclipse, Ant-based tools, or any other IDE. </p>
-
-<p>You should already have a working knowledge of Java and be familiar with
-Android resource loading, the declaration of user interface elements in XML,
-development considerations such as Activity lifecycle, and general principles of
-internationalization and localization. </p>
-
-<p>It is good practice to use the Android resource framework to separate the
-localized aspects of your application as much as possible from the core Java
-functionality:</p>
-
-<ul>
- <li>You can put most or all of the <em>contents</em> of your application's
-user interface into resource files, as described in this document and in <a
-href="{@docRoot}guide/topics/resources/providing-resources.html">Providing Resources</a>.</li>
- <li>The <em>behavior</em> of the user interface, on the other hand, is driven
-by your Java code.
- For example, if users input data that needs to be formatted or sorted
-differently depending on locale, then you would use Java to handle the data
-programmatically. This document does not cover how to localize your Java code.
-</li>
-</ul>
-
-<p>For a short guide to localizing strings in your app, see the training lesson, <a
-href="{@docRoot}training/basics/supporting-devices/languages.html">Supporting Different Languages</a>. </p>
-
-
-<h2 id="resource-switching">Overview: Resource-Switching in Android</h2>
-
-<p>Resources are text strings, layouts, sounds, graphics, and any other static
-data that your Android application needs. An application can include multiple
-sets of resources, each customized for a different device configuration. When a
-user runs the application, Android automatically selects and loads the
-resources that best match the device.</p>
-
-<p>(This document focuses on localization and locale. For a complete description
-of resource-switching and all the types of configurations that you can
-specify — screen orientation, touchscreen type, and so on — see <a
-href="{@docRoot}guide/topics/resources/providing-resources.html#AlternativeResources">Providing
-Alternative Resources</a>.)</p>
-
-<table border="0" cellspacing="0" cellpadding="0">
- <tr border="0">
- <td width="180" style="border: 0pt none ;"><p class="special-note">
- <strong>When you write your application:</strong>
- <br><br>
- You create a set of default resources, plus alternatives to be used in
- different locales.</p></td>
- <td style="border: 0pt none; padding:0">
- <p style="border:0; padding:0"><img src="../../../images/resources/right-arrow.png" alt="right-arrow"
- width="51" height="17"></p></td>
- <td width="180" style="border: 0pt none ;"><p class="special-note">
- <strong>When a user runs your application:</strong>
- <br><br>The Android system selects which resources to load, based on the
- device's locale.</p></td>
- </tr>
-</table>
-
-<p>When you write your application, you create default and alternative resources
-for your application to use. To create resources, you place files within
-specially named subdirectories of the project's <code>res/</code> directory.
-</p>
-
-
-
-<h3 id="defaults-r-important">Why Default Resources Are Important</h3>
-
-<p>Whenever the application runs in a locale for which you have not provided
-locale-specific text, Android will load the default strings from
-<code>res/values/strings.xml</code>. If this default file is absent, or if it
-is missing a string that your application needs, then your application will not run
-and will show an error.
-The example below illustrates what can happen when the default text file is incomplete. </p>
-
-<p><em>Example:</em>
-<p>An application's Java code refers to just two strings, <code>text_a</code> and
- <code>text_b</code>. This application includes a localized resource file
- (<code>res/values-en/strings.xml</code>) that defines <code>text_a</code> and
- <code>text_b</code> in English. This application also includes a default
- resource file (<code>res/values/strings.xml</code>) that includes a
-definition for <code>text_a</code>, but not for <code>text_b</code>:
-<ul>
- <li>This application might compile without a problem. An IDE such as Eclipse
- will not highlight any errors if a resource is missing.</li>
- <li>When this application is launched on a device with locale set to English,
- the application might run without a problem, because
- <code>res/values-en/strings.xml</code> contains both of the needed text
- strings.</li>
- <li>However, <strong>the user will see an error message and a Force Close
- button</strong> when this application is launched on a device set to a
- language other than English. The application will not load.</li>
-</ul>
-
-
-<p>To prevent this situation, make sure that a <code>res/values/strings.xml</code>
- file exists and that it defines every needed string. The situation applies to
- all types of resources, not just strings: You
- need to create a set of default resource files containing all
- the resources that your application calls upon — layouts, drawables,
- animations, etc. For information about testing, see <a href="#test-for-default">
- Testing for Default Resources</a>.</p>
-
-<h2 id="using-framework">Using Resources for Localization</h2>
-
-<h3 id="creating-defaults">How to Create Default Resources</h3>
-
-<p>Put the application's default text in
-a file with the following location and name:</p>
-<p><code> res/values/strings.xml</code> (required directory)</p>
-
-<p>The text strings in <code>res/values/strings.xml</code> should use the
-default language, which is the language that you expect most of your application's users to
-speak. </p>
-
-<p>The default resource set must also include any default drawables and layouts,
- and can include other types of resources such as animations.
-<br>
- <code> res/drawable/</code>(required directory holding at least
- one graphic file, for the application's icon on Google Play)<br>
- <code> res/layout/</code> (required directory holding an XML
- file that defines the default layout)<br>
- <code> res/anim/</code> (required if you have any
- <code>res/anim-<em><qualifiers></em></code> folders)<br>
- <code> res/xml/</code> (required if you have any
- <code>res/xml-<em><qualifiers></em></code> folders)<br>
- <code> res/raw/</code> (required if you have any
- <code>res/raw-<em><qualifiers></em></code> folders)
-</p>
-
-<p class="note"><strong>Tip:</strong> In your code, examine each reference to
- an Android resource. Make sure that a default resource is defined for each
- one. Also make sure that the default string file is complete: A <em>
- localized</em> string file can contain a subset of the strings, but the
- <em>default</em> string file must contain them all.
-</p>
-
-<h3 id="creating-alternatives">How to Create Alternative Resources</h3>
-
-<p>A large part of localizing an application is providing alternative text for
-different languages. In some cases you will also provide alternative graphics,
-sounds, layouts, and other locale-specific resources. </p>
-
-<p>An application can specify many <code>res/<em><qualifiers></em>/</code>
-directories, each with different qualifiers. To create an alternative resource for
-a different locale, you use a qualifier that specifies a language or a
-language-region combination. (The name of a resource directory must conform
-to the naming scheme described in
-<a href="{@docRoot}guide/topics/resources/providing-resources.html#AlternativeResources">Providing
-Alternative Resources</a>,
-or else it will not compile.)</p>
-
-<p><em>Example:</em></p>
-
-<p>Suppose that your application's default language is English. Suppose also
-that you want to localize all the text in your application to French, and most
-of the text in your application (everything except the application's title) to
-Japanese. In this case, you could create three alternative <code>strings.xml</code>
-files, each stored in a locale-specific resource directory:</p>
-
-<ol>
- <li><code>res/values/strings.xml</code><br>
- Contains English text for all the strings that the application uses,
-including text for a string named <code>title</code>.</li>
- <li><code>res/values-fr/strings.xml</code><br>
- Contain French text for all the strings, including <code>title</code>.</li>
- <li><code>res/values-ja/strings.xml</code><br>
- Contain Japanese text for all the strings <em>except</em>
-<code>title</code>.<br>
- <code></code></li>
-</ol>
-
-<p>If your Java code refers to <code>R.string.title</code>, here is what will
-happen at runtime:</p>
-
-<ul>
- <li>If the device is set to any language other than French, Android will load
-<code>title</code> from the <code>res/values/strings.xml</code> file.</li>
- <li>If the device is set to French, Android will load <code>title</code> from
-the <code>res/values-fr/strings.xml</code> file.</li>
-</ul>
-
-<p>Notice that if the device is set to Japanese, Android will look for
-<code>title</code> in the <code>res/values-ja/strings.xml</code> file. But
-because no such string is included in that file, Android will fall back to the
-default, and will load <code>title</code> in English from the
-<code>res/values/strings.xml</code> file. </p>
-
-<h3 id="resource-precedence">Which Resources Take Precedence?</h3>
-
-<p> If multiple resource files match a device's configuration, Android follows a
-set of rules in deciding which file to use. Among the qualifiers that can be
-specified in a resource directory name, <strong>locale almost always takes
-precedence</strong>. </p>
-<p><em>Example:</em></p>
-
-<p>Assume that an application includes a default set of graphics and two other
-sets of graphics, each optimized for a different device setup:</p>
-
-<ul>
- <li><code>res/drawable/</code><br>
- Contains
- default graphics.</li>
- <li><code>res/drawable-small-land-stylus/</code><br>
- Contains graphics optimized for use with a device that expects input from a
- stylus and has a QVGA low-density screen in landscape orientation.</li>
- <li><code>res/drawable-ja/</code> <br>
- Contains graphics optimized for use with Japanese.</li>
-</ul>
-
-<p>If the application runs on a device that is configured to use Japanese,
-Android will load graphics from <code>res/drawable-ja/</code>, even if the
-device happens to be one that expects input from a stylus and has a QVGA
-low-density screen in landscape orientation.</p>
-
-<p class="note"><strong>Exception:</strong> The only qualifiers that take
-precedence over locale in the selection process are MCC and MNC (mobile country
-code and mobile network code). </p>
-
-<p><em>Example:</em></p>
-
-<p>Assume that you have the following situation:</p>
-
-<ul>
- <li>The application code calls for <code>R.string.text_a</code></li>
- <li>Two relevant resource files are available:
- <ul>
- <li><code>res/values-mcc404/strings.xml</code>, which includes
-<code>text_a</code> in the application's default language, in this case
-English.</li>
- <li><code>res/values-hi/strings.xml</code>, which includes
-<code>text_a</code> in Hindi.</li>
- </ul>
- </li>
- <li>The application is running on a device that has the following
-configuration:
- <ul>
- <li>The SIM card is connected to a mobile network in India (MCC 404).</li>
- <li>The language is set to Hindi (<code>hi</code>).</li>
- </ul>
- </li>
-</ul>
-
-<p>Android will load <code>text_a</code> from
-<code>res/values-mcc404/strings.xml</code> (in English), even if the device is
-configured for Hindi. That is because in the resource-selection process, Android
-will prefer an MCC match over a language match. </p>
-
-<p>The selection process is not always as straightforward as these examples
-suggest. Please read <a
-href="{@docRoot}guide/topics/resources/providing-resources.html#BestMatch">How Android Finds
-the Best-matching Resource</a> for a more nuanced description of the
-process. All the qualifiers are described and listed in order of
-precedence in <a
-href="{@docRoot}guide/topics/resources/providing-resources.html#table2">Table 2 of Providing
-Alternative Resources</a>.</p>
-
-<h3 id="referring-to-resources">Referring to Resources in Java</h3>
-
-<p>In your application's Java code, you refer to resources using the syntax
-<code>R.<em>resource_type</em>.<em>resource_name</em></code> or
-<code>android.R.<em>resource_type</em>.<em>resource_name</em></code><em>.</em>
-For more about this, see <a
-href="{@docRoot}guide/topics/resources/accessing-resources.html">Accessing Resources</a>.</p>
-
-<h2 id="checklist">Localization Checklist</h2>
-
-<p>For a complete overview of the process of localizing and distributing an Android application,
-see the <a href="{@docRoot}distribute/tools/localization-checklist.html">Localization
-Checklist</a> document.</p>
-
-<h2 id="strategies">Localization Tips</h2>
-
-<h4 id="failing2">Design your application to work in any locale</h4>
-
-<p>You cannot assume anything about the device on which a user will
-run your application. The device might have hardware that you were not
-anticipating, or it might be set to a locale that you did not plan for or that
-you cannot test. Design your application so that it will function normally or fail gracefully no
-matter what device it runs on.</p>
-
-<p class="note"><strong>Important:</strong> Make sure that your application
-includes a full set of default resources.</p> <p>Make sure to include
-<code>res/drawable/</code> and a <code>res/values/</code> folders (without any
-additional modifiers in the folder names) that contain all the images and text
-that your application will need. </p>
-
-<p>If an application is missing even one default resource, it will not run on a
- device that is set to an unsupported locale. For example, the
- <code>res/values/strings.xml</code> default file might lack one string that
- the application needs: When the application runs in an unsupported locale and
- attempts to load <code>res/values/strings.xml</code>, the user will see an
- error message and a Force Close button. An IDE such as Eclipse will not
- highlight this kind of error, and you will not see the problem when you
- test the application on a device or emulator that is set to a supported locale.</p>
-
-<p>For more information, see <a href="#test-for-default">Testing for Default Resources</a>.</p>
-
-<h4>Design a flexible layout</h4>
-
-<p> If you need to rearrange your layout to fit a certain language (for example
-German with its long words), you can create an alternative layout for that
-language (for example <code>res/layout-de/main.xml</code>). However, doing this
-can make your application harder to maintain. It is better to create a single
-layout that is more flexible.</p>
-
-<p>Another typical situation is a language that requires something different in
-its layout. For example, you might have a contact form that should include two
-name fields when the application runs in Japanese, but three name fields when
-the application runs in some other language. You could handle this in either of
-two ways:</p>
-
-<ul>
- <li>Create one layout with a field that you can programmatically enable or
-disable, based on the language, or</li>
- <li>Have the main layout include another layout that includes the changeable
-field. The second layout can have different configurations for different
-languages.</li>
-</ul>
-
-<h4>Avoid creating more resource files and text strings than you need</h4>
-
-<p>You probably do not need to create a locale-specific
-alternative for every resource in your application. For example, the layout
-defined in the <code>res/layout/main.xml</code> file might work in any locale,
-in which case there would be no need to create any alternative layout files.
-</p>
-
-<p>Also, you might not need to create alternative text for every
-string. For example, assume the following:</p>
-
-<ul>
- <li>Your application's default language is American
-English. Every string that the application uses is defined, using American
-English spellings, in <code>res/values/strings.xml</code>. </li>
-
- <li>For a few important phrases, you want to provide
-British English spelling. You want these alternative strings to be used when your
-application runs on a device in the United Kingdom. </li>
-</ul>
-
-<p>To do this, you could create a small file called
-<code>res/values-en-rGB/strings.xml</code> that includes only the strings that
-should be different when the application runs in the U.K. For all the rest of
-the strings, the application will fall back to the defaults and use what is
-defined in <code>res/values/strings.xml</code>.</p>
-
-<h4>Use the Android Context object for manual locale lookup</h4>
-
-<p>You can look up the locale using the {@link android.content.Context} object
-that Android makes available:</p>
-
-<pre>String locale = context.getResources().getConfiguration().locale.getDisplayName();</pre>
-
-<h2 id="testing">Testing Localized Applications</h2>
-
-<h3 id="device">Testing on a Device</h3>
-<p>Keep in mind that the device you are testing may be significantly different from
- the devices available to consumers in other geographies. The locales available
- on your device may differ from those available on other devices. Also, the
- resolution and density of the device screen may differ, which could affect
- the display of strings and drawables in your UI.</p>
-
-<p>To change the locale or language on a device, use the Settings application.</p>
-
-<h3 id="emulator">Testing on an Emulator</h3>
-
-<p>For details about using the emulator, see See <a
-href="{@docRoot}tools/help/emulator.html">Android Emulator</a>.</p>
-<h4>Creating and using a custom locale</h4>
-
-<p>A "custom" locale is a language/region combination that the Android
-system image does not explicitly support. (For a list of supported locales in
-Android platforms see the Version Notes in the <a
-href="{@docRoot}sdk/index.html">SDK</a> tab). You can test
-how your application will run in a custom locale by creating a custom locale in
-the emulator. There are two ways to do this:</p>
-
-<ul>
- <li>Use the Custom Locale application, which is accessible from the
-Application tab. (After you create a custom locale, switch to it by
-pressing and holding the locale name.)</li>
- <li>Change to a custom locale from the adb shell, as described below.</li>
-</ul>
-
-<p>When you set the emulator to a locale that is not available in the Android
-system image, the system itself will display in its default language. Your
-application, however, should localize properly.</p>
-
-<h4>Changing the emulator locale from the adb shell</h4>
-
-<p>To change the locale in the emulator by using the adb shell. </p>
-
-<ol>
- <li>Pick the locale you want to test and determine its BCP-47 language tag, for
-example, Canadian French would be <code>fr-CA</code>.<br>
- </li>
- <li>Launch an emulator.</li>
- <li>From a command-line shell on the host computer, run the following
-command:<br>
- <code>adb shell</code><br>
- or if you have a device attached, specify that you want the emulator by adding
-the <code>-e</code> option:<br>
- <code>adb -e shell</code></li>
- <li>At the adb shell prompt (<code>#</code>), run this command: <br>
- <code>setprop persist.sys.locale [<em>BCP-47 language tag</em>];stop;sleep 5;start <br>
- </code>Replace bracketed sections with the appropriate codes from Step
-1.</li>
-</ol>
-
-<p>For instance, to test in Canadian French:</p>
-
-<p><code>setprop persist.sys.locale fr-CA;stop;sleep 5;start </code></p>
-
-<p>This will cause the emulator to restart. (It will look like a full reboot,
-but it is not.) Once the Home screen appears again, re-launch your application (for
-example, click the Run icon in Eclipse), and the application will launch with
-the new locale. </p>
-
-<h3 id="test-for-default">Testing for Default Resources</h3>
-<p>Here's how to test whether an application includes every string resource that it needs: </p>
-<ol><li>Set the emulator or device to a language that your application does not
- support. For example, if the application has French strings in
- <code>res/values-fr/</code> but does not have any Spanish strings in
- <code>res/values-es/</code>, then set the emulator's locale to Spanish.
- (You can use the Custom Locale application to set the emulator to an
- unsupported locale.)</li>
- <li>Run the application.</li>
-<li>If the application shows an error message and a Force Close button, it might
- be looking for a string that is not available. Make sure that your
- <code>res/values/strings.xml</code> file includes a definition for
- every string that the application uses.</li>
-</ol>
-</p>
-
-<p>If the test is successful, repeat it for other types of
- configurations. For example, if the application has a layout file called
- <code>res/layout-land/main.xml</code> but does not contain a file called
- <code>res/layout-port/main.xml</code>, then set the emulator or device to
- portrait orientation and see if the application will run.
-
-
-
+page.title=Localizing with Resources
+parent.title=Application Resources
+page.tags="localizing","localization","resources", "formats", "l10n"
+parent.link=index.html
+@jd:body
+
+<div id="qv-wrapper">
+ <div id="qv">
+
+<h2>Quickview</h2>
+
+<ul>
+ <li>Use resource sets to create a localized app.</li>
+ <li>Android loads the correct resource set for the user's language and locale.</li>
+ <li>If localized resources are not available, Android loads your default resources.</li>
+</ul>
+
+<h2>In this document</h2>
+<ol>
+ <li><a href="#resource-switching">Overview: Resource-Switching in Android</a></li>
+<li><a href="#using-framework">Using Resources for Localization</a></li>
+<li><a href="#strategies">Localization Tips</a></li>
+<li><a href="#testing">Testing Localized Applications</a></li>
+</ol>
+
+<h2>See also</h2>
+ <ol>
+ <li><a href="{@docRoot}distribute/tools/localization-checklist.html">Localization Checklist</a></li>
+ <li><a href="{@docRoot}guide/topics/resources/providing-resources.html">Providing Resources</a></li>
+ <li><a href="{@docRoot}guide/topics/ui/declaring-layout.html">Layouts</a></li>
+ <li><a href="{@docRoot}reference/android/app/Activity.html#ActivityLifecycle">Activity Lifecycle</a></li>
+</ol>
+</div>
+</div>
+
+<p>Android will run on many devices in many regions. To reach the most users,
+your application should handle text, audio files, numbers, currency, and
+graphics in ways appropriate to the locales where your application will be used.
+</p>
+
+<p>This document describes best practices for localizing Android
+applications. The principles apply whether you are developing your application
+using ADT with Eclipse, Ant-based tools, or any other IDE. </p>
+
+<p>You should already have a working knowledge of Java and be familiar with
+Android resource loading, the declaration of user interface elements in XML,
+development considerations such as Activity lifecycle, and general principles of
+internationalization and localization. </p>
+
+<p>It is good practice to use the Android resource framework to separate the
+localized aspects of your application as much as possible from the core Java
+functionality:</p>
+
+<ul>
+ <li>You can put most or all of the <em>contents</em> of your application's
+user interface into resource files, as described in this document and in <a
+href="{@docRoot}guide/topics/resources/providing-resources.html">Providing Resources</a>.</li>
+ <li>The <em>behavior</em> of the user interface, on the other hand, is driven
+by your Java code.
+ For example, if users input data that needs to be formatted or sorted
+differently depending on locale, then you would use Java to handle the data
+programmatically. This document does not cover how to localize your Java code.
+</li>
+</ul>
+
+<p>For a short guide to localizing strings in your app, see the training lesson, <a
+href="{@docRoot}training/basics/supporting-devices/languages.html">Supporting Different Languages</a>. </p>
+
+
+<h2 id="resource-switching">Overview: Resource-Switching in Android</h2>
+
+<p>Resources are text strings, layouts, sounds, graphics, and any other static
+data that your Android application needs. An application can include multiple
+sets of resources, each customized for a different device configuration. When a
+user runs the application, Android automatically selects and loads the
+resources that best match the device.</p>
+
+<p>(This document focuses on localization and locale. For a complete description
+of resource-switching and all the types of configurations that you can
+specify — screen orientation, touchscreen type, and so on — see <a
+href="{@docRoot}guide/topics/resources/providing-resources.html#AlternativeResources">Providing
+Alternative Resources</a>.)</p>
+
+<table border="0" cellspacing="0" cellpadding="0">
+ <tr border="0">
+ <td width="180" style="border: 0pt none ;"><p class="special-note">
+ <strong>When you write your application:</strong>
+ <br><br>
+ You create a set of default resources, plus alternatives to be used in
+ different locales.</p></td>
+ <td style="border: 0pt none; padding:0">
+ <p style="border:0; padding:0"><img src="../../../images/resources/right-arrow.png" alt="right-arrow"
+ width="51" height="17"></p></td>
+ <td width="180" style="border: 0pt none ;"><p class="special-note">
+ <strong>When a user runs your application:</strong>
+ <br><br>The Android system selects which resources to load, based on the
+ device's locale.</p></td>
+ </tr>
+</table>
+
+<p>When you write your application, you create default and alternative resources
+for your application to use. To create resources, you place files within
+specially named subdirectories of the project's <code>res/</code> directory.
+</p>
+
+
+
+<h3 id="defaults-r-important">Why Default Resources Are Important</h3>
+
+<p>Whenever the application runs in a locale for which you have not provided
+locale-specific text, Android will load the default strings from
+<code>res/values/strings.xml</code>. If this default file is absent, or if it
+is missing a string that your application needs, then your application will not run
+and will show an error.
+The example below illustrates what can happen when the default text file is incomplete. </p>
+
+<p><em>Example:</em>
+<p>An application's Java code refers to just two strings, <code>text_a</code> and
+ <code>text_b</code>. This application includes a localized resource file
+ (<code>res/values-en/strings.xml</code>) that defines <code>text_a</code> and
+ <code>text_b</code> in English. This application also includes a default
+ resource file (<code>res/values/strings.xml</code>) that includes a
+definition for <code>text_a</code>, but not for <code>text_b</code>:
+<ul>
+ <li>This application might compile without a problem. An IDE such as Eclipse
+ will not highlight any errors if a resource is missing.</li>
+ <li>When this application is launched on a device with locale set to English,
+ the application might run without a problem, because
+ <code>res/values-en/strings.xml</code> contains both of the needed text
+ strings.</li>
+ <li>However, <strong>the user will see an error message and a Force Close
+ button</strong> when this application is launched on a device set to a
+ language other than English. The application will not load.</li>
+</ul>
+
+
+<p>To prevent this situation, make sure that a <code>res/values/strings.xml</code>
+ file exists and that it defines every needed string. The situation applies to
+ all types of resources, not just strings: You
+ need to create a set of default resource files containing all
+ the resources that your application calls upon — layouts, drawables,
+ animations, etc. For information about testing, see <a href="#test-for-default">
+ Testing for Default Resources</a>.</p>
+
+<h2 id="using-framework">Using Resources for Localization</h2>
+
+<h3 id="creating-defaults">How to Create Default Resources</h3>
+
+<p>Put the application's default text in
+a file with the following location and name:</p>
+<p><code> res/values/strings.xml</code> (required directory)</p>
+
+<p>The text strings in <code>res/values/strings.xml</code> should use the
+default language, which is the language that you expect most of your application's users to
+speak. </p>
+
+<p>The default resource set must also include any default drawables and layouts,
+ and can include other types of resources such as animations.
+<br>
+ <code> res/drawable/</code>(required directory holding at least
+ one graphic file, for the application's icon on Google Play)<br>
+ <code> res/layout/</code> (required directory holding an XML
+ file that defines the default layout)<br>
+ <code> res/anim/</code> (required if you have any
+ <code>res/anim-<em><qualifiers></em></code> folders)<br>
+ <code> res/xml/</code> (required if you have any
+ <code>res/xml-<em><qualifiers></em></code> folders)<br>
+ <code> res/raw/</code> (required if you have any
+ <code>res/raw-<em><qualifiers></em></code> folders)
+</p>
+
+<p class="note"><strong>Tip:</strong> In your code, examine each reference to
+ an Android resource. Make sure that a default resource is defined for each
+ one. Also make sure that the default string file is complete: A <em>
+ localized</em> string file can contain a subset of the strings, but the
+ <em>default</em> string file must contain them all.
+</p>
+
+<h3 id="creating-alternatives">How to Create Alternative Resources</h3>
+
+<p>A large part of localizing an application is providing alternative text for
+different languages. In some cases you will also provide alternative graphics,
+sounds, layouts, and other locale-specific resources. </p>
+
+<p>An application can specify many <code>res/<em><qualifiers></em>/</code>
+directories, each with different qualifiers. To create an alternative resource for
+a different locale, you use a qualifier that specifies a language or a
+language-region combination. (The name of a resource directory must conform
+to the naming scheme described in
+<a href="{@docRoot}guide/topics/resources/providing-resources.html#AlternativeResources">Providing
+Alternative Resources</a>,
+or else it will not compile.)</p>
+
+<p><em>Example:</em></p>
+
+<p>Suppose that your application's default language is English. Suppose also
+that you want to localize all the text in your application to French, and most
+of the text in your application (everything except the application's title) to
+Japanese. In this case, you could create three alternative <code>strings.xml</code>
+files, each stored in a locale-specific resource directory:</p>
+
+<ol>
+ <li><code>res/values/strings.xml</code><br>
+ Contains English text for all the strings that the application uses,
+including text for a string named <code>title</code>.</li>
+ <li><code>res/values-fr/strings.xml</code><br>
+ Contain French text for all the strings, including <code>title</code>.</li>
+ <li><code>res/values-ja/strings.xml</code><br>
+ Contain Japanese text for all the strings <em>except</em>
+<code>title</code>.<br>
+ <code></code></li>
+</ol>
+
+<p>If your Java code refers to <code>R.string.title</code>, here is what will
+happen at runtime:</p>
+
+<ul>
+ <li>If the device is set to any language other than French, Android will load
+<code>title</code> from the <code>res/values/strings.xml</code> file.</li>
+ <li>If the device is set to French, Android will load <code>title</code> from
+the <code>res/values-fr/strings.xml</code> file.</li>
+</ul>
+
+<p>Notice that if the device is set to Japanese, Android will look for
+<code>title</code> in the <code>res/values-ja/strings.xml</code> file. But
+because no such string is included in that file, Android will fall back to the
+default, and will load <code>title</code> in English from the
+<code>res/values/strings.xml</code> file. </p>
+
+<h3 id="resource-precedence">Which Resources Take Precedence?</h3>
+
+<p> If multiple resource files match a device's configuration, Android follows a
+set of rules in deciding which file to use. Among the qualifiers that can be
+specified in a resource directory name, <strong>locale almost always takes
+precedence</strong>. </p>
+<p><em>Example:</em></p>
+
+<p>Assume that an application includes a default set of graphics and two other
+sets of graphics, each optimized for a different device setup:</p>
+
+<ul>
+ <li><code>res/drawable/</code><br>
+ Contains
+ default graphics.</li>
+ <li><code>res/drawable-small-land-stylus/</code><br>
+ Contains graphics optimized for use with a device that expects input from a
+ stylus and has a QVGA low-density screen in landscape orientation.</li>
+ <li><code>res/drawable-ja/</code> <br>
+ Contains graphics optimized for use with Japanese.</li>
+</ul>
+
+<p>If the application runs on a device that is configured to use Japanese,
+Android will load graphics from <code>res/drawable-ja/</code>, even if the
+device happens to be one that expects input from a stylus and has a QVGA
+low-density screen in landscape orientation.</p>
+
+<p class="note"><strong>Exception:</strong> The only qualifiers that take
+precedence over locale in the selection process are MCC and MNC (mobile country
+code and mobile network code). </p>
+
+<p><em>Example:</em></p>
+
+<p>Assume that you have the following situation:</p>
+
+<ul>
+ <li>The application code calls for <code>R.string.text_a</code></li>
+ <li>Two relevant resource files are available:
+ <ul>
+ <li><code>res/values-mcc404/strings.xml</code>, which includes
+<code>text_a</code> in the application's default language, in this case
+English.</li>
+ <li><code>res/values-hi/strings.xml</code>, which includes
+<code>text_a</code> in Hindi.</li>
+ </ul>
+ </li>
+ <li>The application is running on a device that has the following
+configuration:
+ <ul>
+ <li>The SIM card is connected to a mobile network in India (MCC 404).</li>
+ <li>The language is set to Hindi (<code>hi</code>).</li>
+ </ul>
+ </li>
+</ul>
+
+<p>Android will load <code>text_a</code> from
+<code>res/values-mcc404/strings.xml</code> (in English), even if the device is
+configured for Hindi. That is because in the resource-selection process, Android
+will prefer an MCC match over a language match. </p>
+
+<p>The selection process is not always as straightforward as these examples
+suggest. Please read <a
+href="{@docRoot}guide/topics/resources/providing-resources.html#BestMatch">How Android Finds
+the Best-matching Resource</a> for a more nuanced description of the
+process. All the qualifiers are described and listed in order of
+precedence in <a
+href="{@docRoot}guide/topics/resources/providing-resources.html#table2">Table 2 of Providing
+Alternative Resources</a>.</p>
+
+<h3 id="referring-to-resources">Referring to Resources in Java</h3>
+
+<p>In your application's Java code, you refer to resources using the syntax
+<code>R.<em>resource_type</em>.<em>resource_name</em></code> or
+<code>android.R.<em>resource_type</em>.<em>resource_name</em></code><em>.</em>
+For more about this, see <a
+href="{@docRoot}guide/topics/resources/accessing-resources.html">Accessing Resources</a>.</p>
+
+<h2 id="checklist">Localization Checklist</h2>
+
+<p>For a complete overview of the process of localizing and distributing an Android application,
+see the <a href="{@docRoot}distribute/tools/localization-checklist.html">Localization
+Checklist</a> document.</p>
+
+<h2 id="strategies">Localization Tips</h2>
+
+<h4 id="failing2">Design your application to work in any locale</h4>
+
+<p>You cannot assume anything about the device on which a user will
+run your application. The device might have hardware that you were not
+anticipating, or it might be set to a locale that you did not plan for or that
+you cannot test. Design your application so that it will function normally or fail gracefully no
+matter what device it runs on.</p>
+
+<p class="note"><strong>Important:</strong> Make sure that your application
+includes a full set of default resources.</p> <p>Make sure to include
+<code>res/drawable/</code> and a <code>res/values/</code> folders (without any
+additional modifiers in the folder names) that contain all the images and text
+that your application will need. </p>
+
+<p>If an application is missing even one default resource, it will not run on a
+ device that is set to an unsupported locale. For example, the
+ <code>res/values/strings.xml</code> default file might lack one string that
+ the application needs: When the application runs in an unsupported locale and
+ attempts to load <code>res/values/strings.xml</code>, the user will see an
+ error message and a Force Close button. An IDE such as Eclipse will not
+ highlight this kind of error, and you will not see the problem when you
+ test the application on a device or emulator that is set to a supported locale.</p>
+
+<p>For more information, see <a href="#test-for-default">Testing for Default Resources</a>.</p>
+
+<h4>Design a flexible layout</h4>
+
+<p> If you need to rearrange your layout to fit a certain language (for example
+German with its long words), you can create an alternative layout for that
+language (for example <code>res/layout-de/main.xml</code>). However, doing this
+can make your application harder to maintain. It is better to create a single
+layout that is more flexible.</p>
+
+<p>Another typical situation is a language that requires something different in
+its layout. For example, you might have a contact form that should include two
+name fields when the application runs in Japanese, but three name fields when
+the application runs in some other language. You could handle this in either of
+two ways:</p>
+
+<ul>
+ <li>Create one layout with a field that you can programmatically enable or
+disable, based on the language, or</li>
+ <li>Have the main layout include another layout that includes the changeable
+field. The second layout can have different configurations for different
+languages.</li>
+</ul>
+
+<h4>Avoid creating more resource files and text strings than you need</h4>
+
+<p>You probably do not need to create a locale-specific
+alternative for every resource in your application. For example, the layout
+defined in the <code>res/layout/main.xml</code> file might work in any locale,
+in which case there would be no need to create any alternative layout files.
+</p>
+
+<p>Also, you might not need to create alternative text for every
+string. For example, assume the following:</p>
+
+<ul>
+ <li>Your application's default language is American
+English. Every string that the application uses is defined, using American
+English spellings, in <code>res/values/strings.xml</code>. </li>
+
+ <li>For a few important phrases, you want to provide
+British English spelling. You want these alternative strings to be used when your
+application runs on a device in the United Kingdom. </li>
+</ul>
+
+<p>To do this, you could create a small file called
+<code>res/values-en-rGB/strings.xml</code> that includes only the strings that
+should be different when the application runs in the U.K. For all the rest of
+the strings, the application will fall back to the defaults and use what is
+defined in <code>res/values/strings.xml</code>.</p>
+
+<h4>Use the Android Context object for manual locale lookup</h4>
+
+<p>You can look up the locale using the {@link android.content.Context} object
+that Android makes available:</p>
+
+<pre>String locale = context.getResources().getConfiguration().locale.getDisplayName();</pre>
+
+<h2 id="testing">Testing Localized Applications</h2>
+
+<h3 id="device">Testing on a Device</h3>
+<p>Keep in mind that the device you are testing may be significantly different from
+ the devices available to consumers in other geographies. The locales available
+ on your device may differ from those available on other devices. Also, the
+ resolution and density of the device screen may differ, which could affect
+ the display of strings and drawables in your UI.</p>
+
+<p>To change the locale or language on a device, use the Settings application.</p>
+
+<h3 id="emulator">Testing on an Emulator</h3>
+
+<p>For details about using the emulator, see See <a
+href="{@docRoot}tools/help/emulator.html">Android Emulator</a>.</p>
+<h4>Creating and using a custom locale</h4>
+
+<p>A "custom" locale is a language/region combination that the Android
+system image does not explicitly support. (For a list of supported locales in
+Android platforms see the Version Notes in the <a
+href="{@docRoot}sdk/index.html">SDK</a> tab). You can test
+how your application will run in a custom locale by creating a custom locale in
+the emulator. There are two ways to do this:</p>
+
+<ul>
+ <li>Use the Custom Locale application, which is accessible from the
+Application tab. (After you create a custom locale, switch to it by
+pressing and holding the locale name.)</li>
+ <li>Change to a custom locale from the adb shell, as described below.</li>
+</ul>
+
+<p>When you set the emulator to a locale that is not available in the Android
+system image, the system itself will display in its default language. Your
+application, however, should localize properly.</p>
+
+<h4>Changing the emulator locale from the adb shell</h4>
+
+<p>To change the locale in the emulator by using the adb shell. </p>
+
+<ol>
+ <li>Pick the locale you want to test and determine its BCP-47 language tag, for
+example, Canadian French would be <code>fr-CA</code>.<br>
+ </li>
+ <li>Launch an emulator.</li>
+ <li>From a command-line shell on the host computer, run the following
+command:<br>
+ <code>adb shell</code><br>
+ or if you have a device attached, specify that you want the emulator by adding
+the <code>-e</code> option:<br>
+ <code>adb -e shell</code></li>
+ <li>At the adb shell prompt (<code>#</code>), run this command: <br>
+ <code>setprop persist.sys.locale [<em>BCP-47 language tag</em>];stop;sleep 5;start <br>
+ </code>Replace bracketed sections with the appropriate codes from Step
+1.</li>
+</ol>
+
+<p>For instance, to test in Canadian French:</p>
+
+<p><code>setprop persist.sys.locale fr-CA;stop;sleep 5;start </code></p>
+
+<p>This will cause the emulator to restart. (It will look like a full reboot,
+but it is not.) Once the Home screen appears again, re-launch your application (for
+example, click the Run icon in Eclipse), and the application will launch with
+the new locale. </p>
+
+<h3 id="test-for-default">Testing for Default Resources</h3>
+<p>Here's how to test whether an application includes every string resource that it needs: </p>
+<ol><li>Set the emulator or device to a language that your application does not
+ support. For example, if the application has French strings in
+ <code>res/values-fr/</code> but does not have any Spanish strings in
+ <code>res/values-es/</code>, then set the emulator's locale to Spanish.
+ (You can use the Custom Locale application to set the emulator to an
+ unsupported locale.)</li>
+ <li>Run the application.</li>
+<li>If the application shows an error message and a Force Close button, it might
+ be looking for a string that is not available. Make sure that your
+ <code>res/values/strings.xml</code> file includes a definition for
+ every string that the application uses.</li>
+</ol>
+</p>
+
+<p>If the test is successful, repeat it for other types of
+ configurations. For example, if the application has a layout file called
+ <code>res/layout-land/main.xml</code> but does not contain a file called
+ <code>res/layout-port/main.xml</code>, then set the emulator or device to
+ portrait orientation and see if the application will run.
+
+
+
diff --git a/docs/overview-ext.html b/docs/overview-ext.html
index 5720245..f80d629 100644
--- a/docs/overview-ext.html
+++ b/docs/overview-ext.html
@@ -1,8 +1,8 @@
-<body>
-Some random libraries that we've imported:
-<ul>
- <li>GData</li>
- <li>Apache commons HTTPClient</li>
- <li>A sax parser</li>
-</ul>
-</body>
+<body>
+Some random libraries that we've imported:
+<ul>
+ <li>GData</li>
+ <li>Apache commons HTTPClient</li>
+ <li>A sax parser</li>
+</ul>
+</body>
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index 8f7c6a62..7871aa8 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -179,10 +179,10 @@
int tag = 0;
String tagStr = parser.getAttributeValue(null, "tag");
if (tagStr != null && TAG_PATTERN.matcher(tagStr).matches()) {
- tag = tagStr.charAt(0) << 24 +
- tagStr.charAt(1) << 16 +
- tagStr.charAt(2) << 8 +
- tagStr.charAt(3);
+ tag = (tagStr.charAt(0) << 24) +
+ (tagStr.charAt(1) << 16) +
+ (tagStr.charAt(2) << 8) +
+ (tagStr.charAt(3) );
} else {
throw new XmlPullParserException("Invalid tag attribute value.", parser, null);
}
diff --git a/graphics/java/android/graphics/Outline.java b/graphics/java/android/graphics/Outline.java
index 3973f2f..aa81b91 100644
--- a/graphics/java/android/graphics/Outline.java
+++ b/graphics/java/android/graphics/Outline.java
@@ -37,22 +37,26 @@
public final class Outline {
private static final float RADIUS_UNDEFINED = Float.NEGATIVE_INFINITY;
- private static final int MODE_EMPTY = 0;
- private static final int MODE_RECT = 1;
- private static final int MODE_CONVEX_PATH = 2;
+ /** @hide */
+ public static final int MODE_EMPTY = 0;
+ /** @hide */
+ public static final int MODE_ROUND_RECT = 1;
+ /** @hide */
+ public static final int MODE_CONVEX_PATH = 2;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = false,
value = {
MODE_EMPTY,
- MODE_RECT,
+ MODE_ROUND_RECT,
MODE_CONVEX_PATH,
})
public @interface Mode {}
+ /** @hide */
@Mode
- private int mMode = MODE_EMPTY;
+ public int mMode = MODE_EMPTY;
/** @hide */
public final Path mPath = new Path();
@@ -176,7 +180,7 @@
return;
}
- mMode = MODE_RECT;
+ mMode = MODE_ROUND_RECT;
mRect.set(left, top, right, bottom);
mRadius = radius;
mPath.rewind();
@@ -199,7 +203,7 @@
* bounds, or {@code false} if no outline bounds are set
*/
public boolean getRect(@NonNull Rect outRect) {
- if (mMode != MODE_RECT) {
+ if (mMode != MODE_ROUND_RECT) {
return false;
}
outRect.set(mRect);
@@ -270,7 +274,7 @@
* Offsets the Outline by (dx,dy)
*/
public void offset(int dx, int dy) {
- if (mMode == MODE_RECT) {
+ if (mMode == MODE_ROUND_RECT) {
mRect.offset(dx, dy);
} else if (mMode == MODE_CONVEX_PATH) {
mPath.offset(dx, dy);
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 291fdc4..0dae796 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -1494,9 +1494,14 @@
}
/**
- * Get font feature settings. Default is null.
+ * Returns the font feature settings. The format is the same as the CSS
+ * font-feature-settings attribute:
+ * <a href="http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings">
+ * http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings</a>
*
- * @return the paint's currently set font feature settings.
+ * @return the paint's currently set font feature settings. Default is null.
+ *
+ * @see #setFontFeatureSettings(String)
*/
public String getFontFeatureSettings() {
return mFontFeatureSettings;
@@ -1506,7 +1511,10 @@
* Set font feature settings.
*
* The format is the same as the CSS font-feature-settings attribute:
- * http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings
+ * <a href="http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings">
+ * http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings</a>
+ *
+ * @see #getFontFeatureSettings()
*
* @param settings the font feature settings string to use, may be null.
*/
diff --git a/graphics/java/android/graphics/PathMeasure.java b/graphics/java/android/graphics/PathMeasure.java
index 0416159..2848949 100644
--- a/graphics/java/android/graphics/PathMeasure.java
+++ b/graphics/java/android/graphics/PathMeasure.java
@@ -112,7 +112,7 @@
* Given a start and stop distance, return in dst the intervening
* segment(s). If the segment is zero-length, return false, else return
* true. startD and stopD are pinned to legal values (0..getLength()).
- * If startD <= stopD then return false (and leave dst untouched).
+ * If startD >= stopD then return false (and leave dst untouched).
* Begin the segment with a moveTo if startWithMoveTo is true.
*
* <p>On {@link android.os.Build.VERSION_CODES#KITKAT} and earlier
@@ -121,6 +121,19 @@
* such as <code>dst.rLineTo(0, 0)</code>.</p>
*/
public boolean getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo) {
+ // Skia used to enforce this as part of it's API, but has since relaxed that restriction
+ // so to maintain consistency in our API we enforce the preconditions here.
+ float length = getLength();
+ if (startD < 0) {
+ startD = 0;
+ }
+ if (stopD > length) {
+ stopD = length;
+ }
+ if (startD >= stopD) {
+ return false;
+ }
+
dst.isSimplePath = false;
return native_getSegment(native_instance, startD, stopD, dst.ni(), startWithMoveTo);
}
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 46a0f43..35385eb 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -1416,6 +1416,9 @@
Log.d(LOGTAG, "on finished called from native");
}
mStarted = false;
+ // Invalidate in the end of the animation to make sure the data in
+ // RT thread is synced back to UI thread.
+ invalidateOwningView();
if (mListener != null) {
mListener.onAnimationEnd(null);
}
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 3915984..4f600b4 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -1198,6 +1198,8 @@
/**
* Inflate this Drawable from an XML resource optionally styled by a theme.
+ * This can't be called more than once for each Drawable. Note that framework may have called
+ * this once to create the Drawable instance from XML resource.
*
* @param r Resources used to resolve attribute values
* @param parser XML parser from which to inflate this Drawable
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index 51221b4..2b950d3 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -243,13 +243,15 @@
}
/**
- * Invokes {@link #loadDrawable(Context)} on a background thread
- * and then runs <code>andThen</code> on the UI thread when finished.
+ * Invokes {@link #loadDrawable(Context)} on a background thread and notifies the <code>
+ * {@link OnDrawableLoadedListener#onDrawableLoaded listener} </code> on the {@code handler}
+ * when finished.
*
* @param context {@link Context Context} in which to load the drawable; see
* {@link #loadDrawable(Context)}
- * @param listener a callback to run on the provided
- * @param handler {@link Handler} on which to run <code>andThen</code>.
+ * @param listener to be {@link OnDrawableLoadedListener#onDrawableLoaded notified} when
+ * {@link #loadDrawable(Context)} finished
+ * @param handler {@link Handler} on which to notify the {@code listener}
*/
public void loadDrawableAsync(Context context, final OnDrawableLoadedListener listener,
Handler handler) {
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index cfcb4e0..deea972 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -27,12 +27,13 @@
// APIs used by KeyChain
String requestPrivateKey(String alias);
byte[] getCertificate(String alias);
+ byte[] getCaCertificates(String alias);
// APIs used by CertInstaller
void installCaCertificate(in byte[] caCertificate);
// APIs used by DevicePolicyManager
- boolean installKeyPair(in byte[] privateKey, in byte[] userCert, String alias);
+ boolean installKeyPair(in byte[] privateKey, in byte[] userCert, in byte[] certChain, String alias);
boolean removeKeyPair(String alias);
// APIs used by Settings
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index 0886487..cce58c2 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -42,6 +42,8 @@
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.BlockingQueue;
@@ -389,7 +391,12 @@
/**
* Returns the {@code X509Certificate} chain for the requested
- * alias, or null if no there is no result.
+ * alias, or null if there is no result.
+ * <p>
+ * <strong>Note:</strong> If a certificate chain was explicitly specified when the alias was
+ * installed, this method will return that chain. If only the client certificate was specified
+ * at the installation time, this method will try to build a certificate chain using all
+ * available trust anchors (preinstalled and user-added).
*
* <p> This method may block while waiting for a connection to another process, and must never
* be called from the main thread.
@@ -413,11 +420,31 @@
if (certificateBytes == null) {
return null;
}
-
- TrustedCertificateStore store = new TrustedCertificateStore();
- List<X509Certificate> chain = store
- .getCertificateChain(toCertificate(certificateBytes));
- return chain.toArray(new X509Certificate[chain.size()]);
+ X509Certificate leafCert = toCertificate(certificateBytes);
+ final byte[] certChainBytes = keyChainService.getCaCertificates(alias);
+ // If the keypair is installed with a certificate chain by either
+ // DevicePolicyManager.installKeyPair or CertInstaller, return that chain.
+ if (certChainBytes != null && certChainBytes.length != 0) {
+ Collection<X509Certificate> chain = toCertificates(certChainBytes);
+ ArrayList<X509Certificate> fullChain = new ArrayList<>(chain.size() + 1);
+ fullChain.add(leafCert);
+ fullChain.addAll(chain);
+ return fullChain.toArray(new X509Certificate[fullChain.size()]);
+ } else {
+ // If there isn't a certificate chain, either due to a pre-existing keypair
+ // installed before N, or no chain is explicitly installed under the new logic,
+ // fall back to old behavior of constructing the chain from trusted credentials.
+ //
+ // This logic exists to maintain old behaviour for already installed keypair, at
+ // the cost of potentially returning extra certificate chain for new clients who
+ // explicitly installed only the client certificate without a chain. The latter
+ // case is actually no different from pre-N behaviour of getCertificateChain(),
+ // in that sense this change introduces no regression. Besides the returned chain
+ // is still valid so the consumer of the chain should have no problem verifying it.
+ TrustedCertificateStore store = new TrustedCertificateStore();
+ List<X509Certificate> chain = store.getCertificateChain(leafCert);
+ return chain.toArray(new X509Certificate[chain.size()]);
+ }
} catch (CertificateException e) {
throw new KeyChainException(e);
} catch (RemoteException e) {
@@ -486,6 +513,21 @@
}
}
+ /** @hide */
+ @NonNull
+ public static Collection<X509Certificate> toCertificates(@NonNull byte[] bytes) {
+ if (bytes == null) {
+ throw new IllegalArgumentException("bytes == null");
+ }
+ try {
+ CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+ return (Collection<X509Certificate>) certFactory.generateCertificates(
+ new ByteArrayInputStream(bytes));
+ } catch (CertificateException e) {
+ throw new AssertionError(e);
+ }
+ }
+
/**
* @hide for reuse by CertInstaller and Settings.
* @see KeyChain#bind
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 52fe973..1ccc59a 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -3935,7 +3935,9 @@
if (Res_GETPACKAGE(resID)+1 == 0) {
ALOGW("No package identifier when getting name for resource number 0x%08x", resID);
} else {
+#ifndef STATIC_ANDROIDFW_FOR_TOOLS
ALOGW("No known package when getting name for resource number 0x%08x", resID);
+#endif
}
return false;
}
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 516591b..be816f78 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -252,6 +252,7 @@
tests/unit/LinearAllocatorTests.cpp \
tests/unit/MatrixTests.cpp \
tests/unit/OffscreenBufferPoolTests.cpp \
+ tests/unit/RenderNodeTests.cpp \
tests/unit/SkiaBehaviorTests.cpp \
tests/unit/StringUtilsTests.cpp \
tests/unit/TextDropShadowCacheTests.cpp \
@@ -266,7 +267,8 @@
tests/unit/FrameBuilderTests.cpp \
tests/unit/LeakCheckTests.cpp \
tests/unit/OpDumperTests.cpp \
- tests/unit/RecordingCanvasTests.cpp
+ tests/unit/RecordingCanvasTests.cpp \
+ tests/unit/SkiaCanvasTests.cpp
endif
include $(LOCAL_PATH)/hwui_static_deps.mk
@@ -321,6 +323,7 @@
$(hwui_test_common_src_files) \
tests/microbench/main.cpp \
tests/microbench/DisplayListCanvasBench.cpp \
+ tests/microbench/FontBench.cpp \
tests/microbench/LinearAllocatorBench.cpp \
tests/microbench/PathParserBench.cpp \
tests/microbench/ShadowBench.cpp \
diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp
index 06b712e..5fb8425 100644
--- a/libs/hwui/BakedOpDispatcher.cpp
+++ b/libs/hwui/BakedOpDispatcher.cpp
@@ -195,7 +195,7 @@
}
static void renderTextShadow(BakedOpRenderer& renderer, FontRenderer& fontRenderer,
- const TextOp& op, const BakedOpState& state) {
+ const TextOp& op, const BakedOpState& textOpState) {
renderer.caches().textureState().activateTexture(0);
PaintUtils::TextShadow textShadow;
@@ -216,13 +216,41 @@
Glop glop;
GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
- .setRoundRectClipState(state.roundRectClipState)
+ .setRoundRectClipState(textOpState.roundRectClipState)
.setMeshTexturedUnitQuad(nullptr)
- .setFillShadowTexturePaint(*texture, textShadow.color, *op.paint, state.alpha)
- .setTransform(state.computedState.transform, TransformFlags::None)
+ .setFillShadowTexturePaint(*texture, textShadow.color, *op.paint, textOpState.alpha)
+ .setTransform(textOpState.computedState.transform, TransformFlags::None)
.setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width(), sy + texture->height()))
.build();
- renderer.renderGlop(state, glop);
+
+ // Compute damage bounds and clip (since may differ from those in textOpState).
+ // Bounds should be same as text op, but with dx/dy offset and radius outset
+ // applied in local space.
+ auto& transform = textOpState.computedState.transform;
+ Rect shadowBounds = op.unmappedBounds; // STROKE
+ const bool expandForStroke = op.paint->getStyle() != SkPaint::kFill_Style;
+ if (expandForStroke) {
+ shadowBounds.outset(op.paint->getStrokeWidth() * 0.5f);
+ }
+ shadowBounds.translate(textShadow.dx, textShadow.dy);
+ shadowBounds.outset(textShadow.radius, textShadow.radius);
+ transform.mapRect(shadowBounds);
+ if (CC_UNLIKELY(expandForStroke &&
+ (!transform.isPureTranslate() || op.paint->getStrokeWidth() < 1.0f))) {
+ shadowBounds.outset(0.5f);
+ }
+
+ auto clipState = textOpState.computedState.clipState;
+ if (clipState->mode != ClipMode::Rectangle
+ || !clipState->rect.contains(shadowBounds)) {
+ // need clip, so pass it and clip bounds
+ shadowBounds.doIntersect(clipState->rect);
+ } else {
+ // don't need clip, ignore
+ clipState = nullptr;
+ }
+
+ renderer.renderGlop(&shadowBounds, clipState, glop);
}
enum class TextRenderType {
@@ -506,6 +534,22 @@
renderer.renderGlop(state, glop);
}
+void BakedOpDispatcher::onColorOp(BakedOpRenderer& renderer, const ColorOp& op, const BakedOpState& state) {
+ SkPaint paint;
+ paint.setColor(op.color);
+ paint.setXfermodeMode(op.mode);
+
+ Glop glop;
+ GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
+ .setRoundRectClipState(state.roundRectClipState)
+ .setMeshUnitQuad()
+ .setFillPaint(paint, state.alpha)
+ .setTransform(Matrix4::identity(), TransformFlags::None)
+ .setModelViewMapUnitToRect(state.computedState.clipState->rect)
+ .build();
+ renderer.renderGlop(state, glop);
+}
+
void BakedOpDispatcher::onFunctorOp(BakedOpRenderer& renderer, const FunctorOp& op, const BakedOpState& state) {
renderer.renderFunctor(op, state);
}
@@ -765,10 +809,6 @@
Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
.build();
renderer.renderGlop(state, glop);
-
- if (op.destroy) {
- renderer.renderState().layerPool().putOrDelete(buffer);
- }
}
}
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
index 20f102b..3c302b3 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -37,6 +37,10 @@
return buffer;
}
+void BakedOpRenderer::recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) {
+ mRenderState.layerPool().putOrDelete(offscreenBuffer);
+}
+
void BakedOpRenderer::startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) {
LOG_ALWAYS_FATAL_IF(mRenderTarget.offscreenBuffer, "already has layer...");
diff --git a/libs/hwui/BakedOpRenderer.h b/libs/hwui/BakedOpRenderer.h
index 1b4065a..62bc564 100644
--- a/libs/hwui/BakedOpRenderer.h
+++ b/libs/hwui/BakedOpRenderer.h
@@ -69,6 +69,7 @@
void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect);
void endFrame(const Rect& repaintRect);
WARN_UNUSED_RESULT OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height);
+ void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer);
void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect);
void endLayer();
WARN_UNUSED_RESULT OffscreenBuffer* copyToLayer(const Rect& area);
diff --git a/libs/hwui/BakedOpState.cpp b/libs/hwui/BakedOpState.cpp
index 85903654..b70d586 100644
--- a/libs/hwui/BakedOpState.cpp
+++ b/libs/hwui/BakedOpState.cpp
@@ -108,5 +108,63 @@
clippedBounds.doIntersect(clipRect->rect);
}
+BakedOpState* BakedOpState::tryConstruct(LinearAllocator& allocator,
+ Snapshot& snapshot, const RecordedOp& recordedOp) {
+ if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
+ BakedOpState* bakedState = allocator.create_trivial<BakedOpState>(
+ allocator, snapshot, recordedOp, false);
+ if (bakedState->computedState.clippedBounds.isEmpty()) {
+ // bounds are empty, so op is rejected
+ allocator.rewindIfLastAlloc(bakedState);
+ return nullptr;
+ }
+ return bakedState;
+}
+
+BakedOpState* BakedOpState::tryConstructUnbounded(LinearAllocator& allocator,
+ Snapshot& snapshot, const RecordedOp& recordedOp) {
+ if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
+ return allocator.create_trivial<BakedOpState>(allocator, snapshot, recordedOp);
+}
+
+BakedOpState* BakedOpState::tryStrokeableOpConstruct(LinearAllocator& allocator,
+ Snapshot& snapshot, const RecordedOp& recordedOp, StrokeBehavior strokeBehavior) {
+ if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
+ bool expandForStroke = (strokeBehavior == StrokeBehavior::StyleDefined)
+ ? (recordedOp.paint && recordedOp.paint->getStyle() != SkPaint::kFill_Style)
+ : true;
+
+ BakedOpState* bakedState = allocator.create_trivial<BakedOpState>(
+ allocator, snapshot, recordedOp, expandForStroke);
+ if (bakedState->computedState.clippedBounds.isEmpty()) {
+ // bounds are empty, so op is rejected
+ // NOTE: this won't succeed if a clip was allocated
+ allocator.rewindIfLastAlloc(bakedState);
+ return nullptr;
+ }
+ return bakedState;
+}
+
+BakedOpState* BakedOpState::tryShadowOpConstruct(LinearAllocator& allocator,
+ Snapshot& snapshot, const ShadowOp* shadowOpPtr) {
+ if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
+
+ // clip isn't empty, so construct the op
+ return allocator.create_trivial<BakedOpState>(allocator, snapshot, shadowOpPtr);
+}
+
+BakedOpState* BakedOpState::directConstruct(LinearAllocator& allocator,
+ const ClipRect* clip, const Rect& dstRect, const RecordedOp& recordedOp) {
+ return allocator.create_trivial<BakedOpState>(clip, dstRect, recordedOp);
+}
+
+void BakedOpState::setupOpacity(const SkPaint* paint) {
+ computedState.opaqueOverClippedBounds = computedState.transform.isSimple()
+ && computedState.clipState->mode == ClipMode::Rectangle
+ && MathUtils::areEqual(alpha, 1.0f)
+ && !roundRectClipState
+ && PaintUtils::isOpaquePaint(paint);
+}
+
} // namespace uirenderer
} // namespace android
diff --git a/libs/hwui/BakedOpState.h b/libs/hwui/BakedOpState.h
index 4e3cb8a..e1441fc 100644
--- a/libs/hwui/BakedOpState.h
+++ b/libs/hwui/BakedOpState.h
@@ -93,6 +93,7 @@
Rect clippedBounds;
int clipSideFlags = 0;
const SkPath* localProjectionPathMask = nullptr;
+ bool opaqueOverClippedBounds = false;
};
/**
@@ -103,23 +104,10 @@
class BakedOpState {
public:
static BakedOpState* tryConstruct(LinearAllocator& allocator,
- Snapshot& snapshot, const RecordedOp& recordedOp) {
- if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
- BakedOpState* bakedState = allocator.create_trivial<BakedOpState>(
- allocator, snapshot, recordedOp, false);
- if (bakedState->computedState.clippedBounds.isEmpty()) {
- // bounds are empty, so op is rejected
- allocator.rewindIfLastAlloc(bakedState);
- return nullptr;
- }
- return bakedState;
- }
+ Snapshot& snapshot, const RecordedOp& recordedOp);
static BakedOpState* tryConstructUnbounded(LinearAllocator& allocator,
- Snapshot& snapshot, const RecordedOp& recordedOp) {
- if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
- return allocator.create_trivial<BakedOpState>(allocator, snapshot, recordedOp);
- }
+ Snapshot& snapshot, const RecordedOp& recordedOp);
enum class StrokeBehavior {
// stroking is forced, regardless of style on paint (such as for lines)
@@ -129,35 +117,16 @@
};
static BakedOpState* tryStrokeableOpConstruct(LinearAllocator& allocator,
- Snapshot& snapshot, const RecordedOp& recordedOp, StrokeBehavior strokeBehavior) {
- if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
- bool expandForStroke = (strokeBehavior == StrokeBehavior::StyleDefined)
- ? (recordedOp.paint && recordedOp.paint->getStyle() != SkPaint::kFill_Style)
- : true;
-
- BakedOpState* bakedState = allocator.create_trivial<BakedOpState>(
- allocator, snapshot, recordedOp, expandForStroke);
- if (bakedState->computedState.clippedBounds.isEmpty()) {
- // bounds are empty, so op is rejected
- // NOTE: this won't succeed if a clip was allocated
- allocator.rewindIfLastAlloc(bakedState);
- return nullptr;
- }
- return bakedState;
- }
+ Snapshot& snapshot, const RecordedOp& recordedOp, StrokeBehavior strokeBehavior);
static BakedOpState* tryShadowOpConstruct(LinearAllocator& allocator,
- Snapshot& snapshot, const ShadowOp* shadowOpPtr) {
- if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
-
- // clip isn't empty, so construct the op
- return allocator.create_trivial<BakedOpState>(allocator, snapshot, shadowOpPtr);
- }
+ Snapshot& snapshot, const ShadowOp* shadowOpPtr);
static BakedOpState* directConstruct(LinearAllocator& allocator,
- const ClipRect* clip, const Rect& dstRect, const RecordedOp& recordedOp) {
- return allocator.create_trivial<BakedOpState>(clip, dstRect, recordedOp);
- }
+ const ClipRect* clip, const Rect& dstRect, const RecordedOp& recordedOp);
+
+ // Set opaqueOverClippedBounds. If this method isn't called, the op is assumed translucent.
+ void setupOpacity(const SkPaint* paint);
// computed state:
ResolvedRenderState computedState;
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index 59f0d7c..181b343 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -45,6 +45,7 @@
, regions(stdAllocator)
, referenceHolders(stdAllocator)
, functors(stdAllocator)
+ , pushStagingFunctors(stdAllocator)
, hasDrawOps(false) {
}
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index 60cc7ba..e209e2a 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -110,6 +110,16 @@
};
/**
+ * Functor that can be used for objects with data in both UI thread and RT to keep the data
+ * in sync. This functor, when added to DisplayList, will be call during DisplayList sync.
+ */
+struct PushStagingFunctor {
+ PushStagingFunctor() {}
+ virtual ~PushStagingFunctor() {}
+ virtual void operator ()() {}
+};
+
+/**
* Data structure that holds the list of commands used in display list stream
*/
class DisplayList {
@@ -142,6 +152,7 @@
const LsaVector<const SkBitmap*>& getBitmapResources() const { return bitmapResources; }
const LsaVector<Functor*>& getFunctors() const { return functors; }
+ const LsaVector<PushStagingFunctor*>& getPushStagingFunctors() { return pushStagingFunctors; }
size_t addChild(NodeOpType* childOp);
@@ -183,6 +194,11 @@
// List of functors
LsaVector<Functor*> functors;
+ // List of functors that need to be notified of pushStaging. Note that this list gets nothing
+ // but a callback during sync DisplayList, unlike the list of functors defined above, which
+ // gets special treatment exclusive for webview.
+ LsaVector<PushStagingFunctor*> pushStagingFunctors;
+
bool hasDrawOps; // only used if !HWUI_NEW_OPS
void cleanupResources();
diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp
index 2dccf32..c6e92ab 100644
--- a/libs/hwui/DisplayListCanvas.cpp
+++ b/libs/hwui/DisplayListCanvas.cpp
@@ -415,7 +415,8 @@
void DisplayListCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
mDisplayList->ref(tree);
- addDrawOp(new (alloc()) DrawVectorDrawableOp(tree));
+ mDisplayList->pushStagingFunctors.push_back(tree->getFunctor());
+ addDrawOp(new (alloc()) DrawVectorDrawableOp(tree, tree->stagingProperties()->getBounds()));
}
void DisplayListCanvas::drawGlyphsOnPath(const uint16_t* glyphs, int count,
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 516e619..59f073f 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -1110,15 +1110,14 @@
class DrawVectorDrawableOp : public DrawOp {
public:
- DrawVectorDrawableOp(VectorDrawableRoot* tree)
- : DrawOp(nullptr), mTree(tree) {}
+ DrawVectorDrawableOp(VectorDrawableRoot* tree, const SkRect& bounds)
+ : DrawOp(nullptr), mTree(tree), mDst(bounds) {}
virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
const SkBitmap& bitmap = mTree->getBitmapUpdateIfDirty();
SkPaint* paint = mTree->getPaint();
- const SkRect bounds = mTree->getBounds();
renderer.drawBitmap(&bitmap, Rect(0, 0, bitmap.width(), bitmap.height()),
- bounds, paint);
+ mDst, paint);
}
virtual void output(int level, uint32_t logFlags) const override {
@@ -1129,6 +1128,7 @@
private:
VectorDrawableRoot* mTree;
+ SkRect mDst;
};
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index fae8e48..0401f2d 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -37,7 +37,8 @@
const LightGeometry& lightGeometry, const Rect &contentDrawBounds, Caches& caches)
: mCanvasState(*this)
, mCaches(caches)
- , mLightRadius(lightGeometry.radius) {
+ , mLightRadius(lightGeometry.radius)
+ , mDrawFbo0(!nodes.empty()) {
ATRACE_NAME("prepare drawing commands");
mLayerBuilders.reserve(layers.entries().size());
@@ -481,12 +482,17 @@
* Defers an unmergeable, strokeable op, accounting correctly
* for paint's style on the bounds being computed.
*/
-const BakedOpState* FrameBuilder::deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
+BakedOpState* FrameBuilder::deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
BakedOpState::StrokeBehavior strokeBehavior) {
// Note: here we account for stroke when baking the op
BakedOpState* bakedState = BakedOpState::tryStrokeableOpConstruct(
mAllocator, *mCanvasState.writableSnapshot(), op, strokeBehavior);
if (!bakedState) return nullptr; // quick rejected
+
+ if (op.opId == RecordedOpId::RectOp && op.paint->getStyle() != SkPaint::kStroke_Style) {
+ bakedState->setupOpacity(op.paint);
+ }
+
currentLayer().deferUnmergeableOp(mAllocator, bakedState, batchId);
return bakedState;
}
@@ -517,6 +523,10 @@
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
+ if (op.bitmap->isOpaque()) {
+ bakedState->setupOpacity(op.paint);
+ }
+
// Don't merge non-simply transformed or neg scale ops, SET_TEXTURE doesn't handle rotation
// Don't merge A8 bitmaps - the paint's color isn't compared by mergeId, or in
// MergingDrawBatch::canMergeWith()
@@ -572,6 +582,12 @@
deferOvalOp(*resolvedOp);
}
+void FrameBuilder::deferColorOp(const ColorOp& op) {
+ BakedOpState* bakedState = tryBakeUnboundedOpState(op);
+ if (!bakedState) return; // quick rejected
+ currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Vertices);
+}
+
void FrameBuilder::deferFunctorOp(const FunctorOp& op) {
BakedOpState* bakedState = tryBakeUnboundedOpState(op);
if (!bakedState) return; // quick rejected
@@ -693,7 +709,17 @@
void FrameBuilder::deferTextureLayerOp(const TextureLayerOp& op) {
if (CC_UNLIKELY(!op.layer->isRenderable())) return;
- BakedOpState* bakedState = tryBakeOpState(op);
+
+ const TextureLayerOp* textureLayerOp = &op;
+ // Now safe to access transform (which was potentially unready at record time)
+ if (!op.layer->getTransform().isIdentity()) {
+ // non-identity transform present, so 'inject it' into op by copying + replacing matrix
+ Matrix4 combinedMatrix(op.localMatrix);
+ combinedMatrix.multiply(op.layer->getTransform());
+ textureLayerOp = mAllocator.create<TextureLayerOp>(op, combinedMatrix);
+ }
+ BakedOpState* bakedState = tryBakeOpState(*textureLayerOp);
+
if (!bakedState) return; // quick rejected
currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::TextureLayer);
}
diff --git a/libs/hwui/FrameBuilder.h b/libs/hwui/FrameBuilder.h
index 0b7a606..039ab6b 100644
--- a/libs/hwui/FrameBuilder.h
+++ b/libs/hwui/FrameBuilder.h
@@ -86,6 +86,7 @@
*/
template <typename StaticDispatcher, typename Renderer>
void replayBakedOps(Renderer& renderer) {
+ std::vector<OffscreenBuffer*> temporaryLayers;
finishDefer();
/**
* Defines a LUT of lambdas which allow a recorded BakedOpState to use state->op->opId to
@@ -129,6 +130,7 @@
} else if (!layer.empty()) {
// save layer - skip entire layer if empty (in which case, LayerOp has null layer).
layer.offscreenBuffer = renderer.startTemporaryLayer(layer.width, layer.height);
+ temporaryLayers.push_back(layer.offscreenBuffer);
GL_CHECKPOINT(MODERATE);
layer.replayBakedOpsImpl((void*)&renderer, unmergedReceivers, mergedReceivers);
GL_CHECKPOINT(MODERATE);
@@ -137,12 +139,18 @@
}
GL_CHECKPOINT(MODERATE);
- const LayerBuilder& fbo0 = *(mLayerBuilders[0]);
- renderer.startFrame(fbo0.width, fbo0.height, fbo0.repaintRect);
- GL_CHECKPOINT(MODERATE);
- fbo0.replayBakedOpsImpl((void*)&renderer, unmergedReceivers, mergedReceivers);
- GL_CHECKPOINT(MODERATE);
- renderer.endFrame(fbo0.repaintRect);
+ if (CC_LIKELY(mDrawFbo0)) {
+ const LayerBuilder& fbo0 = *(mLayerBuilders[0]);
+ renderer.startFrame(fbo0.width, fbo0.height, fbo0.repaintRect);
+ GL_CHECKPOINT(MODERATE);
+ fbo0.replayBakedOpsImpl((void*)&renderer, unmergedReceivers, mergedReceivers);
+ GL_CHECKPOINT(MODERATE);
+ renderer.endFrame(fbo0.repaintRect);
+ }
+
+ for (auto& temporaryLayer : temporaryLayers) {
+ renderer.recycleTemporaryLayer(temporaryLayer);
+ }
}
void dump() const {
@@ -201,7 +209,7 @@
return mAllocator.create<SkPath>();
}
- const BakedOpState* deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
+ BakedOpState* deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
BakedOpState::StrokeBehavior strokeBehavior = BakedOpState::StrokeBehavior::StyleDefined);
/**
@@ -239,6 +247,8 @@
// contains single-frame objects, such as BakedOpStates, LayerBuilders, Batches
LinearAllocator mAllocator;
+
+ const bool mDrawFbo0;
};
}; // namespace uirenderer
diff --git a/libs/hwui/Glop.h b/libs/hwui/Glop.h
index 704bd69..6a96634 100644
--- a/libs/hwui/Glop.h
+++ b/libs/hwui/Glop.h
@@ -163,11 +163,13 @@
GLenum dst;
} blend;
+#if !HWUI_NEW_OPS
/**
* Bounds of the drawing command in layer space. Only mapped into layer
* space once GlopBuilder::build() is called.
*/
Rect bounds; // TODO: remove for HWUI_NEW_OPS
+#endif
/**
* Additional render state to enumerate:
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index 2799def..7d4f410 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -492,7 +492,9 @@
mOutGlop->transform.modelView.loadTranslate(destination.left, destination.top, 0.0f);
mOutGlop->transform.modelView.scale(destination.getWidth(), destination.getHeight(), 1.0f);
+#if !HWUI_NEW_OPS
mOutGlop->bounds = destination;
+#endif
return *this;
}
@@ -516,7 +518,9 @@
mOutGlop->transform.modelView.loadTranslate(left, top, 0.0f);
mOutGlop->transform.modelView.scale(destination.getWidth(), destination.getHeight(), 1.0f);
+#if !HWUI_NEW_OPS
mOutGlop->bounds = destination;
+#endif
return *this;
}
@@ -524,8 +528,10 @@
TRIGGER_STAGE(kModelViewStage);
mOutGlop->transform.modelView.loadTranslate(offsetX, offsetY, 0.0f);
+#if !HWUI_NEW_OPS
mOutGlop->bounds = source;
mOutGlop->bounds.translate(offsetX, offsetY);
+#endif
return *this;
}
@@ -545,8 +551,10 @@
}
mOutGlop->transform.modelView.loadTranslate(offsetX, offsetY, 0.0f);
+#if !HWUI_NEW_OPS
mOutGlop->bounds = source;
mOutGlop->bounds.translate(offsetX, offsetY);
+#endif
return *this;
}
@@ -643,7 +651,9 @@
// Final step: populate program and map bounds into render target space
mOutGlop->fill.program = mCaches.programCache.get(mDescription);
+#if !HWUI_NEW_OPS
mOutGlop->transform.meshTransform().mapRect(mOutGlop->bounds);
+#endif
}
void GlopBuilder::dump(const Glop& glop) {
@@ -683,7 +693,9 @@
ALOGD_IF(glop.roundRectClipState, "Glop RRCS %p", glop.roundRectClipState);
ALOGD("Glop blend %d %d", glop.blend.src, glop.blend.dst);
+#if !HWUI_NEW_OPS
ALOGD("Glop bounds " RECT_STRING, RECT_ARGS(glop.bounds));
+#endif
}
} /* namespace uirenderer */
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index c305f65..d1ff670 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -15,12 +15,16 @@
*/
#include "JankTracker.h"
+#include "Properties.h"
+
#include <algorithm>
#include <cutils/ashmem.h>
#include <cutils/log.h>
#include <cstdio>
#include <errno.h>
#include <inttypes.h>
+#include <limits>
+#include <cmath>
#include <sys/mman.h>
namespace android {
@@ -52,32 +56,35 @@
static const int64_t IGNORE_EXCEEDING = seconds_to_nanoseconds(10);
/*
- * Frames that are exempt from jank metrics.
- * First-draw frames, for example, are expected to
- * be slow, this is hidden from the user with window animations and
- * other tricks
- *
- * Similarly, we don't track direct-drawing via Surface:lockHardwareCanvas()
+ * We don't track direct-drawing via Surface:lockHardwareCanvas()
* for now
*
* TODO: kSurfaceCanvas can negatively impact other drawing by using up
* time on the RenderThread, figure out how to attribute that as a jank-causer
*/
-static const int64_t EXEMPT_FRAMES_FLAGS
- = FrameInfoFlags::WindowLayoutChanged
- | FrameInfoFlags::SurfaceCanvas;
+static const int64_t EXEMPT_FRAMES_FLAGS = FrameInfoFlags::SurfaceCanvas;
// The bucketing algorithm controls so to speak
// If a frame is <= to this it goes in bucket 0
-static const uint32_t kBucketMinThreshold = 7;
+static const uint32_t kBucketMinThreshold = 5;
// If a frame is > this, start counting in increments of 2ms
static const uint32_t kBucket2msIntervals = 32;
// If a frame is > this, start counting in increments of 4ms
static const uint32_t kBucket4msIntervals = 48;
+// For testing purposes to try and eliminate test infra overhead we will
+// consider any unknown delay of frame start as part of the test infrastructure
+// and filter it out of the frame profile data
+static FrameInfoIndex sFrameStart = FrameInfoIndex::IntendedVsync;
+
+// The interval of the slow frame histogram
+static const uint32_t kSlowFrameBucketIntervalMs = 50;
+// The start point of the slow frame bucket in ms
+static const uint32_t kSlowFrameBucketStartMs = 150;
+
// This will be called every frame, performance sensitive
// Uses bit twiddling to avoid branching while achieving the packing desired
-static uint32_t frameCountIndexForFrameTime(nsecs_t frameTime, uint32_t max) {
+static uint32_t frameCountIndexForFrameTime(nsecs_t frameTime) {
uint32_t index = static_cast<uint32_t>(ns2ms(frameTime));
// If index > kBucketMinThreshold mask will be 0xFFFFFFFF as a result
// of negating 1 (twos compliment, yaay) else mask will be 0
@@ -95,7 +102,7 @@
// be a pretty garbage value right now. However, mask is 0 so we'll end
// up with the desired result of 0.
index = (index - kBucketMinThreshold) & mask;
- return index < max ? index : max;
+ return index;
}
// Only called when dumping stats, less performance sensitive
@@ -206,20 +213,30 @@
mData->totalFrameCount++;
// Fast-path for jank-free frames
int64_t totalDuration =
- frame[FrameInfoIndex::FrameCompleted] - frame[FrameInfoIndex::IntendedVsync];
- uint32_t framebucket = frameCountIndexForFrameTime(
- totalDuration, mData->frameCounts.size());
+ frame[FrameInfoIndex::FrameCompleted] - frame[sFrameStart];
+ uint32_t framebucket = frameCountIndexForFrameTime(totalDuration);
// Keep the fast path as fast as possible.
if (CC_LIKELY(totalDuration < mFrameInterval)) {
mData->frameCounts[framebucket]++;
return;
}
+ // Only things like Surface.lockHardwareCanvas() are exempt from tracking
if (frame[FrameInfoIndex::Flags] & EXEMPT_FRAMES_FLAGS) {
return;
}
- mData->frameCounts[framebucket]++;
+ if (framebucket <= mData->frameCounts.size()) {
+ mData->frameCounts[framebucket]++;
+ } else {
+ framebucket = (ns2ms(totalDuration) - kSlowFrameBucketStartMs)
+ / kSlowFrameBucketIntervalMs;
+ framebucket = std::min(framebucket,
+ static_cast<uint32_t>(mData->slowFrameCounts.size() - 1));
+ framebucket = std::max(framebucket, 0u);
+ mData->slowFrameCounts[framebucket]++;
+ }
+
mData->jankFrameCount++;
for (int i = 0; i < NUM_BUCKETS; i++) {
@@ -239,6 +256,9 @@
}
void JankTracker::dumpData(const ProfileData* data, int fd) {
+ if (sFrameStart != FrameInfoIndex::IntendedVsync) {
+ dprintf(fd, "\nNote: Data has been filtered!");
+ }
dprintf(fd, "\nStats since: %" PRIu64 "ns", data->statStartTime);
dprintf(fd, "\nTotal frames rendered: %u", data->totalFrameCount);
dprintf(fd, "\nJanky frames: %u (%.2f%%)", data->jankFrameCount,
@@ -250,6 +270,15 @@
for (int i = 0; i < NUM_BUCKETS; i++) {
dprintf(fd, "\nNumber %s: %u", JANK_TYPE_NAMES[i], data->jankTypeCounts[i]);
}
+ dprintf(fd, "\nHISTOGRAM:");
+ for (size_t i = 0; i < data->frameCounts.size(); i++) {
+ dprintf(fd, " %ums=%u", frameTimeForFrameCountIndex(i),
+ data->frameCounts[i]);
+ }
+ for (size_t i = 0; i < data->slowFrameCounts.size(); i++) {
+ dprintf(fd, " %zums=%u", (i * kSlowFrameBucketIntervalMs) + kSlowFrameBucketStartMs,
+ data->slowFrameCounts[i]);
+ }
dprintf(fd, "\n");
}
@@ -259,11 +288,20 @@
mData->totalFrameCount = 0;
mData->jankFrameCount = 0;
mData->statStartTime = systemTime(CLOCK_MONOTONIC);
+ sFrameStart = Properties::filterOutTestOverhead
+ ? FrameInfoIndex::HandleInputStart
+ : FrameInfoIndex::IntendedVsync;
}
uint32_t JankTracker::findPercentile(const ProfileData* data, int percentile) {
int pos = percentile * data->totalFrameCount / 100;
int remaining = data->totalFrameCount - pos;
+ for (int i = data->slowFrameCounts.size() - 1; i >= 0; i--) {
+ remaining -= data->slowFrameCounts[i];
+ if (remaining <= 0) {
+ return (i * kSlowFrameBucketIntervalMs) + kSlowFrameBucketStartMs;
+ }
+ }
for (int i = data->frameCounts.size() - 1; i >= 0; i--) {
remaining -= data->frameCounts[i];
if (remaining <= 0) {
diff --git a/libs/hwui/JankTracker.h b/libs/hwui/JankTracker.h
index 3887e5e..84b8c3f 100644
--- a/libs/hwui/JankTracker.h
+++ b/libs/hwui/JankTracker.h
@@ -44,7 +44,9 @@
struct ProfileData {
std::array<uint32_t, NUM_BUCKETS> jankTypeCounts;
// See comments on kBucket* constants for what this holds
- std::array<uint32_t, 55> frameCounts;
+ std::array<uint32_t, 57> frameCounts;
+ // Holds a histogram of frame times in 50ms increments from 150ms to 5s
+ std::array<uint16_t, 97> slowFrameCounts;
uint32_t totalFrameCount;
uint32_t jankFrameCount;
diff --git a/libs/hwui/LayerBuilder.cpp b/libs/hwui/LayerBuilder.cpp
index e6a95ff..eea11bf 100644
--- a/libs/hwui/LayerBuilder.cpp
+++ b/libs/hwui/LayerBuilder.cpp
@@ -236,6 +236,21 @@
mClearRects.push_back(rect);
}
+void LayerBuilder::onDeferOp(LinearAllocator& allocator, const BakedOpState* bakedState) {
+ if (bakedState->op->opId != RecordedOpId::CopyToLayerOp) {
+ // First non-CopyToLayer, so stop stashing up layer clears for unclipped save layers,
+ // and issue them together in one draw.
+ flushLayerClears(allocator);
+
+ if (CC_UNLIKELY(activeUnclippedSaveLayers.empty()
+ && bakedState->computedState.opaqueOverClippedBounds
+ && bakedState->computedState.clippedBounds.contains(repaintRect))) {
+ // discard all deferred drawing ops, since new one will occlude them
+ clear();
+ }
+ }
+}
+
void LayerBuilder::flushLayerClears(LinearAllocator& allocator) {
if (CC_UNLIKELY(!mClearRects.empty())) {
const int vertCount = mClearRects.size() * 4;
@@ -270,11 +285,7 @@
void LayerBuilder::deferUnmergeableOp(LinearAllocator& allocator,
BakedOpState* op, batchid_t batchId) {
- if (batchId != OpBatchType::CopyToLayer) {
- // if first op after one or more unclipped saveLayers, flush the layer clears
- flushLayerClears(allocator);
- }
-
+ onDeferOp(allocator, op);
OpBatch* targetBatch = mBatchLookup[batchId];
size_t insertBatchIndex = mBatches.size();
@@ -295,10 +306,7 @@
void LayerBuilder::deferMergeableOp(LinearAllocator& allocator,
BakedOpState* op, batchid_t batchId, mergeid_t mergeId) {
- if (batchId != OpBatchType::CopyToLayer) {
- // if first op after one or more unclipped saveLayers, flush the layer clears
- flushLayerClears(allocator);
- }
+ onDeferOp(allocator, op);
MergingOpBatch* targetBatch = nullptr;
// Try to merge with any existing batch with same mergeId
@@ -348,6 +356,14 @@
}
}
+void LayerBuilder::clear() {
+ mBatches.clear();
+ for (int i = 0; i < OpBatchType::Count; i++) {
+ mBatchLookup[i] = nullptr;
+ mMergingBatchLookup[i].clear();
+ }
+}
+
void LayerBuilder::dump() const {
ALOGD("LayerBuilder %p, %ux%u buffer %p, blo %p, rn %p (%s)",
this, width, height, offscreenBuffer, beginLayerOp,
diff --git a/libs/hwui/LayerBuilder.h b/libs/hwui/LayerBuilder.h
index 4a7ca2d..4de432c 100644
--- a/libs/hwui/LayerBuilder.h
+++ b/libs/hwui/LayerBuilder.h
@@ -100,9 +100,7 @@
return mBatches.empty();
}
- void clear() {
- mBatches.clear();
- }
+ void clear();
void dump() const;
@@ -117,6 +115,7 @@
// list of deferred CopyFromLayer ops, to be deferred upon encountering EndUnclippedLayerOps
std::vector<BakedOpState*> activeUnclippedSaveLayers;
private:
+ void onDeferOp(LinearAllocator& allocator, const BakedOpState* bakedState);
void flushLayerClears(LinearAllocator& allocator);
std::vector<BatchBase*> mBatches;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index c099427..53ea7fa 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1413,7 +1413,9 @@
if (type == GlopRenderType::Standard && !mRenderState.stencil().isWriteEnabled()) {
// TODO: specify more clearly when a draw should dirty the layer.
// is writing to the stencil the only time we should ignore this?
+#if !HWUI_NEW_OPS
dirtyLayer(glop.bounds.left, glop.bounds.top, glop.bounds.right, glop.bounds.bottom);
+#endif
mDirty = true;
}
}
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index bbd8c72..6f68c2b 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -66,6 +66,8 @@
bool Properties::waitForGpuCompletion = false;
+bool Properties::filterOutTestOverhead = false;
+
static int property_get_int(const char* key, int defaultValue) {
char buf[PROPERTY_VALUE_MAX] = {'\0',};
@@ -156,6 +158,8 @@
textureCacheFlushRate = std::max(0.0f, std::min(1.0f,
property_get_float(PROPERTY_TEXTURE_CACHE_FLUSH_RATE, DEFAULT_TEXTURE_CACHE_FLUSH_RATE)));
+ filterOutTestOverhead = property_get_bool(PROPERTY_FILTER_TEST_OVERHEAD, false);
+
return (prevDebugLayersUpdates != debugLayersUpdates)
|| (prevDebugOverdraw != debugOverdraw)
|| (prevDebugStencilClip != debugStencilClip);
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 249b5b0..171873d 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -151,6 +151,8 @@
*/
#define PROPERTY_ENABLE_PARTIAL_UPDATES "debug.hwui.enable_partial_updates"
+#define PROPERTY_FILTER_TEST_OVERHEAD "debug.hwui.filter_test_overhead"
+
///////////////////////////////////////////////////////////////////////////////
// Runtime configuration properties
///////////////////////////////////////////////////////////////////////////////
@@ -294,6 +296,10 @@
// Should be used only by test apps
static bool waitForGpuCompletion;
+ // Should only be set by automated tests to try and filter out
+ // any overhead they add
+ static bool filterOutTestOverhead;
+
private:
static ProfileType sProfileType;
static bool sDisableProfileBars;
diff --git a/libs/hwui/PropertyValuesHolder.cpp b/libs/hwui/PropertyValuesHolder.cpp
index 8f837f6..0932d65 100644
--- a/libs/hwui/PropertyValuesHolder.cpp
+++ b/libs/hwui/PropertyValuesHolder.cpp
@@ -53,7 +53,7 @@
} else {
animatedValue = mStartValue * (1 - fraction) + mEndValue * fraction;
}
- mGroup->setPropertyValue(mPropertyId, animatedValue);
+ mGroup->mutateProperties()->setPropertyValue(mPropertyId, animatedValue);
}
inline U8CPU lerp(U8CPU fromValue, U8CPU toValue, float fraction) {
@@ -72,7 +72,7 @@
void FullPathColorPropertyValuesHolder::setFraction(float fraction) {
SkColor animatedValue = interpolateColors(mStartValue, mEndValue, fraction);
- mFullPath->setColorPropertyValue(mPropertyId, animatedValue);
+ mFullPath->mutateProperties()->setColorPropertyValue(mPropertyId, animatedValue);
}
void FullPathPropertyValuesHolder::setFraction(float fraction) {
@@ -82,17 +82,17 @@
} else {
animatedValue = mStartValue * (1 - fraction) + mEndValue * fraction;
}
- mFullPath->setPropertyValue(mPropertyId, animatedValue);
+ mFullPath->mutateProperties()->setPropertyValue(mPropertyId, animatedValue);
}
void PathDataPropertyValuesHolder::setFraction(float fraction) {
VectorDrawableUtils::interpolatePaths(&mPathData, mStartValue, mEndValue, fraction);
- mPath->setPathData(mPathData);
+ mPath->mutateProperties()->setData(mPathData);
}
void RootAlphaPropertyValuesHolder::setFraction(float fraction) {
float animatedValue = mStartValue * (1 - fraction) + mEndValue * fraction;
- mTree->setRootAlpha(animatedValue);
+ mTree->mutateProperties()->setRootAlpha(animatedValue);
}
} // namepace uirenderer
diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h
index 96a57b6..aee9d63 100644
--- a/libs/hwui/RecordedOp.h
+++ b/libs/hwui/RecordedOp.h
@@ -90,6 +90,7 @@
UNMERGEABLE_OP_FN(ArcOp) \
UNMERGEABLE_OP_FN(BitmapMeshOp) \
UNMERGEABLE_OP_FN(BitmapRectOp) \
+ UNMERGEABLE_OP_FN(ColorOp) \
UNMERGEABLE_OP_FN(FunctorOp) \
UNMERGEABLE_OP_FN(LinesOp) \
UNMERGEABLE_OP_FN(OvalOp) \
@@ -256,6 +257,16 @@
const float* radius;
};
+struct ColorOp : RecordedOp {
+ // Note: unbounded op that will fillclip, so no bounds/matrix needed
+ ColorOp(const ClipBase* localClip, int color, SkXfermode::Mode mode)
+ : RecordedOp(RecordedOpId::ColorOp, Rect(), Matrix4::identity(), localClip, nullptr)
+ , color(color)
+ , mode(mode) {}
+ const int color;
+ const SkXfermode::Mode mode;
+};
+
struct FunctorOp : RecordedOp {
// Note: undefined record-time bounds, since this op fills the clip
// TODO: explicitly define bounds
@@ -408,6 +419,14 @@
TextureLayerOp(BASE_PARAMS_PAINTLESS, Layer* layer)
: SUPER_PAINTLESS(TextureLayerOp)
, layer(layer) {}
+
+ // Copy an existing TextureLayerOp, replacing the underlying matrix
+ TextureLayerOp(const TextureLayerOp& op, const Matrix4& replacementMatrix)
+ : RecordedOp(RecordedOpId::TextureLayerOp, op.unmappedBounds, replacementMatrix,
+ op.localClip, op.paint)
+ , layer(op.layer) {
+
+ }
Layer* layer;
};
@@ -482,22 +501,20 @@
* when creating/tracking a SkPaint* during defer isn't worth the bother.
*/
struct LayerOp : RecordedOp {
- // Records a one-use (saveLayer) layer for drawing. Once drawn, the layer will be destroyed.
+ // Records a one-use (saveLayer) layer for drawing.
LayerOp(BASE_PARAMS, OffscreenBuffer** layerHandle)
: SUPER_PAINTLESS(LayerOp)
, layerHandle(layerHandle)
, alpha(paint ? paint->getAlpha() / 255.0f : 1.0f)
, mode(PaintUtils::getXfermodeDirect(paint))
- , colorFilter(paint ? paint->getColorFilter() : nullptr)
- , destroy(true) {}
+ , colorFilter(paint ? paint->getColorFilter() : nullptr) {}
LayerOp(RenderNode& node)
: RecordedOp(RecordedOpId::LayerOp, Rect(node.getWidth(), node.getHeight()), Matrix4::identity(), nullptr, nullptr)
, layerHandle(node.getLayerHandle())
, alpha(node.properties().layerProperties().alpha() / 255.0f)
, mode(node.properties().layerProperties().xferMode())
- , colorFilter(node.properties().layerProperties().colorFilter())
- , destroy(false) {}
+ , colorFilter(node.properties().layerProperties().colorFilter()) {}
// Records a handle to the Layer object, since the Layer itself won't be
// constructed until after this operation is constructed.
@@ -508,9 +525,6 @@
// pointer to object owned by either LayerProperties, or a recorded Paint object in a
// BeginLayerOp. Lives longer than LayerOp in either case, so no skia ref counting is used.
SkColorFilter* colorFilter;
-
- // whether to destroy the layer, once rendered
- const bool destroy;
};
}; // namespace uirenderer
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 4eeadb7..b78497d 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -234,10 +234,10 @@
// android/graphics/Canvas draw operations
// ----------------------------------------------------------------------------
void RecordingCanvas::drawColor(int color, SkXfermode::Mode mode) {
- SkPaint paint;
- paint.setColor(color);
- paint.setXfermodeMode(mode);
- drawPaint(paint);
+ addOp(alloc().create_trivial<ColorOp>(
+ getRecordedClip(),
+ color,
+ mode));
}
void RecordingCanvas::drawPaint(const SkPaint& paint) {
@@ -430,10 +430,11 @@
}
void RecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
+ mDisplayList->pushStagingFunctors.push_back(tree->getFunctor());
mDisplayList->ref(tree);
addOp(alloc().create_trivial<VectorDrawableOp>(
tree,
- Rect(tree->getBounds()),
+ Rect(tree->stagingProperties()->getBounds()),
*(mState.currentSnapshot()->transform),
getRecordedClip()));
}
@@ -576,15 +577,9 @@
// Note that the backing layer has *not* yet been updated, so don't trust
// its width, height, transform, etc...!
- Matrix4 totalTransform(*(mState.currentSnapshot()->transform));
- if (layerHandle->getTransform()) {
- Matrix4 layerTransform(*layerHandle->getTransform());
- totalTransform.multiply(layerTransform);
- }
-
addOp(alloc().create_trivial<TextureLayerOp>(
Rect(layerHandle->getWidth(), layerHandle->getHeight()),
- totalTransform,
+ *(mState.currentSnapshot()->transform),
getRecordedClip(),
layerHandle->backingLayer()));
}
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 1eb4fa0..acb88e2 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -191,13 +191,16 @@
const SkPaint* paint) override;
// Text
- virtual void drawGlyphs(const uint16_t* glyphs, const float* positions, int glyphCount,
- const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop,
- float boundsRight, float boundsBottom, float totalAdvance) override;
- virtual void drawGlyphsOnPath(const uint16_t* glyphs, int glyphCount, const SkPath& path,
- float hOffset, float vOffset, const SkPaint& paint) override;
virtual bool drawTextAbsolutePos() const override { return false; }
+protected:
+ virtual void drawGlyphs(const uint16_t* text, const float* positions, int count,
+ const SkPaint& paint, float x, float y,
+ float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
+ float totalAdvance) override;
+ virtual void drawGlyphsOnPath(const uint16_t* glyphs, int count, const SkPath& path,
+ float hOffset, float vOffset, const SkPaint& paint) override;
+
private:
const ClipBase* getRecordedClip() {
return mState.writableSnapshot()->mutateClipArea().serializeClip(alloc());
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 61441ce..9578486 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -68,7 +68,7 @@
}
RenderNode::~RenderNode() {
- deleteDisplayList();
+ deleteDisplayList(nullptr);
delete mStagingDisplayList;
#if HWUI_NEW_OPS
LOG_ALWAYS_FATAL_IF(mLayer, "layer missed detachment!");
@@ -88,7 +88,7 @@
// If mParentCount == 0 we are the sole reference to this RenderNode,
// so immediately free the old display list
if (!mParentCount && !mStagingDisplayList) {
- deleteDisplayList();
+ deleteDisplayList(nullptr);
}
}
@@ -462,7 +462,7 @@
}
#endif
-void RenderNode::syncDisplayList() {
+void RenderNode::syncDisplayList(TreeObserver* observer) {
// Make sure we inc first so that we don't fluctuate between 0 and 1,
// which would thrash the layer cache
if (mStagingDisplayList) {
@@ -470,13 +470,16 @@
child->renderNode->incParentRefCount();
}
}
- deleteDisplayList();
+ deleteDisplayList(observer);
mDisplayList = mStagingDisplayList;
mStagingDisplayList = nullptr;
if (mDisplayList) {
for (size_t i = 0; i < mDisplayList->getFunctors().size(); i++) {
(*mDisplayList->getFunctors()[i])(DrawGlInfo::kModeSync, nullptr);
}
+ for (size_t i = 0; i < mDisplayList->getPushStagingFunctors().size(); i++) {
+ (*mDisplayList->getPushStagingFunctors()[i])();
+ }
}
}
@@ -486,15 +489,15 @@
// Damage with the old display list first then the new one to catch any
// changes in isRenderable or, in the future, bounds
damageSelf(info);
- syncDisplayList();
+ syncDisplayList(info.observer);
damageSelf(info);
}
}
-void RenderNode::deleteDisplayList() {
+void RenderNode::deleteDisplayList(TreeObserver* observer) {
if (mDisplayList) {
for (auto&& child : mDisplayList->getChildren()) {
- child->renderNode->decParentRefCount();
+ child->renderNode->decParentRefCount(observer);
}
}
delete mDisplayList;
@@ -526,32 +529,35 @@
}
}
-void RenderNode::destroyHardwareResources() {
+void RenderNode::destroyHardwareResources(TreeObserver* observer) {
if (mLayer) {
destroyLayer(mLayer);
mLayer = nullptr;
}
if (mDisplayList) {
for (auto&& child : mDisplayList->getChildren()) {
- child->renderNode->destroyHardwareResources();
+ child->renderNode->destroyHardwareResources(observer);
}
if (mNeedsDisplayListSync) {
// Next prepare tree we are going to push a new display list, so we can
// drop our current one now
- deleteDisplayList();
+ deleteDisplayList(observer);
}
}
}
-void RenderNode::decParentRefCount() {
+void RenderNode::decParentRefCount(TreeObserver* observer) {
LOG_ALWAYS_FATAL_IF(!mParentCount, "already 0!");
mParentCount--;
if (!mParentCount) {
+ if (observer) {
+ observer->onMaybeRemovedFromTree(this);
+ }
// If a child of ours is being attached to our parent then this will incorrectly
// destroy its hardware resources. However, this situation is highly unlikely
// and the failure is "just" that the layer is re-created, so this should
// be safe enough
- destroyHardwareResources();
+ destroyHardwareResources(observer);
}
}
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 8381925..b0136cf 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -68,6 +68,7 @@
class SaveOp;
class RestoreToCountOp;
class TreeInfo;
+class TreeObserver;
namespace proto {
class RenderNode;
@@ -154,6 +155,14 @@
}
}
+ VirtualLightRefBase* getUserContext() const {
+ return mUserContext.get();
+ }
+
+ void setUserContext(VirtualLightRefBase* context) {
+ mUserContext = context;
+ }
+
bool isPropertyFieldDirty(DirtyPropertyMask field) const {
return mDirtyPropertyFields & field;
}
@@ -187,7 +196,7 @@
}
ANDROID_API virtual void prepareTree(TreeInfo& info);
- void destroyHardwareResources();
+ void destroyHardwareResources(TreeObserver* observer);
// UI thread only!
ANDROID_API void addAnimator(const sp<BaseRenderNodeAnimator>& animator);
@@ -232,6 +241,12 @@
mPositionListener.reset(listener);
}
+ // This is only modified in MODE_FULL, so it can be safely accessed
+ // on the UI thread.
+ ANDROID_API bool hasParents() {
+ return mParentCount;
+ }
+
private:
typedef key_value_pair_t<float, DrawRenderNodeOp*> ZDrawRenderNodeOpPair;
@@ -291,7 +306,7 @@
void syncProperties();
- void syncDisplayList();
+ void syncDisplayList(TreeObserver* observer);
void prepareTreeImpl(TreeInfo& info, bool functorsNeedLayer);
void pushStagingPropertiesChanges(TreeInfo& info);
@@ -302,13 +317,14 @@
#endif
void prepareLayer(TreeInfo& info, uint32_t dirtyMask);
void pushLayerUpdate(TreeInfo& info);
- void deleteDisplayList();
+ void deleteDisplayList(TreeObserver* observer);
void damageSelf(TreeInfo& info);
void incParentRefCount() { mParentCount++; }
- void decParentRefCount();
+ void decParentRefCount(TreeObserver* observer);
String8 mName;
+ sp<VirtualLightRefBase> mUserContext;
uint32_t mDirtyPropertyFields;
RenderProperties mProperties;
diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp
index f577785..5ebf545 100644
--- a/libs/hwui/RenderProperties.cpp
+++ b/libs/hwui/RenderProperties.cpp
@@ -155,6 +155,23 @@
ALOGD("%*s(ClipRect %d, %d, %d, %d)", level * 2, "",
(int)clipRect.left, (int)clipRect.top, (int)clipRect.right, (int)clipRect.bottom);
}
+
+ if (getRevealClip().willClip()) {
+ Rect bounds;
+ getRevealClip().getBounds(&bounds);
+ ALOGD("%*s(Clip to reveal clip with bounds %.2f %.2f %.2f %.2f)", level * 2, "",
+ RECT_ARGS(bounds));
+ }
+
+ auto& outline = mPrimitiveFields.mOutline;
+ if (outline.getShouldClip()) {
+ if (outline.isEmpty()) {
+ ALOGD("%*s(Clip to empty outline)", level * 2, "");
+ } else if (outline.willClip()) {
+ ALOGD("%*s(Clip to outline with bounds %.2f %.2f %.2f %.2f)", level * 2, "",
+ RECT_ARGS(outline.getBounds()));
+ }
+ }
}
void RenderProperties::updateMatrix() {
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index b1ecb71..1b459c1 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -147,13 +147,6 @@
float dstLeft, float dstTop, float dstRight, float dstBottom,
const SkPaint* paint) override;
- virtual void drawGlyphs(const uint16_t* text, const float* positions, int count,
- const SkPaint& paint, float x, float y,
- float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
- float totalAdvance) override;
- virtual void drawGlyphsOnPath(const uint16_t* glyphs, int count, const SkPath& path,
- float hOffset, float vOffset, const SkPaint& paint) override;
-
virtual bool drawTextAbsolutePos() const override { return true; }
virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override;
@@ -169,6 +162,14 @@
virtual void drawRenderNode(uirenderer::RenderNode* renderNode) override;
virtual void callDrawGLFunction(Functor* functor) override;
+protected:
+ virtual void drawGlyphs(const uint16_t* text, const float* positions, int count,
+ const SkPaint& paint, float x, float y,
+ float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
+ float totalAdvance) override;
+ virtual void drawGlyphsOnPath(const uint16_t* glyphs, int count, const SkPath& path,
+ float hOffset, float vOffset, const SkPaint& paint) override;
+
private:
struct SaveRec {
int saveCount;
@@ -746,11 +747,7 @@
}
void SkiaCanvas::drawVectorDrawable(VectorDrawableRoot* vectorDrawable) {
- const SkBitmap& bitmap = vectorDrawable->getBitmapUpdateIfDirty();
- SkRect bounds = vectorDrawable->getBounds();
- drawBitmap(bitmap, 0, 0, bitmap.width(), bitmap.height(),
- bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom,
- vectorDrawable->getPaint());
+ vectorDrawable->drawStaging(this);
}
// ----------------------------------------------------------------------------
@@ -761,14 +758,8 @@
const SkPaint& paint, float x, float y,
float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
float totalAdvance) {
- // Set align to left for drawing, as we don't want individual
- // glyphs centered or right-aligned; the offset above takes
- // care of all alignment.
- SkPaint paintCopy(paint);
- paintCopy.setTextAlign(SkPaint::kLeft_Align);
-
static_assert(sizeof(SkPoint) == sizeof(float)*2, "SkPoint is no longer two floats");
- mCanvas->drawPosText(text, count << 1, reinterpret_cast<const SkPoint*>(positions), paintCopy);
+ mCanvas->drawPosText(text, count << 1, reinterpret_cast<const SkPoint*>(positions), paint);
drawTextDecorations(x, y, totalAdvance, paint);
}
diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp
index c612480..9df32b28 100644
--- a/libs/hwui/SkiaCanvasProxy.cpp
+++ b/libs/hwui/SkiaCanvasProxy.cpp
@@ -318,12 +318,17 @@
posArray = pointStorage.get();
}
- // compute conservative bounds
- // NOTE: We could call the faster paint.getFontBounds for a less accurate,
- // but even more conservative bounds if this is too slow.
+ // Compute conservative bounds. If the content has already been processed
+ // by Minikin then it had already computed these bounds. Unfortunately,
+ // there is no way to capture those bounds as part of the Skia drawPosText
+ // API so we need to do that computation again here.
SkRect bounds;
- glyphs.paint.measureText(glyphs.glyphIDs, glyphs.count << 1, &bounds);
- bounds.offset(x, y);
+ for (int i = 0; i < glyphs.count; i++) {
+ SkRect glyphBounds;
+ glyphs.paint.measureText(&glyphs.glyphIDs[i], sizeof(uint16_t), &glyphBounds);
+ glyphBounds.offset(pos[i].fX, pos[i].fY);
+ bounds.join(glyphBounds);
+ }
static_assert(sizeof(SkPoint) == sizeof(float)*2, "SkPoint is no longer two floats");
mCanvas->drawGlyphs(glyphs.glyphIDs, &posArray[0].fX, glyphs.count, glyphs.paint, x, y,
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index accd303..a43e544 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -32,6 +32,7 @@
class DamageAccumulator;
class LayerUpdateQueue;
class OpenGLRenderer;
+class RenderNode;
class RenderState;
class ErrorHandler {
@@ -41,6 +42,17 @@
~ErrorHandler() {}
};
+class TreeObserver {
+public:
+ // Called when a RenderNode's parent count hits 0.
+ // Due to the unordered nature of tree pushes, once prepareTree
+ // is finished it is possible that the node was "resurrected" and has
+ // a non-zero parent count.
+ virtual void onMaybeRemovedFromTree(RenderNode* node) {}
+protected:
+ ~TreeObserver() {}
+};
+
// This would be a struct, but we want to PREVENT_COPY_AND_ASSIGN
class TreeInfo {
PREVENT_COPY_AND_ASSIGN(TreeInfo);
@@ -86,6 +98,10 @@
#endif
ErrorHandler* errorHandler = nullptr;
+ // Optional, may be nullptr. Used to allow things to observe interesting
+ // tree state changes
+ TreeObserver* observer = nullptr;
+
// Frame number for use with synchronized surfaceview position updating
int64_t frameNumber = -1;
int32_t windowInsetLeft = 0;
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index d35f764..adfe45c 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -17,6 +17,7 @@
#include "VectorDrawable.h"
#include "PathParser.h"
+#include "SkColorFilter.h"
#include "SkImageInfo.h"
#include "SkShader.h"
#include <utils/Log.h>
@@ -32,41 +33,36 @@
const int Tree::MAX_CACHED_BITMAP_SIZE = 2048;
-void Path::draw(SkCanvas* outCanvas, const SkMatrix& groupStackedMatrix, float scaleX, float scaleY) {
+void Path::draw(SkCanvas* outCanvas, const SkMatrix& groupStackedMatrix, float scaleX, float scaleY,
+ bool useStagingData) {
float matrixScale = getMatrixScale(groupStackedMatrix);
if (matrixScale == 0) {
// When either x or y is scaled to 0, we don't need to draw anything.
return;
}
- const SkPath updatedPath = getUpdatedPath();
SkMatrix pathMatrix(groupStackedMatrix);
pathMatrix.postScale(scaleX, scaleY);
//TODO: try apply the path matrix to the canvas instead of creating a new path.
SkPath renderPath;
renderPath.reset();
- renderPath.addPath(updatedPath, pathMatrix);
+
+ if (useStagingData) {
+ SkPath tmpPath;
+ getStagingPath(&tmpPath);
+ renderPath.addPath(tmpPath, pathMatrix);
+ } else {
+ renderPath.addPath(getUpdatedPath(), pathMatrix);
+ }
float minScale = fmin(scaleX, scaleY);
float strokeScale = minScale * matrixScale;
- drawPath(outCanvas, renderPath, strokeScale, pathMatrix);
-}
-
-void Path::setPathData(const Data& data) {
- if (mData == data) {
- return;
- }
- // Updates the path data. Note that we don't generate a new Skia path right away
- // because there are cases where the animation is changing the path data, but the view
- // that hosts the VD has gone off screen, in which case we won't even draw. So we
- // postpone the Skia path generation to the draw time.
- mData = data;
- mSkPathDirty = true;
+ drawPath(outCanvas, renderPath, strokeScale, pathMatrix, useStagingData);
}
void Path::dump() {
- ALOGD("Path: %s has %zu points", mName.c_str(), mData.points.size());
+ ALOGD("Path: %s has %zu points", mName.c_str(), mProperties.getData().points.size());
}
float Path::getMatrixScale(const SkMatrix& groupStackedMatrix) {
@@ -95,213 +91,215 @@
}
return matrixScale;
}
+
+// Called from UI thread during the initial setup/theme change.
Path::Path(const char* pathStr, size_t strLength) {
PathParser::ParseResult result;
- PathParser::getPathDataFromString(&mData, &result, pathStr, strLength);
- if (!result.failureOccurred) {
- VectorDrawableUtils::verbsToPath(&mSkPath, mData);
- }
-}
-
-Path::Path(const Data& data) {
- mData = data;
- // Now we need to construct a path
- VectorDrawableUtils::verbsToPath(&mSkPath, data);
+ Data data;
+ PathParser::getPathDataFromString(&data, &result, pathStr, strLength);
+ mStagingProperties.setData(data);
}
Path::Path(const Path& path) : Node(path) {
- mData = path.mData;
- VectorDrawableUtils::verbsToPath(&mSkPath, mData);
-}
-
-bool Path::canMorph(const Data& morphTo) {
- return VectorDrawableUtils::canMorph(mData, morphTo);
-}
-
-bool Path::canMorph(const Path& path) {
- return canMorph(path.mData);
+ mStagingProperties.syncProperties(path.mStagingProperties);
}
const SkPath& Path::getUpdatedPath() {
if (mSkPathDirty) {
mSkPath.reset();
- VectorDrawableUtils::verbsToPath(&mSkPath, mData);
+ VectorDrawableUtils::verbsToPath(&mSkPath, mProperties.getData());
mSkPathDirty = false;
}
return mSkPath;
}
-void Path::setPath(const char* pathStr, size_t strLength) {
- PathParser::ParseResult result;
- mSkPathDirty = true;
- PathParser::getPathDataFromString(&mData, &result, pathStr, strLength);
+void Path::getStagingPath(SkPath* outPath) {
+ outPath->reset();
+ VectorDrawableUtils::verbsToPath(outPath, mStagingProperties.getData());
+}
+
+void Path::syncProperties() {
+ if (mStagingPropertiesDirty) {
+ mProperties.syncProperties(mStagingProperties);
+ } else {
+ mStagingProperties.syncProperties(mProperties);
+ }
+ mStagingPropertiesDirty = false;
}
FullPath::FullPath(const FullPath& path) : Path(path) {
- mProperties = path.mProperties;
- SkRefCnt_SafeAssign(mStrokeGradient, path.mStrokeGradient);
- SkRefCnt_SafeAssign(mFillGradient, path.mFillGradient);
+ mStagingProperties.syncProperties(path.mStagingProperties);
+}
+
+static void applyTrim(SkPath* outPath, const SkPath& inPath, float trimPathStart, float trimPathEnd,
+ float trimPathOffset) {
+ if (trimPathStart == 0.0f && trimPathEnd == 1.0f) {
+ *outPath = inPath;
+ return;
+ }
+ outPath->reset();
+ if (trimPathStart == trimPathEnd) {
+ // Trimmed path should be empty.
+ return;
+ }
+ SkPathMeasure measure(inPath, false);
+ float len = SkScalarToFloat(measure.getLength());
+ float start = len * fmod((trimPathStart + trimPathOffset), 1.0f);
+ float end = len * fmod((trimPathEnd + trimPathOffset), 1.0f);
+
+ if (start > end) {
+ measure.getSegment(start, len, outPath, true);
+ if (end > 0) {
+ measure.getSegment(0, end, outPath, true);
+ }
+ } else {
+ measure.getSegment(start, end, outPath, true);
+ }
}
const SkPath& FullPath::getUpdatedPath() {
- if (!mSkPathDirty && !mTrimDirty) {
+ if (!mSkPathDirty && !mProperties.mTrimDirty) {
return mTrimmedSkPath;
}
Path::getUpdatedPath();
- if (mProperties.trimPathStart != 0.0f || mProperties.trimPathEnd != 1.0f) {
- applyTrim();
+ if (mProperties.getTrimPathStart() != 0.0f || mProperties.getTrimPathEnd() != 1.0f) {
+ mProperties.mTrimDirty = false;
+ applyTrim(&mTrimmedSkPath, mSkPath, mProperties.getTrimPathStart(),
+ mProperties.getTrimPathEnd(), mProperties.getTrimPathOffset());
return mTrimmedSkPath;
} else {
return mSkPath;
}
}
-void FullPath::updateProperties(float strokeWidth, SkColor strokeColor, float strokeAlpha,
- SkColor fillColor, float fillAlpha, float trimPathStart, float trimPathEnd,
- float trimPathOffset, float strokeMiterLimit, int strokeLineCap, int strokeLineJoin,
- int fillType) {
- mProperties.strokeWidth = strokeWidth;
- mProperties.strokeColor = strokeColor;
- mProperties.strokeAlpha = strokeAlpha;
- mProperties.fillColor = fillColor;
- mProperties.fillAlpha = fillAlpha;
- mProperties.strokeMiterLimit = strokeMiterLimit;
- mProperties.strokeLineCap = strokeLineCap;
- mProperties.strokeLineJoin = strokeLineJoin;
- mProperties.fillType = fillType;
-
- // If any trim property changes, mark trim dirty and update the trim path
- setTrimPathStart(trimPathStart);
- setTrimPathEnd(trimPathEnd);
- setTrimPathOffset(trimPathOffset);
+void FullPath::getStagingPath(SkPath* outPath) {
+ Path::getStagingPath(outPath);
+ SkPath inPath = *outPath;
+ applyTrim(outPath, inPath, mStagingProperties.getTrimPathStart(),
+ mStagingProperties.getTrimPathEnd(), mStagingProperties.getTrimPathOffset());
}
+void FullPath::dump() {
+ Path::dump();
+ ALOGD("stroke width, color, alpha: %f, %d, %f, fill color, alpha: %d, %f",
+ mProperties.getStrokeWidth(), mProperties.getStrokeColor(), mProperties.getStrokeAlpha(),
+ mProperties.getFillColor(), mProperties.getFillAlpha());
+}
+
+
inline SkColor applyAlpha(SkColor color, float alpha) {
int alphaBytes = SkColorGetA(color);
return SkColorSetA(color, alphaBytes * alpha);
}
void FullPath::drawPath(SkCanvas* outCanvas, SkPath& renderPath, float strokeScale,
- const SkMatrix& matrix){
+ const SkMatrix& matrix, bool useStagingData){
+ const FullPathProperties& properties = useStagingData ? mStagingProperties : mProperties;
+
// Draw path's fill, if fill color or gradient is valid
bool needsFill = false;
- if (mFillGradient != nullptr) {
- mPaint.setColor(applyAlpha(SK_ColorBLACK, mProperties.fillAlpha));
- SkShader* newShader = mFillGradient->newWithLocalMatrix(matrix);
- mPaint.setShader(newShader);
+ SkPaint paint;
+ if (properties.getFillGradient() != nullptr) {
+ paint.setColor(applyAlpha(SK_ColorBLACK, properties.getFillAlpha()));
+ SkShader* newShader = properties.getFillGradient()->newWithLocalMatrix(matrix);
+ paint.setShader(newShader);
needsFill = true;
- } else if (mProperties.fillColor != SK_ColorTRANSPARENT) {
- mPaint.setColor(applyAlpha(mProperties.fillColor, mProperties.fillAlpha));
+ } else if (properties.getFillColor() != SK_ColorTRANSPARENT) {
+ paint.setColor(applyAlpha(properties.getFillColor(), properties.getFillAlpha()));
needsFill = true;
}
if (needsFill) {
- mPaint.setStyle(SkPaint::Style::kFill_Style);
- mPaint.setAntiAlias(true);
- SkPath::FillType ft = static_cast<SkPath::FillType>(mProperties.fillType);
+ paint.setStyle(SkPaint::Style::kFill_Style);
+ paint.setAntiAlias(true);
+ SkPath::FillType ft = static_cast<SkPath::FillType>(properties.getFillType());
renderPath.setFillType(ft);
- outCanvas->drawPath(renderPath, mPaint);
+ outCanvas->drawPath(renderPath, paint);
}
- // Draw path's stroke, if stroke color or gradient is valid
+ // Draw path's stroke, if stroke color or Gradient is valid
bool needsStroke = false;
- if (mStrokeGradient != nullptr) {
- mPaint.setColor(applyAlpha(SK_ColorBLACK, mProperties.strokeAlpha));
- SkShader* newShader = mStrokeGradient->newWithLocalMatrix(matrix);
- mPaint.setShader(newShader);
+ if (properties.getStrokeGradient() != nullptr) {
+ paint.setColor(applyAlpha(SK_ColorBLACK, properties.getStrokeAlpha()));
+ SkShader* newShader = properties.getStrokeGradient()->newWithLocalMatrix(matrix);
+ paint.setShader(newShader);
needsStroke = true;
- } else if (mProperties.strokeColor != SK_ColorTRANSPARENT) {
- mPaint.setColor(applyAlpha(mProperties.strokeColor, mProperties.strokeAlpha));
+ } else if (properties.getStrokeColor() != SK_ColorTRANSPARENT) {
+ paint.setColor(applyAlpha(properties.getStrokeColor(), properties.getStrokeAlpha()));
needsStroke = true;
}
if (needsStroke) {
- mPaint.setStyle(SkPaint::Style::kStroke_Style);
- mPaint.setAntiAlias(true);
- mPaint.setStrokeJoin(SkPaint::Join(mProperties.strokeLineJoin));
- mPaint.setStrokeCap(SkPaint::Cap(mProperties.strokeLineCap));
- mPaint.setStrokeMiter(mProperties.strokeMiterLimit);
- mPaint.setStrokeWidth(mProperties.strokeWidth * strokeScale);
- outCanvas->drawPath(renderPath, mPaint);
+ paint.setStyle(SkPaint::Style::kStroke_Style);
+ paint.setAntiAlias(true);
+ paint.setStrokeJoin(SkPaint::Join(properties.getStrokeLineJoin()));
+ paint.setStrokeCap(SkPaint::Cap(properties.getStrokeLineCap()));
+ paint.setStrokeMiter(properties.getStrokeMiterLimit());
+ paint.setStrokeWidth(properties.getStrokeWidth() * strokeScale);
+ outCanvas->drawPath(renderPath, paint);
}
}
-/**
- * Applies trimming to the specified path.
- */
-void FullPath::applyTrim() {
- if (mProperties.trimPathStart == 0.0f && mProperties.trimPathEnd == 1.0f) {
- // No trimming necessary.
- return;
- }
- mTrimDirty = false;
- mTrimmedSkPath.reset();
- if (mProperties.trimPathStart == mProperties.trimPathEnd) {
- // Trimmed path should be empty.
- return;
- }
- SkPathMeasure measure(mSkPath, false);
- float len = SkScalarToFloat(measure.getLength());
- float start = len * fmod((mProperties.trimPathStart + mProperties.trimPathOffset), 1.0f);
- float end = len * fmod((mProperties.trimPathEnd + mProperties.trimPathOffset), 1.0f);
+void FullPath::syncProperties() {
+ Path::syncProperties();
- if (start > end) {
- measure.getSegment(start, len, &mTrimmedSkPath, true);
- if (end > 0) {
- measure.getSegment(0, end, &mTrimmedSkPath, true);
- }
+ if (mStagingPropertiesDirty) {
+ mProperties.syncProperties(mStagingProperties);
} else {
- measure.getSegment(start, end, &mTrimmedSkPath, true);
+ // Update staging property with property values from animation.
+ mStagingProperties.syncProperties(mProperties);
}
+ mStagingPropertiesDirty = false;
}
-REQUIRE_COMPATIBLE_LAYOUT(FullPath::Properties);
+REQUIRE_COMPATIBLE_LAYOUT(FullPath::FullPathProperties::PrimitiveFields);
static_assert(sizeof(float) == sizeof(int32_t), "float is not the same size as int32_t");
static_assert(sizeof(SkColor) == sizeof(int32_t), "SkColor is not the same size as int32_t");
-bool FullPath::getProperties(int8_t* outProperties, int length) {
- int propertyDataSize = sizeof(Properties);
+bool FullPath::FullPathProperties::copyProperties(int8_t* outProperties, int length) const {
+ int propertyDataSize = sizeof(FullPathProperties::PrimitiveFields);
if (length != propertyDataSize) {
LOG_ALWAYS_FATAL("Properties needs exactly %d bytes, a byte array of size %d is provided",
propertyDataSize, length);
return false;
}
- Properties* out = reinterpret_cast<Properties*>(outProperties);
- *out = mProperties;
+
+ PrimitiveFields* out = reinterpret_cast<PrimitiveFields*>(outProperties);
+ *out = mPrimitiveFields;
return true;
}
-void FullPath::setColorPropertyValue(int propertyId, int32_t value) {
+void FullPath::FullPathProperties::setColorPropertyValue(int propertyId, int32_t value) {
Property currentProperty = static_cast<Property>(propertyId);
- if (currentProperty == Property::StrokeColor) {
- mProperties.strokeColor = value;
- } else if (currentProperty == Property::FillColor) {
- mProperties.fillColor = value;
+ if (currentProperty == Property::strokeColor) {
+ setStrokeColor(value);
+ } else if (currentProperty == Property::fillColor) {
+ setFillColor(value);
} else {
- LOG_ALWAYS_FATAL("Error setting color property on FullPath: No valid property with id: %d",
- propertyId);
+ LOG_ALWAYS_FATAL("Error setting color property on FullPath: No valid property"
+ " with id: %d", propertyId);
}
}
-void FullPath::setPropertyValue(int propertyId, float value) {
+void FullPath::FullPathProperties::setPropertyValue(int propertyId, float value) {
Property property = static_cast<Property>(propertyId);
switch (property) {
- case Property::StrokeWidth:
+ case Property::strokeWidth:
setStrokeWidth(value);
break;
- case Property::StrokeAlpha:
+ case Property::strokeAlpha:
setStrokeAlpha(value);
break;
- case Property::FillAlpha:
+ case Property::fillAlpha:
setFillAlpha(value);
break;
- case Property::TrimPathStart:
+ case Property::trimPathStart:
setTrimPathStart(value);
break;
- case Property::TrimPathEnd:
+ case Property::trimPathEnd:
setTrimPathEnd(value);
break;
- case Property::TrimPathOffset:
+ case Property::trimPathOffset:
setTrimPathOffset(value);
break;
default:
@@ -311,16 +309,16 @@
}
void ClipPath::drawPath(SkCanvas* outCanvas, SkPath& renderPath,
- float strokeScale, const SkMatrix& matrix){
+ float strokeScale, const SkMatrix& matrix, bool useStagingData){
outCanvas->clipPath(renderPath, SkRegion::kIntersect_Op);
}
Group::Group(const Group& group) : Node(group) {
- mProperties = group.mProperties;
+ mStagingProperties.syncProperties(group.mStagingProperties);
}
void Group::draw(SkCanvas* outCanvas, const SkMatrix& currentMatrix, float scaleX,
- float scaleY) {
+ float scaleY, bool useStagingData) {
// TODO: Try apply the matrix to the canvas instead of passing it down the tree
// Calculate current group's matrix by preConcat the parent's and
@@ -328,14 +326,15 @@
// Basically the Mfinal = Mviewport * M0 * M1 * M2;
// Mi the local matrix at level i of the group tree.
SkMatrix stackedMatrix;
- getLocalMatrix(&stackedMatrix);
+ const GroupProperties& prop = useStagingData ? mStagingProperties : mProperties;
+ getLocalMatrix(&stackedMatrix, prop);
stackedMatrix.postConcat(currentMatrix);
// Save the current clip information, which is local to this group.
outCanvas->save();
// Draw the group tree in the same order as the XML file.
for (auto& child : mChildren) {
- child->draw(outCanvas, stackedMatrix, scaleX, scaleY);
+ child->draw(outCanvas, stackedMatrix, scaleX, scaleY, useStagingData);
}
// Restore the previous clip information.
outCanvas->restore();
@@ -343,96 +342,106 @@
void Group::dump() {
ALOGD("Group %s has %zu children: ", mName.c_str(), mChildren.size());
+ ALOGD("Group translateX, Y : %f, %f, scaleX, Y: %f, %f", mProperties.getTranslateX(),
+ mProperties.getTranslateY(), mProperties.getScaleX(), mProperties.getScaleY());
for (size_t i = 0; i < mChildren.size(); i++) {
mChildren[i]->dump();
}
}
-void Group::updateLocalMatrix(float rotate, float pivotX, float pivotY,
- float scaleX, float scaleY, float translateX, float translateY) {
- setRotation(rotate);
- setPivotX(pivotX);
- setPivotY(pivotY);
- setScaleX(scaleX);
- setScaleY(scaleY);
- setTranslateX(translateX);
- setTranslateY(translateY);
+void Group::syncProperties() {
+ // Copy over the dirty staging properties
+ if (mStagingPropertiesDirty) {
+ mProperties.syncProperties(mStagingProperties);
+ } else {
+ mStagingProperties.syncProperties(mProperties);
+ }
+ mStagingPropertiesDirty = false;
+ for (auto& child : mChildren) {
+ child->syncProperties();
+ }
}
-void Group::getLocalMatrix(SkMatrix* outMatrix) {
+void Group::getLocalMatrix(SkMatrix* outMatrix, const GroupProperties& properties) {
outMatrix->reset();
// TODO: use rotate(mRotate, mPivotX, mPivotY) and scale with pivot point, instead of
// translating to pivot for rotating and scaling, then translating back.
- outMatrix->postTranslate(-mProperties.pivotX, -mProperties.pivotY);
- outMatrix->postScale(mProperties.scaleX, mProperties.scaleY);
- outMatrix->postRotate(mProperties.rotate, 0, 0);
- outMatrix->postTranslate(mProperties.translateX + mProperties.pivotX,
- mProperties.translateY + mProperties.pivotY);
+ outMatrix->postTranslate(-properties.getPivotX(), -properties.getPivotY());
+ outMatrix->postScale(properties.getScaleX(), properties.getScaleY());
+ outMatrix->postRotate(properties.getRotation(), 0, 0);
+ outMatrix->postTranslate(properties.getTranslateX() + properties.getPivotX(),
+ properties.getTranslateY() + properties.getPivotY());
}
void Group::addChild(Node* child) {
mChildren.emplace_back(child);
+ if (mPropertyChangedListener != nullptr) {
+ child->setPropertyChangedListener(mPropertyChangedListener);
+ }
}
-bool Group::getProperties(float* outProperties, int length) {
- int propertyCount = static_cast<int>(Property::Count);
+bool Group::GroupProperties::copyProperties(float* outProperties, int length) const {
+ int propertyCount = static_cast<int>(Property::count);
if (length != propertyCount) {
LOG_ALWAYS_FATAL("Properties needs exactly %d bytes, a byte array of size %d is provided",
propertyCount, length);
return false;
}
- Properties* out = reinterpret_cast<Properties*>(outProperties);
- *out = mProperties;
+
+ PrimitiveFields* out = reinterpret_cast<PrimitiveFields*>(outProperties);
+ *out = mPrimitiveFields;
return true;
}
// TODO: Consider animating the properties as float pointers
-float Group::getPropertyValue(int propertyId) const {
+// Called on render thread
+float Group::GroupProperties::getPropertyValue(int propertyId) const {
Property currentProperty = static_cast<Property>(propertyId);
switch (currentProperty) {
- case Property::Rotate:
- return mProperties.rotate;
- case Property::PivotX:
- return mProperties.pivotX;
- case Property::PivotY:
- return mProperties.pivotY;
- case Property::ScaleX:
- return mProperties.scaleX;
- case Property::ScaleY:
- return mProperties.scaleY;
- case Property::TranslateX:
- return mProperties.translateX;
- case Property::TranslateY:
- return mProperties.translateY;
+ case Property::rotate:
+ return getRotation();
+ case Property::pivotX:
+ return getPivotX();
+ case Property::pivotY:
+ return getPivotY();
+ case Property::scaleX:
+ return getScaleX();
+ case Property::scaleY:
+ return getScaleY();
+ case Property::translateX:
+ return getTranslateX();
+ case Property::translateY:
+ return getTranslateY();
default:
LOG_ALWAYS_FATAL("Invalid property index: %d", propertyId);
return 0;
}
}
-void Group::setPropertyValue(int propertyId, float value) {
+// Called on render thread
+void Group::GroupProperties::setPropertyValue(int propertyId, float value) {
Property currentProperty = static_cast<Property>(propertyId);
switch (currentProperty) {
- case Property::Rotate:
- mProperties.rotate = value;
+ case Property::rotate:
+ setRotation(value);
break;
- case Property::PivotX:
- mProperties.pivotX = value;
+ case Property::pivotX:
+ setPivotX(value);
break;
- case Property::PivotY:
- mProperties.pivotY = value;
+ case Property::pivotY:
+ setPivotY(value);
break;
- case Property::ScaleX:
- mProperties.scaleX = value;
+ case Property::scaleX:
+ setScaleX(value);
break;
- case Property::ScaleY:
- mProperties.scaleY = value;
+ case Property::scaleY:
+ setScaleY(value);
break;
- case Property::TranslateX:
- mProperties.translateX = value;
+ case Property::translateX:
+ setTranslateX(value);
break;
- case Property::TranslateY:
- mProperties.translateY = value;
+ case Property::translateY:
+ setTranslateY(value);
break;
default:
LOG_ALWAYS_FATAL("Invalid property index: %d", propertyId);
@@ -440,7 +449,11 @@
}
bool Group::isValidProperty(int propertyId) {
- return propertyId >= 0 && propertyId < static_cast<int>(Property::Count);
+ return GroupProperties::isValidProperty(propertyId);
+}
+
+bool Group::GroupProperties::isValidProperty(int propertyId) {
+ return propertyId >= 0 && propertyId < static_cast<int>(Property::count);
}
void Tree::draw(Canvas* outCanvas, SkColorFilter* colorFilter,
@@ -449,18 +462,18 @@
// avoid blurry scaling, we have to draw into a bitmap with exact pixel
// size first. This bitmap size is determined by the bounds and the
// canvas scale.
- outCanvas->getMatrix(&mCanvasMatrix);
- mBounds = bounds;
+ SkMatrix canvasMatrix;
+ outCanvas->getMatrix(&canvasMatrix);
float canvasScaleX = 1.0f;
float canvasScaleY = 1.0f;
- if (mCanvasMatrix.getSkewX() == 0 && mCanvasMatrix.getSkewY() == 0) {
+ if (canvasMatrix.getSkewX() == 0 && canvasMatrix.getSkewY() == 0) {
// Only use the scale value when there's no skew or rotation in the canvas matrix.
// TODO: Add a cts test for drawing VD on a canvas with negative scaling factors.
- canvasScaleX = fabs(mCanvasMatrix.getScaleX());
- canvasScaleY = fabs(mCanvasMatrix.getScaleY());
+ canvasScaleX = fabs(canvasMatrix.getScaleX());
+ canvasScaleY = fabs(canvasMatrix.getScaleY());
}
- int scaledWidth = (int) (mBounds.width() * canvasScaleX);
- int scaledHeight = (int) (mBounds.height() * canvasScaleY);
+ int scaledWidth = (int) (bounds.width() * canvasScaleX);
+ int scaledHeight = (int) (bounds.height() * canvasScaleY);
scaledWidth = std::min(Tree::MAX_CACHED_BITMAP_SIZE, scaledWidth);
scaledHeight = std::min(Tree::MAX_CACHED_BITMAP_SIZE, scaledHeight);
@@ -468,63 +481,105 @@
return;
}
- mPaint.setColorFilter(colorFilter);
-
+ mStagingProperties.setScaledSize(scaledWidth, scaledHeight);
int saveCount = outCanvas->save(SaveFlags::MatrixClip);
- outCanvas->translate(mBounds.fLeft, mBounds.fTop);
+ outCanvas->translate(bounds.fLeft, bounds.fTop);
// Handle RTL mirroring.
if (needsMirroring) {
- outCanvas->translate(mBounds.width(), 0);
+ outCanvas->translate(bounds.width(), 0);
outCanvas->scale(-1.0f, 1.0f);
}
+ mStagingProperties.setColorFilter(colorFilter);
// At this point, canvas has been translated to the right position.
// And we use this bound for the destination rect for the drawBitmap, so
// we offset to (0, 0);
- mBounds.offsetTo(0, 0);
- createCachedBitmapIfNeeded(scaledWidth, scaledHeight);
-
+ SkRect tmpBounds = bounds;
+ tmpBounds.offsetTo(0, 0);
+ mStagingProperties.setBounds(tmpBounds);
outCanvas->drawVectorDrawable(this);
-
outCanvas->restoreToCount(saveCount);
}
-SkPaint* Tree::getPaint() {
- SkPaint* paint;
- if (mRootAlpha == 1.0f && mPaint.getColorFilter() == NULL) {
- paint = NULL;
- } else {
- mPaint.setFilterQuality(kLow_SkFilterQuality);
- mPaint.setAlpha(mRootAlpha * 255);
- paint = &mPaint;
+void Tree::drawStaging(Canvas* outCanvas) {
+ bool redrawNeeded = allocateBitmapIfNeeded(&mStagingCache.bitmap,
+ mStagingProperties.getScaledWidth(), mStagingProperties.getScaledHeight());
+ // draw bitmap cache
+ if (redrawNeeded || mStagingCache.dirty) {
+ updateBitmapCache(&mStagingCache.bitmap, true);
+ mStagingCache.dirty = false;
}
- return paint;
+
+ SkPaint tmpPaint;
+ SkPaint* paint = updatePaint(&tmpPaint, &mStagingProperties);
+ outCanvas->drawBitmap(mStagingCache.bitmap, 0, 0,
+ mStagingCache.bitmap.width(), mStagingCache.bitmap.height(),
+ mStagingProperties.getBounds().left(), mStagingProperties.getBounds().top(),
+ mStagingProperties.getBounds().right(), mStagingProperties.getBounds().bottom(), paint);
+}
+
+SkPaint* Tree::getPaint() {
+ return updatePaint(&mPaint, &mProperties);
+}
+
+// Update the given paint with alpha and color filter. Return nullptr if no color filter is
+// specified and root alpha is 1. Otherwise, return updated paint.
+SkPaint* Tree::updatePaint(SkPaint* outPaint, TreeProperties* prop) {
+ if (prop->getRootAlpha() == 1.0f && prop->getColorFilter() == nullptr) {
+ return nullptr;
+ } else {
+ outPaint->setColorFilter(mStagingProperties.getColorFilter());
+ outPaint->setFilterQuality(kLow_SkFilterQuality);
+ outPaint->setAlpha(prop->getRootAlpha() * 255);
+ return outPaint;
+ }
}
const SkBitmap& Tree::getBitmapUpdateIfDirty() {
- mCachedBitmap.eraseColor(SK_ColorTRANSPARENT);
- SkCanvas outCanvas(mCachedBitmap);
- float scaleX = (float) mCachedBitmap.width() / mViewportWidth;
- float scaleY = (float) mCachedBitmap.height() / mViewportHeight;
- mRootNode->draw(&outCanvas, SkMatrix::I(), scaleX, scaleY);
- mCacheDirty = false;
- return mCachedBitmap;
+ bool redrawNeeded = allocateBitmapIfNeeded(&mCache.bitmap, mProperties.getScaledWidth(),
+ mProperties.getScaledHeight());
+ if (redrawNeeded || mCache.dirty) {
+ updateBitmapCache(&mCache.bitmap, false);
+ mCache.dirty = false;
+ }
+ return mCache.bitmap;
}
-void Tree::createCachedBitmapIfNeeded(int width, int height) {
- if (!canReuseBitmap(width, height)) {
+void Tree::updateBitmapCache(SkBitmap* outCache, bool useStagingData) {
+ outCache->eraseColor(SK_ColorTRANSPARENT);
+ SkCanvas outCanvas(*outCache);
+ float viewportWidth = useStagingData ?
+ mStagingProperties.getViewportWidth() : mProperties.getViewportWidth();
+ float viewportHeight = useStagingData ?
+ mStagingProperties.getViewportHeight() : mProperties.getViewportHeight();
+ float scaleX = outCache->width() / viewportWidth;
+ float scaleY = outCache->height() / viewportHeight;
+ mRootNode->draw(&outCanvas, SkMatrix::I(), scaleX, scaleY, useStagingData);
+}
+
+bool Tree::allocateBitmapIfNeeded(SkBitmap* outCache, int width, int height) {
+ if (!canReuseBitmap(*outCache, width, height)) {
SkImageInfo info = SkImageInfo::Make(width, height,
kN32_SkColorType, kPremul_SkAlphaType);
- mCachedBitmap.setInfo(info);
+ outCache->setInfo(info);
// TODO: Count the bitmap cache against app's java heap
- mCachedBitmap.allocPixels(info);
- mCacheDirty = true;
+ outCache->allocPixels(info);
+ return true;
}
+ return false;
}
-bool Tree::canReuseBitmap(int width, int height) {
- return width == mCachedBitmap.width() && height == mCachedBitmap.height();
+bool Tree::canReuseBitmap(const SkBitmap& bitmap, int width, int height) {
+ return width == bitmap.width() && height == bitmap.height();
+}
+
+void Tree::onPropertyChanged(TreeProperties* prop) {
+ if (prop == &mStagingProperties) {
+ mStagingCache.dirty = true;
+ } else {
+ mCache.dirty = true;
+ }
}
}; // namespace VectorDrawable
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index 7a45bf5..e4c7ed7 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -18,9 +18,11 @@
#define ANDROID_HWUI_VPATH_H
#include "hwui/Canvas.h"
+#include "DisplayList.h"
#include <SkBitmap.h>
#include <SkColor.h>
+#include <SkColorFilter.h>
#include <SkCanvas.h>
#include <SkMatrix.h>
#include <SkPaint.h>
@@ -38,8 +40,11 @@
namespace uirenderer {
namespace VectorDrawable {
-#define VD_SET_PROP_WITH_FLAG(field, value, flag) (VD_SET_PROP(field, value) ? (flag = true, true): false);
+#define VD_SET_PROP_WITH_FLAG(field, value, flag) (VD_SET_PROP_AND_NOTIFY(field, value) ? (flag = true, true) : false)
#define VD_SET_PROP(field, value) (value != field ? (field = value, true) : false)
+#define VD_SET_PROP_AND_NOTIFY(field, value) ({ bool retVal = VD_SET_PROP(field, value);\
+ onPropertyChanged(); retVal;})
+#define UPDATE_SKPROP(field, value) ({bool retVal = (field != value); if (field != value) SkRefCnt_SafeAssign(field, value); retVal;})
/* A VectorDrawable is composed of a tree of nodes.
* Each node can be a group node, or a path.
@@ -52,22 +57,65 @@
* / \ |
* Path Path Path
*
+ * VectorDrawables are drawn into bitmap caches first, then the caches are drawn to the given
+ * canvas with root alpha applied. Two caches are maintained for VD, one in UI thread, the other in
+ * Render Thread. A generation id is used to keep track of changes in the vector drawable tree.
+ * Each cache has their own generation id to track whether they are up to date with the latest
+ * change in the tree.
+ *
+ * Any property change to the vector drawable coming from UI thread (such as bulk setters to update
+ * all the properties, and viewport change, etc.) are only modifying the staging properties. The
+ * staging properties will then be marked dirty and will be pushed over to render thread properties
+ * at sync point. If staging properties are not dirty at sync point, we sync backwards by updating
+ * staging properties with render thread properties to reflect the latest animation value.
+ *
*/
+
+class PropertyChangedListener {
+public:
+ PropertyChangedListener(bool* dirty, bool* stagingDirty)
+ : mDirty(dirty), mStagingDirty(stagingDirty) {}
+ void onPropertyChanged() {
+ *mDirty = true;
+ }
+ void onStagingPropertyChanged() {
+ *mStagingDirty = true;
+ }
+private:
+ bool* mDirty;
+ bool* mStagingDirty;
+};
+
class ANDROID_API Node {
public:
+ class Properties {
+ public:
+ Properties(Node* node) : mNode(node) {}
+ inline void onPropertyChanged() {
+ mNode->onPropertyChanged(this);
+ }
+ private:
+ Node* mNode;
+ };
Node(const Node& node) {
mName = node.mName;
}
Node() {}
virtual void draw(SkCanvas* outCanvas, const SkMatrix& currentMatrix,
- float scaleX, float scaleY) = 0;
+ float scaleX, float scaleY, bool useStagingData) = 0;
virtual void dump() = 0;
void setName(const char* name) {
mName = name;
}
+ virtual void setPropertyChangedListener(PropertyChangedListener* listener) {
+ mPropertyChangedListener = listener;
+ }
+ virtual void onPropertyChanged(Properties* properties) = 0;
virtual ~Node(){}
+ virtual void syncProperties() = 0;
protected:
std::string mName;
+ PropertyChangedListener* mPropertyChangedListener = nullptr;
};
class ANDROID_API Path : public Node {
@@ -81,150 +129,267 @@
&& points == data.points;
}
};
- Path(const Data& nodes);
+
+ class PathProperties : public Properties {
+ public:
+ PathProperties(Node* node) : Properties(node) {}
+ void syncProperties(const PathProperties& prop) {
+ mData = prop.mData;
+ onPropertyChanged();
+ }
+ void setData(const Data& data) {
+ // Updates the path data. Note that we don't generate a new Skia path right away
+ // because there are cases where the animation is changing the path data, but the view
+ // that hosts the VD has gone off screen, in which case we won't even draw. So we
+ // postpone the Skia path generation to the draw time.
+ if (data == mData) {
+ return;
+ }
+ mData = data;
+ onPropertyChanged();
+
+ }
+ const Data& getData() const {
+ return mData;
+ }
+ private:
+ Data mData;
+ };
+
Path(const Path& path);
Path(const char* path, size_t strLength);
Path() {}
+
void dump() override;
- bool canMorph(const Data& path);
- bool canMorph(const Path& path);
void draw(SkCanvas* outCanvas, const SkMatrix& groupStackedMatrix,
- float scaleX, float scaleY) override;
- void setPath(const char* path, size_t strLength);
- void setPathData(const Data& data);
+ float scaleX, float scaleY, bool useStagingData) override;
static float getMatrixScale(const SkMatrix& groupStackedMatrix);
+ virtual void syncProperties() override;
+ virtual void onPropertyChanged(Properties* prop) override {
+ if (prop == &mStagingProperties) {
+ mStagingPropertiesDirty = true;
+ if (mPropertyChangedListener) {
+ mPropertyChangedListener->onStagingPropertyChanged();
+ }
+ } else if (prop == &mProperties){
+ mSkPathDirty = true;
+ if (mPropertyChangedListener) {
+ mPropertyChangedListener->onPropertyChanged();
+ }
+ }
+ }
+ PathProperties* mutateStagingProperties() { return &mStagingProperties; }
+ const PathProperties* stagingProperties() { return &mStagingProperties; }
+
+ // This should only be called from animations on RT
+ PathProperties* mutateProperties() { return &mProperties; }
protected:
virtual const SkPath& getUpdatedPath();
+ virtual void getStagingPath(SkPath* outPath);
virtual void drawPath(SkCanvas *outCanvas, SkPath& renderPath,
- float strokeScale, const SkMatrix& matrix) = 0;
- Data mData;
- SkPath mSkPath;
+ float strokeScale, const SkMatrix& matrix, bool useStagingData) = 0;
+
+ // Internal data, render thread only.
bool mSkPathDirty = true;
+ SkPath mSkPath;
+
+private:
+ PathProperties mProperties = PathProperties(this);
+ PathProperties mStagingProperties = PathProperties(this);
+ bool mStagingPropertiesDirty = true;
};
class ANDROID_API FullPath: public Path {
public:
+ class FullPathProperties : public Properties {
+ public:
+ struct PrimitiveFields {
+ float strokeWidth = 0;
+ SkColor strokeColor = SK_ColorTRANSPARENT;
+ float strokeAlpha = 1;
+ SkColor fillColor = SK_ColorTRANSPARENT;
+ float fillAlpha = 1;
+ float trimPathStart = 0;
+ float trimPathEnd = 1;
+ float trimPathOffset = 0;
+ int32_t strokeLineCap = SkPaint::Cap::kButt_Cap;
+ int32_t strokeLineJoin = SkPaint::Join::kMiter_Join;
+ float strokeMiterLimit = 4;
+ int fillType = 0; /* non-zero or kWinding_FillType in Skia */
+ };
+ FullPathProperties(Node* mNode) : Properties(mNode), mTrimDirty(false) {}
+ void syncProperties(const FullPathProperties& prop) {
+ mPrimitiveFields = prop.mPrimitiveFields;
+ mTrimDirty = true;
+ fillGradient.reset(prop.fillGradient);
+ strokeGradient.reset(prop.strokeGradient);
+ onPropertyChanged();
+ }
+ void setFillGradient(SkShader* gradient) {
+ if(fillGradient != gradient){
+ fillGradient.reset(gradient);
+ onPropertyChanged();
+ }
+ }
+ void setStrokeGradient(SkShader* gradient) {
+ if(strokeGradient != gradient){
+ strokeGradient.reset(gradient);
+ onPropertyChanged();
+ }
+ }
+ SkShader* getFillGradient() const {
+ return fillGradient;
+ }
+ SkShader* getStrokeGradient() const {
+ return strokeGradient;
+ }
+ float getStrokeWidth() const{
+ return mPrimitiveFields.strokeWidth;
+ }
+ void setStrokeWidth(float strokeWidth) {
+ VD_SET_PROP_AND_NOTIFY(strokeWidth, strokeWidth);
+ }
+ SkColor getStrokeColor() const{
+ return mPrimitiveFields.strokeColor;
+ }
+ void setStrokeColor(SkColor strokeColor) {
+ VD_SET_PROP_AND_NOTIFY(mPrimitiveFields.strokeColor, strokeColor);
+ }
+ float getStrokeAlpha() const{
+ return mPrimitiveFields.strokeAlpha;
+ }
+ void setStrokeAlpha(float strokeAlpha) {
+ VD_SET_PROP_AND_NOTIFY(mPrimitiveFields.strokeAlpha, strokeAlpha);
+ }
+ SkColor getFillColor() const {
+ return mPrimitiveFields.fillColor;
+ }
+ void setFillColor(SkColor fillColor) {
+ VD_SET_PROP_AND_NOTIFY(mPrimitiveFields.fillColor, fillColor);
+ }
+ float getFillAlpha() const{
+ return mPrimitiveFields.fillAlpha;
+ }
+ void setFillAlpha(float fillAlpha) {
+ VD_SET_PROP_AND_NOTIFY(mPrimitiveFields.fillAlpha, fillAlpha);
+ }
+ float getTrimPathStart() const{
+ return mPrimitiveFields.trimPathStart;
+ }
+ void setTrimPathStart(float trimPathStart) {
+ VD_SET_PROP_WITH_FLAG(mPrimitiveFields.trimPathStart, trimPathStart, mTrimDirty);
+ }
+ float getTrimPathEnd() const{
+ return mPrimitiveFields.trimPathEnd;
+ }
+ void setTrimPathEnd(float trimPathEnd) {
+ VD_SET_PROP_WITH_FLAG(mPrimitiveFields.trimPathEnd, trimPathEnd, mTrimDirty);
+ }
+ float getTrimPathOffset() const{
+ return mPrimitiveFields.trimPathOffset;
+ }
+ void setTrimPathOffset(float trimPathOffset) {
+ VD_SET_PROP_WITH_FLAG(mPrimitiveFields.trimPathOffset, trimPathOffset, mTrimDirty);
+ }
-struct Properties {
- float strokeWidth = 0;
- SkColor strokeColor = SK_ColorTRANSPARENT;
- float strokeAlpha = 1;
- SkColor fillColor = SK_ColorTRANSPARENT;
- float fillAlpha = 1;
- float trimPathStart = 0;
- float trimPathEnd = 1;
- float trimPathOffset = 0;
- int32_t strokeLineCap = SkPaint::Cap::kButt_Cap;
- int32_t strokeLineJoin = SkPaint::Join::kMiter_Join;
- float strokeMiterLimit = 4;
- int fillType = 0; /* non-zero or kWinding_FillType in Skia */
-};
+ float getStrokeMiterLimit() const {
+ return mPrimitiveFields.strokeMiterLimit;
+ }
+ float getStrokeLineCap() const {
+ return mPrimitiveFields.strokeLineCap;
+ }
+ float getStrokeLineJoin() const {
+ return mPrimitiveFields.strokeLineJoin;
+ }
+ float getFillType() const {
+ return mPrimitiveFields.fillType;
+ }
+ bool copyProperties(int8_t* outProperties, int length) const;
+ void updateProperties(float strokeWidth, SkColor strokeColor, float strokeAlpha,
+ SkColor fillColor, float fillAlpha, float trimPathStart, float trimPathEnd,
+ float trimPathOffset, float strokeMiterLimit, int strokeLineCap, int strokeLineJoin,
+ int fillType) {
+ mPrimitiveFields.strokeWidth = strokeWidth;
+ mPrimitiveFields.strokeColor = strokeColor;
+ mPrimitiveFields.strokeAlpha = strokeAlpha;
+ mPrimitiveFields.fillColor = fillColor;
+ mPrimitiveFields.fillAlpha = fillAlpha;
+ mPrimitiveFields.trimPathStart = trimPathStart;
+ mPrimitiveFields.trimPathEnd = trimPathEnd;
+ mPrimitiveFields.trimPathOffset = trimPathOffset;
+ mPrimitiveFields.strokeMiterLimit = strokeMiterLimit;
+ mPrimitiveFields.strokeLineCap = strokeLineCap;
+ mPrimitiveFields.strokeLineJoin = strokeLineJoin;
+ mPrimitiveFields.fillType = fillType;
+ mTrimDirty = true;
+ onPropertyChanged();
+ }
+ // Set property values during animation
+ void setColorPropertyValue(int propertyId, int32_t value);
+ void setPropertyValue(int propertyId, float value);
+ bool mTrimDirty;
+ private:
+ enum class Property {
+ strokeWidth = 0,
+ strokeColor,
+ strokeAlpha,
+ fillColor,
+ fillAlpha,
+ trimPathStart,
+ trimPathEnd,
+ trimPathOffset,
+ strokeLineCap,
+ strokeLineJoin,
+ strokeMiterLimit,
+ fillType,
+ count,
+ };
+ PrimitiveFields mPrimitiveFields;
+ SkAutoTUnref<SkShader> fillGradient;
+ SkAutoTUnref<SkShader> strokeGradient;
+ };
+ // Called from UI thread
FullPath(const FullPath& path); // for cloning
FullPath(const char* path, size_t strLength) : Path(path, strLength) {}
FullPath() : Path() {}
- FullPath(const Data& nodes) : Path(nodes) {}
+ void dump() override;
+ FullPathProperties* mutateStagingProperties() { return &mStagingProperties; }
+ const FullPathProperties* stagingProperties() { return &mStagingProperties; }
- ~FullPath() {
- SkSafeUnref(mFillGradient);
- SkSafeUnref(mStrokeGradient);
- }
+ // This should only be called from animations on RT
+ FullPathProperties* mutateProperties() { return &mProperties; }
- void updateProperties(float strokeWidth, SkColor strokeColor,
- float strokeAlpha, SkColor fillColor, float fillAlpha,
- float trimPathStart, float trimPathEnd, float trimPathOffset,
- float strokeMiterLimit, int strokeLineCap, int strokeLineJoin, int fillType);
- // TODO: Cleanup: Remove the setter and getters below, and their counterparts in java and JNI
- float getStrokeWidth() {
- return mProperties.strokeWidth;
+ virtual void syncProperties() override;
+ virtual void onPropertyChanged(Properties* properties) override {
+ Path::onPropertyChanged(properties);
+ if (properties == &mStagingProperties) {
+ mStagingPropertiesDirty = true;
+ if (mPropertyChangedListener) {
+ mPropertyChangedListener->onStagingPropertyChanged();
+ }
+ } else if (properties == &mProperties) {
+ if (mPropertyChangedListener) {
+ mPropertyChangedListener->onPropertyChanged();
+ }
+ }
}
- void setStrokeWidth(float strokeWidth) {
- mProperties.strokeWidth = strokeWidth;
- }
- SkColor getStrokeColor() {
- return mProperties.strokeColor;
- }
- void setStrokeColor(SkColor strokeColor) {
- mProperties.strokeColor = strokeColor;
- }
- float getStrokeAlpha() {
- return mProperties.strokeAlpha;
- }
- void setStrokeAlpha(float strokeAlpha) {
- mProperties.strokeAlpha = strokeAlpha;
- }
- SkColor getFillColor() {
- return mProperties.fillColor;
- }
- void setFillColor(SkColor fillColor) {
- mProperties.fillColor = fillColor;
- }
- float getFillAlpha() {
- return mProperties.fillAlpha;
- }
- void setFillAlpha(float fillAlpha) {
- mProperties.fillAlpha = fillAlpha;
- }
- float getTrimPathStart() {
- return mProperties.trimPathStart;
- }
- void setTrimPathStart(float trimPathStart) {
- VD_SET_PROP_WITH_FLAG(mProperties.trimPathStart, trimPathStart, mTrimDirty);
- }
- float getTrimPathEnd() {
- return mProperties.trimPathEnd;
- }
- void setTrimPathEnd(float trimPathEnd) {
- VD_SET_PROP_WITH_FLAG(mProperties.trimPathEnd, trimPathEnd, mTrimDirty);
- }
- float getTrimPathOffset() {
- return mProperties.trimPathOffset;
- }
- void setTrimPathOffset(float trimPathOffset) {
- VD_SET_PROP_WITH_FLAG(mProperties.trimPathOffset, trimPathOffset, mTrimDirty);
- }
- bool getProperties(int8_t* outProperties, int length);
- void setColorPropertyValue(int propertyId, int32_t value);
- void setPropertyValue(int propertyId, float value);
-
- void setFillGradient(SkShader* fillGradient) {
- SkRefCnt_SafeAssign(mFillGradient, fillGradient);
- };
- void setStrokeGradient(SkShader* strokeGradient) {
- SkRefCnt_SafeAssign(mStrokeGradient, strokeGradient);
- };
-
protected:
const SkPath& getUpdatedPath() override;
+ void getStagingPath(SkPath* outPath) override;
void drawPath(SkCanvas* outCanvas, SkPath& renderPath,
- float strokeScale, const SkMatrix& matrix) override;
-
+ float strokeScale, const SkMatrix& matrix, bool useStagingData) override;
private:
- enum class Property {
- StrokeWidth = 0,
- StrokeColor,
- StrokeAlpha,
- FillColor,
- FillAlpha,
- TrimPathStart,
- TrimPathEnd,
- TrimPathOffset,
- StrokeLineCap,
- StrokeLineJoin,
- StrokeMiterLimit,
- FillType,
- Count,
- };
- // Applies trimming to the specified path.
- void applyTrim();
- Properties mProperties;
- bool mTrimDirty = true;
+
+ FullPathProperties mProperties = FullPathProperties(this);
+ FullPathProperties mStagingProperties = FullPathProperties(this);
+ bool mStagingPropertiesDirty = true;
+
+ // Intermediate data for drawing, render thread only
SkPath mTrimmedSkPath;
- SkPaint mPaint;
- SkShader* mStrokeGradient = nullptr;
- SkShader* mFillGradient = nullptr;
+
};
class ANDROID_API ClipPath: public Path {
@@ -232,143 +397,316 @@
ClipPath(const ClipPath& path) : Path(path) {}
ClipPath(const char* path, size_t strLength) : Path(path, strLength) {}
ClipPath() : Path() {}
- ClipPath(const Data& nodes) : Path(nodes) {}
protected:
void drawPath(SkCanvas* outCanvas, SkPath& renderPath,
- float strokeScale, const SkMatrix& matrix) override;
+ float strokeScale, const SkMatrix& matrix, bool useStagingData) override;
};
class ANDROID_API Group: public Node {
public:
- struct Properties {
- float rotate = 0;
- float pivotX = 0;
- float pivotY = 0;
- float scaleX = 1;
- float scaleY = 1;
- float translateX = 0;
- float translateY = 0;
+ class GroupProperties : public Properties {
+ public:
+ GroupProperties(Node* mNode) : Properties(mNode) {}
+ struct PrimitiveFields {
+ float rotate = 0;
+ float pivotX = 0;
+ float pivotY = 0;
+ float scaleX = 1;
+ float scaleY = 1;
+ float translateX = 0;
+ float translateY = 0;
+ } mPrimitiveFields;
+ void syncProperties(const GroupProperties& prop) {
+ mPrimitiveFields = prop.mPrimitiveFields;
+ onPropertyChanged();
+ }
+ float getRotation() const {
+ return mPrimitiveFields.rotate;
+ }
+ void setRotation(float rotation) {
+ VD_SET_PROP_AND_NOTIFY(mPrimitiveFields.rotate, rotation);
+ }
+ float getPivotX() const {
+ return mPrimitiveFields.pivotX;
+ }
+ void setPivotX(float pivotX) {
+ VD_SET_PROP_AND_NOTIFY(mPrimitiveFields.pivotX, pivotX);
+ }
+ float getPivotY() const {
+ return mPrimitiveFields.pivotY;
+ }
+ void setPivotY(float pivotY) {
+ VD_SET_PROP_AND_NOTIFY(mPrimitiveFields.pivotY, pivotY);
+ }
+ float getScaleX() const {
+ return mPrimitiveFields.scaleX;
+ }
+ void setScaleX(float scaleX) {
+ VD_SET_PROP_AND_NOTIFY(mPrimitiveFields.scaleX, scaleX);
+ }
+ float getScaleY() const {
+ return mPrimitiveFields.scaleY;
+ }
+ void setScaleY(float scaleY) {
+ VD_SET_PROP_AND_NOTIFY(mPrimitiveFields.scaleY, scaleY);
+ }
+ float getTranslateX() const {
+ return mPrimitiveFields.translateX;
+ }
+ void setTranslateX(float translateX) {
+ VD_SET_PROP_AND_NOTIFY(mPrimitiveFields.translateX, translateX);
+ }
+ float getTranslateY() const {
+ return mPrimitiveFields.translateY;
+ }
+ void setTranslateY(float translateY) {
+ VD_SET_PROP_AND_NOTIFY(translateY, translateY);
+ }
+ void updateProperties(float rotate, float pivotX, float pivotY,
+ float scaleX, float scaleY, float translateX, float translateY) {
+ mPrimitiveFields.rotate = rotate;
+ mPrimitiveFields.pivotX = pivotX;
+ mPrimitiveFields.pivotY = pivotY;
+ mPrimitiveFields.scaleX = scaleX;
+ mPrimitiveFields.scaleY = scaleY;
+ mPrimitiveFields.translateX = translateX;
+ mPrimitiveFields.translateY = translateY;
+ onPropertyChanged();
+ }
+ void setPropertyValue(int propertyId, float value);
+ float getPropertyValue(int propertyId) const;
+ bool copyProperties(float* outProperties, int length) const;
+ static bool isValidProperty(int propertyId);
+ private:
+ enum class Property {
+ rotate = 0,
+ pivotX,
+ pivotY,
+ scaleX,
+ scaleY,
+ translateX,
+ translateY,
+ // Count of the properties, must be at the end.
+ count,
+ };
};
+
Group(const Group& group);
Group() {}
- float getRotation() {
- return mProperties.rotate;
- }
- void setRotation(float rotation) {
- mProperties.rotate = rotation;
- }
- float getPivotX() {
- return mProperties.pivotX;
- }
- void setPivotX(float pivotX) {
- mProperties.pivotX = pivotX;
- }
- float getPivotY() {
- return mProperties.pivotY;
- }
- void setPivotY(float pivotY) {
- mProperties.pivotY = pivotY;
- }
- float getScaleX() {
- return mProperties.scaleX;
- }
- void setScaleX(float scaleX) {
- mProperties.scaleX = scaleX;
- }
- float getScaleY() {
- return mProperties.scaleY;
- }
- void setScaleY(float scaleY) {
- mProperties.scaleY = scaleY;
- }
- float getTranslateX() {
- return mProperties.translateX;
- }
- void setTranslateX(float translateX) {
- mProperties.translateX = translateX;
- }
- float getTranslateY() {
- return mProperties.translateY;
- }
- void setTranslateY(float translateY) {
- mProperties.translateY = translateY;
- }
- virtual void draw(SkCanvas* outCanvas, const SkMatrix& currentMatrix,
- float scaleX, float scaleY) override;
- void updateLocalMatrix(float rotate, float pivotX, float pivotY,
- float scaleX, float scaleY, float translateX, float translateY);
- void getLocalMatrix(SkMatrix* outMatrix);
void addChild(Node* child);
+ virtual void setPropertyChangedListener(PropertyChangedListener* listener) override {
+ Node::setPropertyChangedListener(listener);
+ for (auto& child : mChildren) {
+ child->setPropertyChangedListener(listener);
+ }
+ }
+ virtual void syncProperties() override;
+ GroupProperties* mutateStagingProperties() { return &mStagingProperties; }
+ const GroupProperties* stagingProperties() { return &mStagingProperties; }
+
+ // This should only be called from animations on RT
+ GroupProperties* mutateProperties() { return &mProperties; }
+
+ // Methods below could be called from either UI thread or Render Thread.
+ virtual void draw(SkCanvas* outCanvas, const SkMatrix& currentMatrix,
+ float scaleX, float scaleY, bool useStagingData) override;
+ void getLocalMatrix(SkMatrix* outMatrix, const GroupProperties& properties);
void dump() override;
- bool getProperties(float* outProperties, int length);
- float getPropertyValue(int propertyId) const;
- void setPropertyValue(int propertyId, float value);
static bool isValidProperty(int propertyId);
+ virtual void onPropertyChanged(Properties* properties) override {
+ if (properties == &mStagingProperties) {
+ mStagingPropertiesDirty = true;
+ if (mPropertyChangedListener) {
+ mPropertyChangedListener->onStagingPropertyChanged();
+ }
+ } else {
+ if (mPropertyChangedListener) {
+ mPropertyChangedListener->onPropertyChanged();
+ }
+ }
+ }
+
private:
- enum class Property {
- Rotate = 0,
- PivotX,
- PivotY,
- ScaleX,
- ScaleY,
- TranslateX,
- TranslateY,
- // Count of the properties, must be at the end.
- Count,
- };
+ GroupProperties mProperties = GroupProperties(this);
+ GroupProperties mStagingProperties = GroupProperties(this);
+ bool mStagingPropertiesDirty = true;
std::vector< std::unique_ptr<Node> > mChildren;
- Properties mProperties;
};
class ANDROID_API Tree : public VirtualLightRefBase {
public:
- Tree(Group* rootNode) : mRootNode(rootNode) {}
+ Tree(Group* rootNode) : mRootNode(rootNode) {
+ mRootNode->setPropertyChangedListener(&mPropertyChangedListener);
+ }
void draw(Canvas* outCanvas, SkColorFilter* colorFilter,
const SkRect& bounds, bool needsMirroring, bool canReuseCache);
+ void drawStaging(Canvas* canvas);
const SkBitmap& getBitmapUpdateIfDirty();
- void createCachedBitmapIfNeeded(int width, int height);
- bool canReuseBitmap(int width, int height);
void setAllowCaching(bool allowCaching) {
mAllowCaching = allowCaching;
}
- bool setRootAlpha(float rootAlpha) {
- return VD_SET_PROP(mRootAlpha, rootAlpha);
+ SkPaint* getPaint();
+ void syncProperties() {
+ if (mStagingProperties.mNonAnimatablePropertiesDirty) {
+ mProperties.syncNonAnimatableProperties(mStagingProperties);
+ mStagingProperties.mNonAnimatablePropertiesDirty = false;
+ }
+
+ if (mStagingProperties.mAnimatablePropertiesDirty) {
+ mProperties.syncAnimatableProperties(mStagingProperties);
+ } else {
+ mStagingProperties.syncAnimatableProperties(mProperties);
+ }
+ mStagingProperties.mAnimatablePropertiesDirty = false;
+ mRootNode->syncProperties();
}
- float getRootAlpha() {
- return mRootAlpha;
- }
- void setViewportSize(float viewportWidth, float viewportHeight) {
- mViewportWidth = viewportWidth;
- mViewportHeight = viewportHeight;
- }
- SkPaint* getPaint();
- const SkRect& getBounds() const {
- return mBounds;
- }
+ class TreeProperties {
+ public:
+ TreeProperties(Tree* tree) : mTree(tree) {}
+ // Properties that can only be modified by UI thread, therefore sync should
+ // only go from UI to RT
+ struct NonAnimatableProperties {
+ float viewportWidth = 0;
+ float viewportHeight = 0;
+ SkRect bounds;
+ int scaledWidth = 0;
+ int scaledHeight = 0;
+ SkColorFilter* colorFilter = nullptr;
+ ~NonAnimatableProperties() {
+ SkSafeUnref(colorFilter);
+ }
+ } mNonAnimatableProperties;
+ bool mNonAnimatablePropertiesDirty = true;
+
+ float mRootAlpha = 1.0f;
+ bool mAnimatablePropertiesDirty = true;
+
+ void syncNonAnimatableProperties(const TreeProperties& prop) {
+ // Copy over the data that can only be changed in UI thread
+ if (mNonAnimatableProperties.colorFilter != prop.mNonAnimatableProperties.colorFilter) {
+ SkRefCnt_SafeAssign(mNonAnimatableProperties.colorFilter,
+ prop.mNonAnimatableProperties.colorFilter);
+ }
+ mNonAnimatableProperties = prop.mNonAnimatableProperties;
+ }
+
+ void setViewportSize(float width, float height) {
+ if (mNonAnimatableProperties.viewportWidth != width
+ || mNonAnimatableProperties.viewportHeight != height) {
+ mNonAnimatablePropertiesDirty = true;
+ mNonAnimatableProperties.viewportWidth = width;
+ mNonAnimatableProperties.viewportHeight = height;
+ mTree->onPropertyChanged(this);
+ }
+ }
+ void setBounds(const SkRect& bounds) {
+ if (mNonAnimatableProperties.bounds != bounds) {
+ mNonAnimatableProperties.bounds = bounds;
+ mNonAnimatablePropertiesDirty = true;
+ mTree->onPropertyChanged(this);
+ }
+ }
+
+ void setScaledSize(int width, int height) {
+ if (mNonAnimatableProperties.scaledWidth != width
+ || mNonAnimatableProperties.scaledHeight != height) {
+ mNonAnimatableProperties.scaledWidth = width;
+ mNonAnimatableProperties.scaledHeight = height;
+ mNonAnimatablePropertiesDirty = true;
+ mTree->onPropertyChanged(this);
+ }
+ }
+ void setColorFilter(SkColorFilter* filter) {
+ if (UPDATE_SKPROP(mNonAnimatableProperties.colorFilter, filter)) {
+ mNonAnimatablePropertiesDirty = true;
+ mTree->onPropertyChanged(this);
+ }
+ }
+ SkColorFilter* getColorFilter() const{
+ return mNonAnimatableProperties.colorFilter;
+ }
+
+ float getViewportWidth() const {
+ return mNonAnimatableProperties.viewportWidth;
+ }
+ float getViewportHeight() const {
+ return mNonAnimatableProperties.viewportHeight;
+ }
+ float getScaledWidth() const {
+ return mNonAnimatableProperties.scaledWidth;
+ }
+ float getScaledHeight() const {
+ return mNonAnimatableProperties.scaledHeight;
+ }
+ void syncAnimatableProperties(const TreeProperties& prop) {
+ mRootAlpha = prop.mRootAlpha;
+ }
+ bool setRootAlpha(float rootAlpha) {
+ if (rootAlpha != mRootAlpha) {
+ mAnimatablePropertiesDirty = true;
+ mRootAlpha = rootAlpha;
+ mTree->onPropertyChanged(this);
+ return true;
+ }
+ return false;
+ }
+ float getRootAlpha() const { return mRootAlpha;}
+ const SkRect& getBounds() const {
+ return mNonAnimatableProperties.bounds;
+ }
+ Tree* mTree;
+ };
+ void onPropertyChanged(TreeProperties* prop);
+ TreeProperties* mutateStagingProperties() { return &mStagingProperties; }
+ const TreeProperties* stagingProperties() { return &mStagingProperties; }
+ PushStagingFunctor* getFunctor() { return &mFunctor;}
+
+ // This should only be called from animations on RT
+ TreeProperties* mutateProperties() { return &mProperties; }
private:
+ class VectorDrawableFunctor : public PushStagingFunctor {
+ public:
+ VectorDrawableFunctor(Tree* tree) : mTree(tree) {}
+ virtual void operator ()() {
+ mTree->syncProperties();
+ }
+ private:
+ Tree* mTree;
+ };
+
+ SkPaint* updatePaint(SkPaint* outPaint, TreeProperties* prop);
+ bool allocateBitmapIfNeeded(SkBitmap* outCache, int width, int height);
+ bool canReuseBitmap(const SkBitmap&, int width, int height);
+ void updateBitmapCache(SkBitmap* outCache, bool useStagingData);
// Cap the bitmap size, such that it won't hurt the performance too much
// and it won't crash due to a very large scale.
// The drawable will look blurry above this size.
const static int MAX_CACHED_BITMAP_SIZE;
- bool mCacheDirty = true;
bool mAllowCaching = true;
- float mViewportWidth = 0;
- float mViewportHeight = 0;
- float mRootAlpha = 1.0f;
-
std::unique_ptr<Group> mRootNode;
- SkRect mBounds;
- SkMatrix mCanvasMatrix;
- SkPaint mPaint;
- SkPathMeasure mPathMeasure;
- SkBitmap mCachedBitmap;
+ TreeProperties mProperties = TreeProperties(this);
+ TreeProperties mStagingProperties = TreeProperties(this);
+
+ VectorDrawableFunctor mFunctor = VectorDrawableFunctor(this);
+
+ SkPaint mPaint;
+ struct Cache {
+ SkBitmap bitmap;
+ bool dirty = true;
+ };
+
+ Cache mStagingCache;
+ Cache mCache;
+
+ PropertyChangedListener mPropertyChangedListener
+ = PropertyChangedListener(&mCache.dirty, &mStagingCache.dirty);
};
} // namespace VectorDrawable
diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp
index 9a825fd..8e04c87 100644
--- a/libs/hwui/font/Font.cpp
+++ b/libs/hwui/font/Font.cpp
@@ -356,8 +356,6 @@
}
void Font::precache(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs) {
- ATRACE_NAME("Precache Glyphs");
-
if (numGlyphs == 0 || glyphs == nullptr) {
return;
}
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index 8c3eea3..7bfa15a 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -166,6 +166,11 @@
bounds.offset(x, y);
}
+ // Set align to left for drawing, as we don't want individual
+ // glyphs centered or right-aligned; the offset above takes
+ // care of all alignment.
+ paint.setTextAlign(Paint::kLeft_Align);
+
DrawTextFunctor f(layout, this, glyphs.get(), pos.get(),
paint, x, y, bounds, layout.getAdvance());
MinikinUtils::forFontRun(layout, &paint, f);
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index dc669f0..5dbda43 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -53,6 +53,7 @@
} // namespace SaveFlags
namespace uirenderer {
+class SkiaCanvasProxy;
namespace VectorDrawable {
class Tree;
};
@@ -205,19 +206,6 @@
float dstLeft, float dstTop, float dstRight, float dstBottom,
const SkPaint* paint) = 0;
- // Text
- /**
- * drawText: count is of glyphs
- * totalAdvance: used to define width of text decorations (underlines, strikethroughs).
- */
- virtual void drawGlyphs(const uint16_t* glyphs, const float* positions, int count,
- const SkPaint& paint, float x, float y,
- float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
- float totalAdvance) = 0;
- /** drawTextOnPath: count is of glyphs */
- virtual void drawGlyphsOnPath(const uint16_t* glyphs, int count, const SkPath& path,
- float hOffset, float vOffset, const SkPaint& paint) = 0;
-
/**
* Specifies if the positions passed to ::drawText are absolute or relative
* to the (x,y) value provided.
@@ -244,6 +232,22 @@
protected:
void drawTextDecorations(float x, float y, float length, const SkPaint& paint);
+
+ /**
+ * drawText: count is of glyphs
+ * totalAdvance: used to define width of text decorations (underlines, strikethroughs).
+ */
+ virtual void drawGlyphs(const uint16_t* glyphs, const float* positions, int count,
+ const SkPaint& paint, float x, float y,
+ float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
+ float totalAdvance) = 0;
+ /** drawTextOnPath: count is of glyphs */
+ virtual void drawGlyphsOnPath(const uint16_t* glyphs, int count, const SkPath& path,
+ float hOffset, float vOffset, const SkPaint& paint) = 0;
+
+ friend class DrawTextFunctor;
+ friend class DrawTextOnPathFunctor;
+ friend class uirenderer::SkiaCanvasProxy;
};
}; // namespace android
diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h
index 69c321c..9599c30 100644
--- a/libs/hwui/hwui/Paint.h
+++ b/libs/hwui/hwui/Paint.h
@@ -30,6 +30,7 @@
public:
Paint();
Paint(const Paint& paint);
+ Paint(const SkPaint& paint);
~Paint();
Paint& operator=(const Paint& other);
diff --git a/libs/hwui/hwui/PaintImpl.cpp b/libs/hwui/hwui/PaintImpl.cpp
index 1172a0e..b27672c 100644
--- a/libs/hwui/hwui/PaintImpl.cpp
+++ b/libs/hwui/hwui/PaintImpl.cpp
@@ -29,6 +29,11 @@
mHyphenEdit(paint.mHyphenEdit) {
}
+Paint::Paint(const SkPaint& paint) : SkPaint(paint),
+ mLetterSpacing(0), mFontFeatureSettings(), mMinikinLangListId(0),
+ mFontVariant(VARIANT_DEFAULT) {
+}
+
Paint::~Paint() {
}
diff --git a/libs/hwui/renderstate/OffscreenBufferPool.cpp b/libs/hwui/renderstate/OffscreenBufferPool.cpp
index bb1a044..73b6c02 100644
--- a/libs/hwui/renderstate/OffscreenBufferPool.cpp
+++ b/libs/hwui/renderstate/OffscreenBufferPool.cpp
@@ -127,7 +127,7 @@
}
void OffscreenBufferPool::clear() {
- for (auto entry : mPool) {
+ for (auto& entry : mPool) {
delete entry.layer;
}
mPool.clear();
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index eee5278..63fa788 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -86,10 +86,12 @@
freePrefetechedLayers();
destroyHardwareResources();
mAnimationContext->destroy();
+#if !HWUI_NEW_OPS
if (mCanvas) {
delete mCanvas;
mCanvas = nullptr;
}
+#endif
}
void CanvasContext::setSurface(Surface* surface) {
@@ -574,7 +576,7 @@
static void destroyPrefetechedNode(RenderNode* node) {
ALOGW("Incorrectly called buildLayer on View: %s, destroying layer...", node->getName());
- node->destroyHardwareResources();
+ node->destroyHardwareResources(nullptr);
node->decStrong(nullptr);
}
@@ -587,9 +589,11 @@
void CanvasContext::buildLayer(RenderNode* node) {
ATRACE_CALL();
- if (!mEglManager.hasEglContext() || !mCanvas) {
- return;
- }
+ if (!mEglManager.hasEglContext()) return;
+#if !HWUI_NEW_OPS
+ if (!mCanvas) return;
+#endif
+
// buildLayer() will leave the tree in an unknown state, so we must stop drawing
stopDrawing();
@@ -609,7 +613,15 @@
node->setPropertyFieldsDirty(RenderNode::GENERIC);
#if HWUI_NEW_OPS
- // TODO: support buildLayer
+ static const std::vector< sp<RenderNode> > emptyNodeList;
+ auto& caches = Caches::getInstance();
+ FrameBuilder frameBuilder(mLayerUpdateQueue, SkRect::MakeWH(1, 1), 1, 1,
+ emptyNodeList, mLightGeometry, mContentDrawBounds, caches);
+ mLayerUpdateQueue.clear();
+ BakedOpRenderer renderer(caches, mRenderThread.renderState(),
+ mOpaque, mLightInfo);
+ LOG_ALWAYS_FATAL_IF(renderer.didDraw(), "shouldn't draw in buildlayer case");
+ frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
#else
mCanvas->markLayersAsBuildLayers();
mCanvas->flushLayerUpdates();
@@ -629,7 +641,7 @@
if (mEglManager.hasEglContext()) {
freePrefetechedLayers();
for (const sp<RenderNode>& node : mRenderNodes) {
- node->destroyHardwareResources();
+ node->destroyHardwareResources(nullptr);
}
Caches& caches = Caches::getInstance();
// Make sure to release all the textures we were owning as there won't
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 6706c30..6d0889e 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -196,10 +196,11 @@
RingBuffer<SwapHistory, 3> mSwapHistory;
bool mOpaque;
- OpenGLRenderer* mCanvas = nullptr;
#if HWUI_NEW_OPS
BakedOpRenderer::LightInfo mLightInfo;
FrameBuilder::LightGeometry mLightGeometry = { {0, 0, 0}, 0 };
+#else
+ OpenGLRenderer* mCanvas = nullptr;
#endif
bool mHaveNewSurface = false;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 04223a7..16dd108 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -56,6 +56,7 @@
enum {
FrameStats = 1 << 0,
Reset = 1 << 1,
+ JankStats = 1 << 2,
};
};
@@ -415,7 +416,6 @@
CREATE_BRIDGE4(dumpProfileInfo, CanvasContext* context, RenderThread* thread,
int fd, int dumpFlags) {
args->context->profiler().dumpData(args->fd);
- args->thread->jankTracker().dump(args->fd);
if (args->dumpFlags & DumpFlags::FrameStats) {
args->context->dumpFrames(args->fd);
}
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index a4aee61..c762eed 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -16,6 +16,7 @@
#include "TestUtils.h"
+#include "hwui/Paint.h"
#include "DeferredLayerUpdater.h"
#include "LayerRenderer.h"
@@ -41,17 +42,21 @@
sp<DeferredLayerUpdater> TestUtils::createTextureLayerUpdater(
renderthread::RenderThread& renderThread, uint32_t width, uint32_t height,
- std::function<void(Matrix4*)> transformSetupCallback) {
+ const SkMatrix& transform) {
+ Layer* layer = LayerRenderer::createTextureLayer(renderThread.renderState());
+ layer->getTransform().load(transform);
+
+ sp<DeferredLayerUpdater> layerUpdater = new DeferredLayerUpdater(layer);
+ layerUpdater->setSize(width, height);
+ layerUpdater->setTransform(&transform);
+
+ // updateLayer so it's ready to draw
bool isOpaque = true;
bool forceFilter = true;
GLenum renderTarget = GL_TEXTURE_EXTERNAL_OES;
-
- Layer* layer = LayerRenderer::createTextureLayer(renderThread.renderState());
LayerRenderer::updateTextureLayer(layer, width, height, isOpaque, forceFilter,
- renderTarget, Matrix4::identity().data);
- transformSetupCallback(&(layer->getTransform()));
+ renderTarget, Matrix4::identity().data);
- sp<DeferredLayerUpdater> layerUpdater = new DeferredLayerUpdater(layer);
return layerUpdater;
}
@@ -87,50 +92,17 @@
*outTotalAdvance = totalAdvance;
}
-void TestUtils::drawUtf8ToCanvas(TestCanvas* canvas, const char* text,
+
+void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text,
const SkPaint& paint, float x, float y) {
- // drawing text requires GlyphID TextEncoding (which JNI layer would have done)
- LOG_ALWAYS_FATAL_IF(paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding,
- "must use glyph encoding");
- SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry);
- SkAutoGlyphCacheNoGamma autoCache(paint, &surfaceProps, &SkMatrix::I());
-
- std::vector<glyph_t> glyphs;
- std::vector<float> positions;
- float totalAdvance;
- Rect bounds;
- layoutTextUnscaled(paint, text, &glyphs, &positions, &totalAdvance, &bounds);
-
- // apply alignment via x parameter (which JNI layer would have done)
- if (paint.getTextAlign() == SkPaint::kCenter_Align) {
- x -= totalAdvance / 2;
- } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
- x -= totalAdvance;
- }
-
- bounds.translate(x, y);
-
- // Force left alignment, since alignment offset is already baked in
- SkPaint alignPaintCopy(paint);
- alignPaintCopy.setTextAlign(SkPaint::kLeft_Align);
- canvas->drawGlyphs(glyphs.data(), positions.data(), glyphs.size(), alignPaintCopy, x, y,
- bounds.left, bounds.top, bounds.right, bounds.bottom, totalAdvance);
+ auto utf16 = asciiToUtf16(text);
+ canvas->drawText(utf16.get(), 0, strlen(text), strlen(text), x, y, 0, paint, nullptr);
}
-void TestUtils::drawUtf8ToCanvas(TestCanvas* canvas, const char* text,
+void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text,
const SkPaint& paint, const SkPath& path) {
- // drawing text requires GlyphID TextEncoding (which JNI layer would have done)
- LOG_ALWAYS_FATAL_IF(paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding,
- "must use glyph encoding");
- SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry);
- SkAutoGlyphCacheNoGamma autoCache(paint, &surfaceProps, &SkMatrix::I());
-
- std::vector<glyph_t> glyphs;
- while (*text != '\0') {
- SkUnichar unichar = SkUTF8_NextUnichar(&text);
- glyphs.push_back(autoCache.getCache()->unicharToGlyph(unichar));
- }
- canvas->drawGlyphsOnPath(glyphs.data(), glyphs.size(), path, 0, 0, paint);
+ auto utf16 = asciiToUtf16(text);
+ canvas->drawTextOnPath(utf16.get(), strlen(text), 0, path, 0, 0, paint, nullptr);
}
void TestUtils::TestTask::run() {
@@ -143,12 +115,13 @@
renderState.onGLContextDestroyed();
}
-std::unique_ptr<uint16_t[]> TestUtils::utf8ToUtf16(const char* str) {
- const size_t strLen = strlen(str);
- const ssize_t utf16Len = utf8_to_utf16_length((uint8_t*) str, strLen);
- std::unique_ptr<uint16_t[]> dst(new uint16_t[utf16Len + 1]);
- utf8_to_utf16((uint8_t*) str, strLen, (char16_t*) dst.get());
- return dst;
+std::unique_ptr<uint16_t[]> TestUtils::asciiToUtf16(const char* str) {
+ const int length = strlen(str);
+ std::unique_ptr<uint16_t[]> utf16(new uint16_t[length]);
+ for (int i = 0; i < length; i++) {
+ utf16.get()[i] = str[i];
+ }
+ return utf16;
}
} /* namespace uirenderer */
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index a5e7a5f..5492035 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -122,7 +122,7 @@
static sp<DeferredLayerUpdater> createTextureLayerUpdater(
renderthread::RenderThread& renderThread, uint32_t width, uint32_t height,
- std::function<void(Matrix4*)> transformSetupCallback);
+ const SkMatrix& transform);
template<class CanvasType>
static std::unique_ptr<DisplayList> createDisplayList(int width, int height,
@@ -207,18 +207,18 @@
std::vector<glyph_t>* outGlyphs, std::vector<float>* outPositions,
float* outTotalAdvance, Rect* outBounds);
- static void drawUtf8ToCanvas(TestCanvas* canvas, const char* text,
+ static void drawUtf8ToCanvas(Canvas* canvas, const char* text,
const SkPaint& paint, float x, float y);
- static void drawUtf8ToCanvas(TestCanvas* canvas, const char* text,
+ static void drawUtf8ToCanvas(Canvas* canvas, const char* text,
const SkPaint& paint, const SkPath& path);
- static std::unique_ptr<uint16_t[]> utf8ToUtf16(const char* str);
+ static std::unique_ptr<uint16_t[]> asciiToUtf16(const char* str);
private:
static void syncHierarchyPropertiesAndDisplayListImpl(RenderNode* node) {
node->syncProperties();
- node->syncDisplayList();
+ node->syncDisplayList(nullptr);
auto displayList = node->getDisplayList();
if (displayList) {
for (auto&& childOp : displayList->getChildren()) {
diff --git a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
index 52039ef..63c067b 100644
--- a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
@@ -42,7 +42,7 @@
}
void doFrame(int frameNr) override {
- std::unique_ptr<uint16_t[]> text = TestUtils::utf8ToUtf16(
+ std::unique_ptr<uint16_t[]> text = TestUtils::asciiToUtf16(
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
ssize_t textLength = 26 * 2;
diff --git a/libs/hwui/tests/microbench/FontBench.cpp b/libs/hwui/tests/microbench/FontBench.cpp
new file mode 100644
index 0000000..df3d041
--- /dev/null
+++ b/libs/hwui/tests/microbench/FontBench.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <benchmark/benchmark.h>
+
+#include "GammaFontRenderer.h"
+#include "tests/common/TestUtils.h"
+
+#include <SkPaint.h>
+
+using namespace android;
+using namespace android::uirenderer;
+
+void BM_FontRenderer_precache_cachehits(benchmark::State& state) {
+ TestUtils::runOnRenderThread([&state](renderthread::RenderThread& thread) {
+ SkPaint paint;
+ paint.setTextSize(20);
+ paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ GammaFontRenderer gammaFontRenderer;
+ FontRenderer& fontRenderer = gammaFontRenderer.getFontRenderer();
+ fontRenderer.setFont(&paint, SkMatrix::I());
+
+ std::vector<glyph_t> glyphs;
+ std::vector<float> positions;
+ float totalAdvance;
+ uirenderer::Rect bounds;
+ TestUtils::layoutTextUnscaled(paint, "This is a test",
+ &glyphs, &positions, &totalAdvance, &bounds);
+
+ fontRenderer.precache(&paint, glyphs.data(), glyphs.size(), SkMatrix::I());
+
+ while (state.KeepRunning()) {
+ fontRenderer.precache(&paint, glyphs.data(), glyphs.size(), SkMatrix::I());
+ }
+ });
+}
+BENCHMARK(BM_FontRenderer_precache_cachehits);
diff --git a/libs/hwui/tests/microbench/FrameBuilderBench.cpp b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
index 9daf633..0aef620 100644
--- a/libs/hwui/tests/microbench/FrameBuilderBench.cpp
+++ b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
@@ -113,15 +113,17 @@
};
void BM_FrameBuilder_defer_scene(benchmark::State& state) {
- const char* sceneName = *(SCENES.begin() + state.range_x());
- state.SetLabel(sceneName);
- auto nodes = getSyncedSceneNodes(sceneName);
- while (state.KeepRunning()) {
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
- SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, gDisplay.h,
- nodes, sLightGeometry, Caches::getInstance());
- benchmark::DoNotOptimize(&frameBuilder);
- }
+ TestUtils::runOnRenderThread([&state](RenderThread& thread) {
+ const char* sceneName = *(SCENES.begin() + state.range_x());
+ state.SetLabel(sceneName);
+ auto nodes = getSyncedSceneNodes(sceneName);
+ while (state.KeepRunning()) {
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
+ SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, gDisplay.h,
+ nodes, sLightGeometry, Caches::getInstance());
+ benchmark::DoNotOptimize(&frameBuilder);
+ }
+ });
}
BENCHMARK(BM_FrameBuilder_defer_scene)->DenseRange(0, SCENES.size() - 1);
diff --git a/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp b/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
index 654ddc6..5471486 100644
--- a/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
+++ b/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
@@ -85,7 +85,9 @@
<< "Should see conservative offset from PathCache::computeBounds";
Rect expectedBounds(10, 15, 20, 25);
expectedBounds.outset(expectedOffset);
+#if !HWUI_NEW_OPS
EXPECT_EQ(expectedBounds, glop.bounds) << "bounds outset by stroke 'offset'";
+#endif
Matrix4 expectedModelView;
expectedModelView.loadTranslate(10 - expectedOffset, 15 - expectedOffset, 0);
expectedModelView.scale(10 + 2 * expectedOffset, 10 + 2 * expectedOffset, 1);
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index 0aabfb1..0ea246f 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -30,6 +30,7 @@
namespace uirenderer {
const LayerUpdateQueue sEmptyLayerUpdateQueue;
+const std::vector< sp<RenderNode> > sEmptyNodeList;
const FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50};
@@ -48,9 +49,12 @@
public:
virtual ~TestRendererBase() {}
virtual OffscreenBuffer* startTemporaryLayer(uint32_t, uint32_t) {
- ADD_FAILURE() << "Layer creation not expected in this test";
+ ADD_FAILURE() << "Temporary layers not expected in this test";
return nullptr;
}
+ virtual void recycleTemporaryLayer(OffscreenBuffer*) {
+ ADD_FAILURE() << "Temporary layers not expected in this test";
+ }
virtual void startRepaintLayer(OffscreenBuffer*, const Rect& repaintRect) {
ADD_FAILURE() << "Layer repaint not expected in this test";
}
@@ -216,6 +220,122 @@
<< "Expect number of ops = 2 * loop count";
}
+RENDERTHREAD_TEST(FrameBuilder, empty_noFbo0) {
+ class EmptyNoFbo0TestRenderer : public TestRendererBase {
+ public:
+ void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
+ ADD_FAILURE() << "Primary frame draw not expected in this test";
+ }
+ void endFrame(const Rect& repaintRect) override {
+ ADD_FAILURE() << "Primary frame draw not expected in this test";
+ }
+ };
+
+ // Pass empty node list, so no work is enqueued for Fbo0
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ sEmptyNodeList, sLightGeometry, Caches::getInstance());
+ EmptyNoFbo0TestRenderer renderer;
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+}
+
+RENDERTHREAD_TEST(FrameBuilder, empty_withFbo0) {
+ class EmptyWithFbo0TestRenderer : public TestRendererBase {
+ public:
+ void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
+ EXPECT_EQ(0, mIndex++);
+ }
+ void endFrame(const Rect& repaintRect) override {
+ EXPECT_EQ(1, mIndex++);
+ }
+ };
+ auto node = TestUtils::createNode(10, 10, 110, 110,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
+ // no drawn content
+ });
+ auto syncedNodeList = TestUtils::createSyncedNodeList(node);
+
+ // Draw, but pass empty node list, so no work is done for primary frame
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ syncedNodeList, sLightGeometry, Caches::getInstance());
+ EmptyWithFbo0TestRenderer renderer;
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(2, renderer.getIndex()) << "No drawing content produced,"
+ " but fbo0 update lifecycle should still be observed";
+}
+
+RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_rects) {
+ class AvoidOverdrawRectsTestRenderer : public TestRendererBase {
+ public:
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(mIndex++, 0) << "Should be one rect";
+ EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds)
+ << "Last rect should occlude others.";
+ }
+ };
+ auto node = TestUtils::createNode(0, 0, 200, 200,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
+ canvas.drawRect(0, 0, 200, 200, SkPaint());
+ canvas.drawRect(0, 0, 200, 200, SkPaint());
+ canvas.drawRect(10, 10, 190, 190, SkPaint());
+ });
+
+ // Damage (and therefore clip) is same as last draw, subset of renderable area.
+ // This means last op occludes other contents, and they'll be rejected to avoid overdraw.
+ SkRect damageRect = SkRect::MakeLTRB(10, 10, 190, 190);
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, damageRect, 200, 200,
+ TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+
+ EXPECT_EQ(3u, node->getDisplayList()->getOps().size())
+ << "Recording must not have rejected ops, in order for this test to be valid";
+
+ AvoidOverdrawRectsTestRenderer renderer;
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(1, renderer.getIndex()) << "Expect exactly one op";
+}
+
+RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_bitmaps) {
+ static SkBitmap opaqueBitmap = TestUtils::createSkBitmap(50, 50,
+ SkColorType::kRGB_565_SkColorType);
+ static SkBitmap transpBitmap = TestUtils::createSkBitmap(50, 50,
+ SkColorType::kAlpha_8_SkColorType);
+ class AvoidOverdrawBitmapsTestRenderer : public TestRendererBase {
+ public:
+ void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
+ switch(mIndex++) {
+ case 0:
+ EXPECT_EQ(opaqueBitmap.pixelRef(), op.bitmap->pixelRef());
+ break;
+ case 1:
+ EXPECT_EQ(transpBitmap.pixelRef(), op.bitmap->pixelRef());
+ break;
+ default:
+ ADD_FAILURE() << "Only two ops expected.";
+ }
+ }
+ };
+
+ auto node = TestUtils::createNode(0, 0, 50, 50,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
+ canvas.drawRect(0, 0, 50, 50, SkPaint());
+ canvas.drawRect(0, 0, 50, 50, SkPaint());
+ canvas.drawBitmap(transpBitmap, 0, 0, nullptr);
+
+ // only the below draws should remain, since they're
+ canvas.drawBitmap(opaqueBitmap, 0, 0, nullptr);
+ canvas.drawBitmap(transpBitmap, 0, 0, nullptr);
+ });
+
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(50, 50), 50, 50,
+ TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+
+ EXPECT_EQ(5u, node->getDisplayList()->getOps().size())
+ << "Recording must not have rejected ops, in order for this test to be valid";
+
+ AvoidOverdrawBitmapsTestRenderer renderer;
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(2, renderer.getIndex()) << "Expect exactly two ops";
+}
+
RENDERTHREAD_TEST(FrameBuilder, clippedMerging) {
class ClippedMergingTestRenderer : public TestRendererBase {
public:
@@ -344,8 +464,9 @@
EXPECT_TRUE(stroke.contains(fill));
EXPECT_FALSE(fill.contains(stroke));
+ // outset by half the stroke width
Rect outsetFill(fill);
- outsetFill.outset(10);
+ outsetFill.outset(5);
EXPECT_EQ(stroke, outsetFill);
}
};
@@ -371,8 +492,8 @@
EXPECT_EQ(3, renderer.getIndex()) << "Expect 3 ops";
}
-RENDERTHREAD_TEST(FrameBuilder, textureLayer) {
- class TextureLayerTestRenderer : public TestRendererBase {
+RENDERTHREAD_TEST(FrameBuilder, textureLayer_clipLocalMatrix) {
+ class TextureLayerClipLocalMatrixTestRenderer : public TestRendererBase {
public:
void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
EXPECT_EQ(0, mIndex++);
@@ -386,9 +507,7 @@
};
auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
- [](Matrix4* transform) {
- transform->loadTranslate(5, 5, 0);
- });
+ SkMatrix::MakeTrans(5, 5));
auto node = TestUtils::createNode(0, 0, 200, 200,
[&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
@@ -399,11 +518,56 @@
});
FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
- TextureLayerTestRenderer renderer;
+ TextureLayerClipLocalMatrixTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(1, renderer.getIndex());
}
+RENDERTHREAD_TEST(FrameBuilder, textureLayer_combineMatrices) {
+ class TextureLayerCombineMatricesTestRenderer : public TestRendererBase {
+ public:
+ void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(0, mIndex++);
+
+ Matrix4 expected;
+ expected.loadTranslate(35, 45, 0);
+ EXPECT_MATRIX_APPROX_EQ(expected, state.computedState.transform);
+ }
+ };
+
+ auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
+ SkMatrix::MakeTrans(5, 5));
+
+ auto node = TestUtils::createNode(0, 0, 200, 200,
+ [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
+ canvas.save(SaveFlags::MatrixClip);
+ canvas.translate(30, 40);
+ canvas.drawLayer(layerUpdater.get());
+ canvas.restore();
+ });
+
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+ TextureLayerCombineMatricesTestRenderer renderer;
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(1, renderer.getIndex());
+}
+
+RENDERTHREAD_TEST(FrameBuilder, textureLayer_reject) {
+ auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
+ SkMatrix::MakeTrans(5, 5));
+ layerUpdater->backingLayer()->setRenderTarget(GL_NONE); // Should be rejected
+
+ auto node = TestUtils::createNode(0, 0, 200, 200,
+ [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
+ canvas.drawLayer(layerUpdater.get());
+ });
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+ FailRenderer renderer;
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+}
+
RENDERTHREAD_TEST(FrameBuilder, functor_reject) {
class FunctorTestRenderer : public TestRendererBase {
public:
@@ -428,7 +592,31 @@
EXPECT_EQ(1, renderer.getIndex()) << "Functor should not be rejected";
}
-RENDERTHREAD_TEST(FrameBuilder, renderNode) {
+RENDERTHREAD_TEST(FrameBuilder, deferColorOp_unbounded) {
+ class ColorTestRenderer : public TestRendererBase {
+ public:
+ void onColorOp(const ColorOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(0, mIndex++);
+ EXPECT_EQ(Rect(200, 200), state.computedState.clippedBounds)
+ << "Color op should be expanded to bounds of surrounding";
+ }
+ };
+
+ auto unclippedColorView = TestUtils::createNode(0, 0, 10, 10,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
+ props.setClipToBounds(false);
+ canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
+ });
+
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ TestUtils::createSyncedNodeList(unclippedColorView),
+ sLightGeometry, Caches::getInstance());
+ ColorTestRenderer renderer;
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(1, renderer.getIndex()) << "ColorOp should not be rejected";
+}
+
+TEST(FrameBuilder, renderNode) {
class RenderNodeTestRenderer : public TestRendererBase {
public:
void onRectOp(const RectOp& op, const BakedOpState& state) override {
@@ -525,6 +713,10 @@
EXPECT_EQ(Rect(200, 200), state.computedState.clipRect());
EXPECT_TRUE(state.computedState.transform.isIdentity());
}
+ void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
+ EXPECT_EQ(4, mIndex++);
+ EXPECT_EQ(nullptr, offscreenBuffer);
+ }
};
auto node = TestUtils::createNode(0, 0, 200, 200,
@@ -537,7 +729,7 @@
TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
SaveLayerSimpleTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(4, renderer.getIndex());
+ EXPECT_EQ(5, renderer.getIndex());
}
RENDERTHREAD_TEST(FrameBuilder, saveLayer_nested) {
@@ -589,6 +781,15 @@
EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer layer
} else { ADD_FAILURE(); }
}
+ void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
+ const int index = mIndex++;
+ // order isn't important, but we need to see both
+ if (index == 10) {
+ EXPECT_EQ((OffscreenBuffer*)0x400, offscreenBuffer);
+ } else if (index == 11) {
+ EXPECT_EQ((OffscreenBuffer*)0x800, offscreenBuffer);
+ } else { ADD_FAILURE(); }
+ }
};
auto node = TestUtils::createNode(0, 0, 800, 800,
@@ -609,7 +810,7 @@
TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
SaveLayerNestedTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(10, renderer.getIndex());
+ EXPECT_EQ(12, renderer.getIndex());
}
RENDERTHREAD_TEST(FrameBuilder, saveLayer_contentRejection) {
@@ -824,10 +1025,15 @@
}
void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
EXPECT_EQ(9, mIndex++);
+ EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle);
}
void endFrame(const Rect& repaintRect) override {
EXPECT_EQ(11, mIndex++);
}
+ void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
+ EXPECT_EQ(12, mIndex++);
+ EXPECT_EQ((OffscreenBuffer*)0xabcd, offscreenBuffer);
+ }
};
auto node = TestUtils::createNode(0, 0, 600, 600, // 500x500 triggers clipping
@@ -844,7 +1050,7 @@
TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
SaveLayerUnclippedComplexTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(12, renderer.getIndex());
+ EXPECT_EQ(13, renderer.getIndex());
}
RENDERTHREAD_TEST(FrameBuilder, hwLayer_simple) {
@@ -966,6 +1172,9 @@
void endFrame(const Rect& repaintRect) override {
EXPECT_EQ(12, mIndex++);
}
+ void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
+ EXPECT_EQ(13, mIndex++);
+ }
};
auto child = TestUtils::createNode(50, 50, 150, 150,
@@ -1003,13 +1212,71 @@
syncedList, sLightGeometry, Caches::getInstance());
HwLayerComplexTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(13, renderer.getIndex());
+ EXPECT_EQ(14, renderer.getIndex());
// clean up layer pointers, so we can safely destruct RenderNodes
*(child->getLayerHandle()) = nullptr;
*(parent->getLayerHandle()) = nullptr;
}
+
+RENDERTHREAD_TEST(FrameBuilder, buildLayer) {
+ class BuildLayerTestRenderer : public TestRendererBase {
+ public:
+ void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
+ EXPECT_EQ(0, mIndex++);
+ EXPECT_EQ(100u, offscreenBuffer->viewportWidth);
+ EXPECT_EQ(100u, offscreenBuffer->viewportHeight);
+ EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect);
+ }
+ void onColorOp(const ColorOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(1, mIndex++);
+
+ EXPECT_TRUE(state.computedState.transform.isIdentity())
+ << "Transform should be reset within layer";
+
+ EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect())
+ << "Damage rect should be used to clip layer content";
+ }
+ void endLayer() override {
+ EXPECT_EQ(2, mIndex++);
+ }
+ void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
+ ADD_FAILURE() << "Primary frame draw not expected in this test";
+ }
+ void endFrame(const Rect& repaintRect) override {
+ ADD_FAILURE() << "Primary frame draw not expected in this test";
+ }
+ };
+
+ auto node = TestUtils::createNode(10, 10, 110, 110,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
+ props.mutateLayerProperties().setType(LayerType::RenderLayer);
+ canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
+ });
+ OffscreenBuffer** layerHandle = node->getLayerHandle();
+
+ // create RenderNode's layer here in same way prepareTree would
+ OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
+ *layerHandle = &layer;
+
+ auto syncedNodeList = TestUtils::createSyncedNodeList(node);
+
+ // only enqueue partial damage
+ LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
+ layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
+
+ // Draw, but pass empty node list, so no work is done for primary frame
+ FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(1, 1), 1, 1,
+ sEmptyNodeList, sLightGeometry, Caches::getInstance());
+ BuildLayerTestRenderer renderer;
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(3, renderer.getIndex());
+
+ // clean up layer pointer, so we can safely destruct RenderNode
+ *layerHandle = nullptr;
+}
+
static void drawOrderedRect(RecordingCanvas* canvas, uint8_t expectedDrawOrder) {
SkPaint paint;
paint.setColor(SkColorSetARGB(256, 0, 0, expectedDrawOrder)); // order put in blue channel
@@ -1349,6 +1616,9 @@
void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
EXPECT_EQ(4, mIndex++);
}
+ void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
+ EXPECT_EQ(5, mIndex++);
+ }
};
auto parent = TestUtils::createNode(0, 0, 200, 200,
@@ -1367,7 +1637,7 @@
(FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance());
ShadowSaveLayerTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
- EXPECT_EQ(5, renderer.getIndex());
+ EXPECT_EQ(6, renderer.getIndex());
}
RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) {
@@ -1596,6 +1866,9 @@
void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
EXPECT_EQ(3, mIndex++);
}
+ void recycleTemporaryLayer(OffscreenBuffer* offscreenBuffer) override {
+ EXPECT_EQ(4, mIndex++);
+ }
private:
SaveLayerAlphaData* mOutData;
};
@@ -1621,7 +1894,7 @@
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
// assert, since output won't be valid if we haven't seen a save layer triggered
- ASSERT_EQ(4, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior.";
+ ASSERT_EQ(5, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior.";
}
RENDERTHREAD_TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) {
diff --git a/libs/hwui/tests/unit/GlopBuilderTests.cpp b/libs/hwui/tests/unit/GlopBuilderTests.cpp
index 454011f..95543d3 100644
--- a/libs/hwui/tests/unit/GlopBuilderTests.cpp
+++ b/libs/hwui/tests/unit/GlopBuilderTests.cpp
@@ -85,7 +85,9 @@
}
static void expectGlopEq(Glop& expectedGlop, Glop& builtGlop) {
+#if !HWUI_NEW_OPS
EXPECT_EQ(expectedGlop.bounds, builtGlop.bounds);
+#endif
expectBlendEq(expectedGlop.blend, builtGlop.blend);
expectFillEq(expectedGlop.fill, builtGlop.fill);
expectMeshEq(expectedGlop.mesh, builtGlop.mesh);
@@ -136,7 +138,9 @@
// unit quad also should be translate by additional (0.3, 0.3) to snap to exact pixels.
goldenGlop->transform.modelView.loadTranslate(1.3, 1.3, 0);
goldenGlop->transform.modelView.scale(99, 99, 1);
+#if !HWUI_NEW_OPS
goldenGlop->bounds = android::uirenderer::Rect(1.70, 1.70, 100.70, 100.70);
+#endif
goldenGlop->transform.canvas = simpleTranslate;
goldenGlop->fill.texture.filter = GL_NEAREST;
expectGlopEq(*goldenGlop, glop);
diff --git a/libs/hwui/tests/unit/LeakCheckTests.cpp b/libs/hwui/tests/unit/LeakCheckTests.cpp
index 9161f90..e2fc376 100644
--- a/libs/hwui/tests/unit/LeakCheckTests.cpp
+++ b/libs/hwui/tests/unit/LeakCheckTests.cpp
@@ -30,6 +30,25 @@
const FrameBuilder::LightGeometry sLightGeometery = { {100, 100, 100}, 50};
const BakedOpRenderer::LightInfo sLightInfo = { 128, 128 };
+RENDERTHREAD_TEST(LeakCheck, saveLayer_overdrawRejection) {
+ auto node = TestUtils::createNode(0, 0, 100, 100,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
+ canvas.saveLayerAlpha(0, 0, 100, 100, 128, SaveFlags::ClipToLayer);
+ canvas.drawRect(0, 0, 100, 100, SkPaint());
+ canvas.restore();
+
+ // opaque draw, rejects saveLayer beneath
+ canvas.drawRect(0, 0, 100, 100, SkPaint());
+ });
+ RenderState& renderState = renderThread.renderState();
+ Caches& caches = Caches::getInstance();
+
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
+ TestUtils::createSyncedNodeList(node), sLightGeometery, Caches::getInstance());
+ BakedOpRenderer renderer(caches, renderState, true, sLightInfo);
+ frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
+}
+
RENDERTHREAD_TEST(LeakCheck, saveLayerUnclipped_simple) {
auto node = TestUtils::createNode(0, 0, 200, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
diff --git a/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp b/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp
index 37a485e..b7950aa 100644
--- a/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp
+++ b/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp
@@ -30,119 +30,126 @@
EXPECT_EQ(1024u, OffscreenBuffer::computeIdealDimension(1000));
}
-TEST(OffscreenBuffer, construct) {
- TestUtils::runOnRenderThread([] (renderthread::RenderThread& thread) {
- OffscreenBuffer layer(thread.renderState(), Caches::getInstance(), 49u, 149u);
- EXPECT_EQ(49u, layer.viewportWidth);
- EXPECT_EQ(149u, layer.viewportHeight);
+RENDERTHREAD_TEST(OffscreenBuffer, construct) {
+ OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 49u, 149u);
+ EXPECT_EQ(49u, layer.viewportWidth);
+ EXPECT_EQ(149u, layer.viewportHeight);
- EXPECT_EQ(64u, layer.texture.width());
- EXPECT_EQ(192u, layer.texture.height());
+ EXPECT_EQ(64u, layer.texture.width());
+ EXPECT_EQ(192u, layer.texture.height());
- EXPECT_EQ(64u * 192u * 4u, layer.getSizeInBytes());
- });
+ EXPECT_EQ(64u * 192u * 4u, layer.getSizeInBytes());
}
-TEST(OffscreenBuffer, getTextureCoordinates) {
- TestUtils::runOnRenderThread([] (renderthread::RenderThread& thread) {
- OffscreenBuffer layerAligned(thread.renderState(), Caches::getInstance(), 256u, 256u);
- EXPECT_EQ(Rect(0, 1, 1, 0),
- layerAligned.getTextureCoordinates());
+RENDERTHREAD_TEST(OffscreenBuffer, getTextureCoordinates) {
+ OffscreenBuffer layerAligned(renderThread.renderState(), Caches::getInstance(), 256u, 256u);
+ EXPECT_EQ(Rect(0, 1, 1, 0),
+ layerAligned.getTextureCoordinates());
- OffscreenBuffer layerUnaligned(thread.renderState(), Caches::getInstance(), 200u, 225u);
- EXPECT_EQ(Rect(0, 225.0f / 256.0f, 200.0f / 256.0f, 0),
- layerUnaligned.getTextureCoordinates());
- });
+ OffscreenBuffer layerUnaligned(renderThread.renderState(), Caches::getInstance(), 200u, 225u);
+ EXPECT_EQ(Rect(0, 225.0f / 256.0f, 200.0f / 256.0f, 0),
+ layerUnaligned.getTextureCoordinates());
}
-TEST(OffscreenBuffer, dirty) {
- TestUtils::runOnRenderThread([] (renderthread::RenderThread& thread) {
- OffscreenBuffer buffer(thread.renderState(), Caches::getInstance(), 256u, 256u);
- buffer.dirty(Rect(-100, -100, 100, 100));
- EXPECT_EQ(android::Rect(100, 100), buffer.region.getBounds());
- });
+RENDERTHREAD_TEST(OffscreenBuffer, dirty) {
+ OffscreenBuffer buffer(renderThread.renderState(), Caches::getInstance(), 256u, 256u);
+ buffer.dirty(Rect(-100, -100, 100, 100));
+ EXPECT_EQ(android::Rect(100, 100), buffer.region.getBounds());
}
-TEST(OffscreenBufferPool, construct) {
- TestUtils::runOnRenderThread([] (renderthread::RenderThread& thread) {
- OffscreenBufferPool pool;
- EXPECT_EQ(0u, pool.getCount()) << "pool must be created empty";
- EXPECT_EQ(0u, pool.getSize()) << "pool must be created empty";
- EXPECT_EQ((uint32_t) Properties::layerPoolSize, pool.getMaxSize())
- << "pool must read size from Properties";
- });
+RENDERTHREAD_TEST(OffscreenBufferPool, construct) {
+ OffscreenBufferPool pool;
+ EXPECT_EQ(0u, pool.getCount()) << "pool must be created empty";
+ EXPECT_EQ(0u, pool.getSize()) << "pool must be created empty";
+ EXPECT_EQ((uint32_t) Properties::layerPoolSize, pool.getMaxSize())
+ << "pool must read size from Properties";
}
-TEST(OffscreenBufferPool, getPutClear) {
- TestUtils::runOnRenderThread([] (renderthread::RenderThread& thread) {
- OffscreenBufferPool pool;
+RENDERTHREAD_TEST(OffscreenBufferPool, getPutClear) {
+ OffscreenBufferPool pool;
- auto layer = pool.get(thread.renderState(), 100u, 200u);
- EXPECT_EQ(100u, layer->viewportWidth);
- EXPECT_EQ(200u, layer->viewportHeight);
+ auto layer = pool.get(renderThread.renderState(), 100u, 200u);
+ EXPECT_EQ(100u, layer->viewportWidth);
+ EXPECT_EQ(200u, layer->viewportHeight);
- ASSERT_LT(layer->getSizeInBytes(), pool.getMaxSize());
+ ASSERT_LT(layer->getSizeInBytes(), pool.getMaxSize());
- pool.putOrDelete(layer);
- ASSERT_EQ(layer->getSizeInBytes(), pool.getSize());
+ pool.putOrDelete(layer);
+ ASSERT_EQ(layer->getSizeInBytes(), pool.getSize());
- auto layer2 = pool.get(thread.renderState(), 102u, 202u);
- EXPECT_EQ(layer, layer2) << "layer should be recycled";
- ASSERT_EQ(0u, pool.getSize()) << "pool should have been emptied by removing only layer";
+ auto layer2 = pool.get(renderThread.renderState(), 102u, 202u);
+ EXPECT_EQ(layer, layer2) << "layer should be recycled";
+ ASSERT_EQ(0u, pool.getSize()) << "pool should have been emptied by removing only layer";
- pool.putOrDelete(layer);
- EXPECT_EQ(1u, pool.getCount());
- pool.clear();
- EXPECT_EQ(0u, pool.getSize());
- EXPECT_EQ(0u, pool.getCount());
- });
+ pool.putOrDelete(layer);
+ EXPECT_EQ(1u, pool.getCount());
+ pool.clear();
+ EXPECT_EQ(0u, pool.getSize());
+ EXPECT_EQ(0u, pool.getCount());
}
-TEST(OffscreenBufferPool, resize) {
- TestUtils::runOnRenderThread([] (renderthread::RenderThread& thread) {
- OffscreenBufferPool pool;
+RENDERTHREAD_TEST(OffscreenBufferPool, resize) {
+ OffscreenBufferPool pool;
- auto layer = pool.get(thread.renderState(), 64u, 64u);
- layer->dirty(Rect(64, 64));
+ auto layer = pool.get(renderThread.renderState(), 64u, 64u);
+ layer->dirty(Rect(64, 64));
- // resize in place
- ASSERT_EQ(layer, pool.resize(layer, 60u, 55u));
- EXPECT_TRUE(layer->region.isEmpty()) << "In place resize should clear usage region";
- EXPECT_EQ(60u, layer->viewportWidth);
- EXPECT_EQ(55u, layer->viewportHeight);
- EXPECT_EQ(64u, layer->texture.width());
- EXPECT_EQ(64u, layer->texture.height());
+ // resize in place
+ ASSERT_EQ(layer, pool.resize(layer, 60u, 55u));
+ EXPECT_TRUE(layer->region.isEmpty()) << "In place resize should clear usage region";
+ EXPECT_EQ(60u, layer->viewportWidth);
+ EXPECT_EQ(55u, layer->viewportHeight);
+ EXPECT_EQ(64u, layer->texture.width());
+ EXPECT_EQ(64u, layer->texture.height());
- // resized to use different object in pool
- auto layer2 = pool.get(thread.renderState(), 128u, 128u);
- layer2->dirty(Rect(128, 128));
- EXPECT_FALSE(layer2->region.isEmpty());
- pool.putOrDelete(layer2);
- ASSERT_EQ(1u, pool.getCount());
+ // resized to use different object in pool
+ auto layer2 = pool.get(renderThread.renderState(), 128u, 128u);
+ layer2->dirty(Rect(128, 128));
+ EXPECT_FALSE(layer2->region.isEmpty());
+ pool.putOrDelete(layer2);
+ ASSERT_EQ(1u, pool.getCount());
- ASSERT_EQ(layer2, pool.resize(layer, 120u, 125u));
- EXPECT_TRUE(layer2->region.isEmpty()) << "Swap resize should clear usage region";
- EXPECT_EQ(120u, layer2->viewportWidth);
- EXPECT_EQ(125u, layer2->viewportHeight);
- EXPECT_EQ(128u, layer2->texture.width());
- EXPECT_EQ(128u, layer2->texture.height());
+ ASSERT_EQ(layer2, pool.resize(layer, 120u, 125u));
+ EXPECT_TRUE(layer2->region.isEmpty()) << "Swap resize should clear usage region";
+ EXPECT_EQ(120u, layer2->viewportWidth);
+ EXPECT_EQ(125u, layer2->viewportHeight);
+ EXPECT_EQ(128u, layer2->texture.width());
+ EXPECT_EQ(128u, layer2->texture.height());
- // original allocation now only thing in pool
- EXPECT_EQ(1u, pool.getCount());
- EXPECT_EQ(layer->getSizeInBytes(), pool.getSize());
+ // original allocation now only thing in pool
+ EXPECT_EQ(1u, pool.getCount());
+ EXPECT_EQ(layer->getSizeInBytes(), pool.getSize());
- pool.putOrDelete(layer2);
- });
+ pool.putOrDelete(layer2);
}
-TEST(OffscreenBufferPool, putAndDestroy) {
- TestUtils::runOnRenderThread([] (renderthread::RenderThread& thread) {
- OffscreenBufferPool pool;
- // layer too big to return to the pool
- // Note: this relies on the fact that the pool won't reject based on max texture size
- auto hugeLayer = pool.get(thread.renderState(), pool.getMaxSize() / 64, 64);
- EXPECT_GT(hugeLayer->getSizeInBytes(), pool.getMaxSize());
- pool.putOrDelete(hugeLayer);
- EXPECT_EQ(0u, pool.getCount()); // failed to put (so was destroyed instead)
- });
+RENDERTHREAD_TEST(OffscreenBufferPool, putAndDestroy) {
+ OffscreenBufferPool pool;
+ // layer too big to return to the pool
+ // Note: this relies on the fact that the pool won't reject based on max texture size
+ auto hugeLayer = pool.get(renderThread.renderState(), pool.getMaxSize() / 64, 64);
+ EXPECT_GT(hugeLayer->getSizeInBytes(), pool.getMaxSize());
+ pool.putOrDelete(hugeLayer);
+ EXPECT_EQ(0u, pool.getCount()); // failed to put (so was destroyed instead)
+}
+
+RENDERTHREAD_TEST(OffscreenBufferPool, clear) {
+ EXPECT_EQ(0, GpuMemoryTracker::getInstanceCount(GpuObjectType::OffscreenBuffer));
+ OffscreenBufferPool pool;
+
+ // Create many buffers, with several at each size
+ std::vector<OffscreenBuffer*> buffers;
+ for (int size = 32; size <= 128; size += 32) {
+ for (int i = 0; i < 10; i++) {
+ buffers.push_back(pool.get(renderThread.renderState(), size, size));
+ }
+ }
+ EXPECT_EQ(0u, pool.getCount()) << "Expect nothing inside";
+ for (auto& buffer : buffers) pool.putOrDelete(buffer);
+ EXPECT_EQ(40u, pool.getCount()) << "Expect all items added";
+ EXPECT_EQ(40, GpuMemoryTracker::getInstanceCount(GpuObjectType::OffscreenBuffer));
+ pool.clear();
+ EXPECT_EQ(0u, pool.getCount()) << "Expect all items cleared";
+
+ EXPECT_EQ(0, GpuMemoryTracker::getInstanceCount(GpuObjectType::OffscreenBuffer));
}
diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
index 6ab5110..58376c6 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -16,6 +16,7 @@
#include <gtest/gtest.h>
+#include <DeferredLayerUpdater.h>
#include <RecordedOp.h>
#include <RecordingCanvas.h>
#include <hwui/Paint.h>
@@ -23,6 +24,9 @@
#include <tests/common/TestUtils.h>
#include <utils/Color.h>
+#include <SkGradientShader.h>
+#include <SkShader.h>
+
namespace android {
namespace uirenderer {
@@ -36,6 +40,12 @@
}
}
+static void validateSingleOp(std::unique_ptr<DisplayList>& dl,
+ std::function<void(const RecordedOp& op)> opValidator) {
+ ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
+ opValidator(*(dl->getOps()[0]));
+}
+
TEST(RecordingCanvas, emptyPlayback) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
canvas.save(SaveFlags::MatrixClip);
@@ -223,9 +233,9 @@
ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
auto op = *(dl->getOps()[0]);
- EXPECT_EQ(RecordedOpId::RectOp, op.opId);
+ EXPECT_EQ(RecordedOpId::ColorOp, op.opId);
EXPECT_EQ(nullptr, op.localClip);
- EXPECT_TRUE(op.unmappedBounds.contains(Rect(200, 200))) << "Expect recording/clip bounds";
+ EXPECT_TRUE(op.unmappedBounds.isEmpty()) << "Expect undefined recorded bounds";
}
TEST(RecordingCanvas, backgroundAndImage) {
@@ -281,6 +291,21 @@
ASSERT_EQ(2, count);
}
+RENDERTHREAD_TEST(RecordingCanvas, textureLayer) {
+ auto layerUpdater = TestUtils::createTextureLayerUpdater(renderThread, 100, 100,
+ SkMatrix::MakeTrans(5, 5));
+
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200,
+ [&layerUpdater](RecordingCanvas& canvas) {
+ canvas.drawLayer(layerUpdater.get());
+ });
+
+ validateSingleOp(dl, [] (const RecordedOp& op) {
+ ASSERT_EQ(RecordedOpId::TextureLayerOp, op.opId);
+ ASSERT_TRUE(op.localMatrix.isIdentity()) << "Op must not apply matrix at record time.";
+ });
+}
+
TEST(RecordingCanvas, saveLayer_simple) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.saveLayerAlpha(10, 20, 190, 180, 128, SaveFlags::ClipToLayer);
@@ -567,36 +592,78 @@
TEST(RecordingCanvas, refPaint) {
SkPaint paint;
- paint.setAntiAlias(true);
- paint.setTextSize(20);
- paint.setTextAlign(SkPaint::kLeft_Align);
- paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [&paint](RecordingCanvas& canvas) {
paint.setColor(SK_ColorBLUE);
- // first three should use same paint
+ // first two should use same paint
canvas.drawRect(0, 0, 200, 10, paint);
SkPaint paintCopy(paint);
canvas.drawRect(0, 10, 200, 20, paintCopy);
- TestUtils::drawUtf8ToCanvas(&canvas, "helloworld", paint, 50, 25);
// only here do we use different paint ptr
paint.setColor(SK_ColorRED);
canvas.drawRect(0, 20, 200, 30, paint);
});
auto ops = dl->getOps();
- ASSERT_EQ(4u, ops.size());
+ ASSERT_EQ(3u, ops.size());
- // first three are the same
+ // first two are the same
EXPECT_NE(nullptr, ops[0]->paint);
EXPECT_NE(&paint, ops[0]->paint);
EXPECT_EQ(ops[0]->paint, ops[1]->paint);
- EXPECT_EQ(ops[0]->paint, ops[2]->paint);
// last is different, but still copied / non-null
- EXPECT_NE(nullptr, ops[3]->paint);
- EXPECT_NE(ops[0]->paint, ops[3]->paint);
- EXPECT_NE(&paint, ops[3]->paint);
+ EXPECT_NE(nullptr, ops[2]->paint);
+ EXPECT_NE(ops[0]->paint, ops[2]->paint);
+ EXPECT_NE(&paint, ops[2]->paint);
+}
+
+TEST(RecordingCanvas, refBitmap) {
+ SkBitmap bitmap = TestUtils::createSkBitmap(100, 100);
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) {
+ canvas.drawBitmap(bitmap, 0, 0, nullptr);
+ });
+ auto& bitmaps = dl->getBitmapResources();
+ EXPECT_EQ(1u, bitmaps.size());
+}
+
+TEST(RecordingCanvas, refBitmapInShader_bitmapShader) {
+ SkBitmap bitmap = TestUtils::createSkBitmap(100, 100);
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) {
+ SkPaint paint;
+ SkAutoTUnref<SkShader> shader(SkShader::CreateBitmapShader(bitmap,
+ SkShader::TileMode::kClamp_TileMode,
+ SkShader::TileMode::kClamp_TileMode));
+ paint.setShader(shader);
+ canvas.drawRoundRect(0, 0, 100, 100, 20.0f, 20.0f, paint);
+ });
+ auto& bitmaps = dl->getBitmapResources();
+ EXPECT_EQ(1u, bitmaps.size());
+}
+
+TEST(RecordingCanvas, refBitmapInShader_composeShader) {
+ SkBitmap bitmap = TestUtils::createSkBitmap(100, 100);
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) {
+ SkPaint paint;
+ SkAutoTUnref<SkShader> shader1(SkShader::CreateBitmapShader(bitmap,
+ SkShader::TileMode::kClamp_TileMode,
+ SkShader::TileMode::kClamp_TileMode));
+
+ SkPoint center;
+ center.set(50, 50);
+ SkColor colors[2];
+ colors[0] = Color::Black;
+ colors[1] = Color::White;
+ SkAutoTUnref<SkShader> shader2(SkGradientShader::CreateRadial(center, 50, colors, nullptr, 2,
+ SkShader::TileMode::kRepeat_TileMode));
+
+ SkAutoTUnref<SkShader> composeShader(SkShader::CreateComposeShader(shader1, shader2,
+ SkXfermode::Mode::kMultiply_Mode));
+ paint.setShader(composeShader);
+ canvas.drawRoundRect(0, 0, 100, 100, 20.0f, 20.0f, paint);
+ });
+ auto& bitmaps = dl->getBitmapResources();
+ EXPECT_EQ(1u, bitmaps.size());
}
TEST(RecordingCanvas, drawText) {
@@ -605,7 +672,7 @@
paint.setAntiAlias(true);
paint.setTextSize(20);
paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
- std::unique_ptr<uint16_t[]> dst = TestUtils::utf8ToUtf16("HELLO");
+ std::unique_ptr<uint16_t[]> dst = TestUtils::asciiToUtf16("HELLO");
canvas.drawText(dst.get(), 0, 5, 5, 25, 25, kBidi_Force_LTR, paint, NULL);
});
@@ -629,7 +696,7 @@
paint.setAntiAlias(true);
paint.setTextSize(20);
paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
- std::unique_ptr<uint16_t[]> dst = TestUtils::utf8ToUtf16("HELLO");
+ std::unique_ptr<uint16_t[]> dst = TestUtils::asciiToUtf16("HELLO");
canvas.drawText(dst.get(), 0, 5, 5, 25, 25, kBidi_Force_LTR, paint, NULL);
});
diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp
new file mode 100644
index 0000000..7c57a50
--- /dev/null
+++ b/libs/hwui/tests/unit/RenderNodeTests.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "RenderNode.h"
+#include "TreeInfo.h"
+#include "tests/common/TestUtils.h"
+#include "utils/Color.h"
+
+using namespace android;
+using namespace android::uirenderer;
+
+TEST(RenderNode, hasParents) {
+ auto child = TestUtils::createNode(0, 0, 200, 400,
+ [](RenderProperties& props, TestCanvas& canvas) {
+ canvas.drawColor(Color::Red_500, SkXfermode::kSrcOver_Mode);
+ });
+ auto parent = TestUtils::createNode(0, 0, 200, 400,
+ [&child](RenderProperties& props, TestCanvas& canvas) {
+ canvas.drawRenderNode(child.get());
+ });
+
+ TestUtils::syncHierarchyPropertiesAndDisplayList(parent);
+
+ EXPECT_TRUE(child->hasParents()) << "Child node has no parent";
+ EXPECT_FALSE(parent->hasParents()) << "Root node shouldn't have any parents";
+
+ TestUtils::recordNode(*parent, [](TestCanvas& canvas) {
+ canvas.drawColor(Color::Amber_500, SkXfermode::kSrcOver_Mode);
+ });
+
+ EXPECT_TRUE(child->hasParents()) << "Child should still have a parent";
+ EXPECT_FALSE(parent->hasParents()) << "Root node shouldn't have any parents";
+
+ TestUtils::syncHierarchyPropertiesAndDisplayList(parent);
+
+ EXPECT_FALSE(child->hasParents()) << "Child should be removed";
+ EXPECT_FALSE(parent->hasParents()) << "Root node shouldn't have any parents";
+}
diff --git a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
index 586625b..875e260 100644
--- a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
+++ b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
@@ -41,3 +41,10 @@
EXPECT_EQ(SkShader::kRepeat_TileMode, xy[1]);
EXPECT_EQ(origBitmap.pixelRef(), bitmap.pixelRef());
}
+
+TEST(SkiaBehavior, genIds) {
+ SkBitmap bitmap = TestUtils::createSkBitmap(100, 100);
+ uint32_t genId = bitmap.getGenerationID();
+ bitmap.notifyPixelsChanged();
+ EXPECT_NE(genId, bitmap.getGenerationID());
+}
diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
new file mode 100644
index 0000000..5a01193
--- /dev/null
+++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "tests/common/TestUtils.h"
+
+#include <gtest/gtest.h>
+#include <RecordingCanvas.h>
+#include <SkPicture.h>
+#include <SkPictureRecorder.h>
+
+using namespace android;
+using namespace android::uirenderer;
+
+/**
+ * Verify that we get the same culling bounds for text for (1) drawing glyphs
+ * directly to a Canvas or (2) going through a SkPicture as an intermediate step.
+ */
+TEST(SkiaCanvasProxy, drawGlyphsViaPicture) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+ // setup test variables
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setTextSize(20);
+ paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ static const char* text = "testing text bounds";
+
+ // draw text directly into Recording canvas
+ TestUtils::drawUtf8ToCanvas(&canvas, text, paint, 25, 25);
+
+ // record the same text draw into a SkPicture and replay it into a Recording canvas
+ SkPictureRecorder recorder;
+ SkCanvas* skCanvas = recorder.beginRecording(200, 200, NULL, 0);
+ std::unique_ptr<Canvas> pictCanvas(Canvas::create_canvas(skCanvas));
+ TestUtils::drawUtf8ToCanvas(pictCanvas.get(), text, paint, 25, 25);
+ SkAutoTUnref<const SkPicture> picture(recorder.endRecording());
+
+ canvas.asSkCanvas()->drawPicture(picture);
+ });
+
+ // verify that the text bounds and matrices match
+ ASSERT_EQ(2U, dl->getOps().size());
+ auto directOp = dl->getOps()[0];
+ auto pictureOp = dl->getOps()[1];
+ ASSERT_EQ(RecordedOpId::TextOp, directOp->opId);
+ EXPECT_EQ(directOp->opId, pictureOp->opId);
+ EXPECT_EQ(directOp->unmappedBounds, pictureOp->unmappedBounds);
+ EXPECT_EQ(directOp->localMatrix, pictureOp->localMatrix);
+}
diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h
index db53713..4faab9a 100644
--- a/libs/hwui/utils/PaintUtils.h
+++ b/libs/hwui/utils/PaintUtils.h
@@ -67,6 +67,21 @@
&& getXfermode(paint.getXfermode()) == SkXfermode::kSrcOver_Mode;
}
+ static bool isOpaquePaint(const SkPaint* paint) {
+ if (!paint) return true; // default (paintless) behavior is SrcOver, black
+
+ if (paint->getAlpha() != 0xFF
+ || PaintUtils::isBlendedShader(paint->getShader())
+ || PaintUtils::isBlendedColorFilter(paint->getColorFilter())) {
+ return false;
+ }
+
+ // Only let simple srcOver / src blending modes declare opaque, since behavior is clear.
+ SkXfermode::Mode mode = getXfermode(paint->getXfermode());
+ return mode == SkXfermode::Mode::kSrcOver_Mode
+ || mode == SkXfermode::Mode::kSrc_Mode;
+ }
+
static bool isBlendedShader(const SkShader* shader) {
if (shader == nullptr) {
return false;
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index 17ce533..d78ccee 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -81,41 +81,42 @@
*/
public static final int MULTIPATH_INDICATOR_NOT_USED = 2;
- /**
- * The state of GNSS receiver the measurement is invalid or unknown.
- */
+ /** This GNSS measurement's tracking state is invalid or unknown. */
public static final int STATE_UNKNOWN = 0;
-
- /**
- * The state of the GNSS receiver is ranging code lock.
- */
+ /** This GNSS measurement's tracking state has code lock. */
public static final int STATE_CODE_LOCK = (1<<0);
-
- /**
- * The state of the GNSS receiver is in bit sync.
- */
+ /** This GNSS measurement's tracking state has bit sync. */
public static final int STATE_BIT_SYNC = (1<<1);
-
- /**
- *The state of the GNSS receiver is in sub-frame sync.
- */
+ /** This GNSS measurement's tracking state has sub-frame sync. */
public static final int STATE_SUBFRAME_SYNC = (1<<2);
-
- /**
- * The state of the GNSS receiver has TOW decoded.
- */
+ /** This GNSS measurement's tracking state has time-of-week decoded. */
public static final int STATE_TOW_DECODED = (1<<3);
-
- /**
- * The state of the GNSS receiver contains millisecond ambiguity.
- */
+ /** This GNSS measurement's tracking state contains millisecond ambiguity. */
public static final int STATE_MSEC_AMBIGUOUS = (1<<4);
+ /** This GNSS measurement's tracking state has symbol sync. */
+ public static final int STATE_SYMBOL_SYNC = (1<<5);
+ /** This Glonass measurement's tracking state has string sync. */
+ public static final int STATE_GLO_STRING_SYNC = (1<<6);
+ /** This Glonass measurement's tracking state has time-of-day decoded. */
+ public static final int STATE_GLO_TOD_DECODED = (1<<7);
+ /** This Beidou measurement's tracking state has D2 bit sync. */
+ public static final int STATE_BDS_D2_BIT_SYNC = (1<<8);
+ /** This Beidou measurement's tracking state has D2 sub-frame sync. */
+ public static final int STATE_BDS_D2_SUBFRAME_SYNC = (1<<9);
+ /** This Galileo measurement's tracking state has E1B/C code lock. */
+ public static final int STATE_GAL_E1BC_CODE_LOCK = (1<<10);
+ /** This Galileo measurement's tracking state has E1C secondary code lock. */
+ public static final int STATE_GAL_E1C_2ND_CODE_LOCK = (1<<11);
+ /** This Galileo measurement's tracking state has E1B page sync. */
+ public static final int STATE_GAL_E1B_PAGE_SYNC = (1<<12);
+ /** This SBAS measurement's tracking state has whole second level sync. */
+ public static final int STATE_SBAS_SYNC = (1<<13);
/**
- * All the GNSS receiver state flags.
+ * All the GNSS receiver state flags, for bit masking purposes (not a sensible state for any
+ * individual measurement.)
*/
- private static final int STATE_ALL = STATE_CODE_LOCK | STATE_BIT_SYNC | STATE_SUBFRAME_SYNC
- | STATE_TOW_DECODED | STATE_MSEC_AMBIGUOUS;
+ private static final int STATE_ALL = 0x3fff; // 2 bits + 4 bits + 4 bits + 4 bits = 14 bits
/**
* The state of the 'Accumulated Delta Range' is invalid or unknown.
@@ -276,29 +277,58 @@
if (mState == STATE_UNKNOWN) {
return "Unknown";
}
+
StringBuilder builder = new StringBuilder();
- if ((mState & STATE_CODE_LOCK) == STATE_CODE_LOCK) {
+ if ((mState & STATE_CODE_LOCK) != 0) {
builder.append("CodeLock|");
}
- if ((mState & STATE_BIT_SYNC) == STATE_BIT_SYNC) {
+ if ((mState & STATE_BIT_SYNC) != 0) {
builder.append("BitSync|");
}
- if ((mState & STATE_SUBFRAME_SYNC) == STATE_SUBFRAME_SYNC) {
+ if ((mState & STATE_SUBFRAME_SYNC) != 0) {
builder.append("SubframeSync|");
}
- if ((mState & STATE_TOW_DECODED) == STATE_TOW_DECODED) {
+ if ((mState & STATE_TOW_DECODED) != 0) {
builder.append("TowDecoded|");
}
- if ((mState & STATE_MSEC_AMBIGUOUS) == STATE_MSEC_AMBIGUOUS) {
- builder.append("MsecAmbiguous");
+ if ((mState & STATE_MSEC_AMBIGUOUS) != 0) {
+ builder.append("MsecAmbiguous|");
}
+ if ((mState & STATE_SYMBOL_SYNC) != 0) {
+ builder.append("SymbolSync|");
+ }
+ if ((mState & STATE_GLO_STRING_SYNC) != 0) {
+ builder.append("GloStringSync|");
+ }
+ if ((mState & STATE_GLO_TOD_DECODED) != 0) {
+ builder.append("GloTodDecoded|");
+ }
+ if ((mState & STATE_BDS_D2_BIT_SYNC) != 0) {
+ builder.append("BdsD2BitSync|");
+ }
+ if ((mState & STATE_BDS_D2_SUBFRAME_SYNC) != 0) {
+ builder.append("BdsD2SubframeSync|");
+ }
+ if ((mState & STATE_GAL_E1BC_CODE_LOCK) != 0) {
+ builder.append("GalE1bcCodeLock|");
+ }
+ if ((mState & STATE_GAL_E1C_2ND_CODE_LOCK) != 0) {
+ builder.append("E1c2ndCodeLock|");
+ }
+ if ((mState & STATE_GAL_E1B_PAGE_SYNC) != 0) {
+ builder.append("GalE1bPageSync|");
+ }
+ if ((mState & STATE_SBAS_SYNC) != 0) {
+ builder.append("SbasSync|");
+ }
+
int remainingStates = mState & ~STATE_ALL;
if (remainingStates > 0) {
builder.append("Other(");
builder.append(Integer.toBinaryString(remainingStates));
builder.append(")|");
}
- builder.deleteCharAt(builder.length() - 1);
+ builder.setLength(builder.length() - 1);
return builder.toString();
}
diff --git a/location/java/android/location/GnssStatus.java b/location/java/android/location/GnssStatus.java
index 9c509d6..d76feec 100644
--- a/location/java/android/location/GnssStatus.java
+++ b/location/java/android/location/GnssStatus.java
@@ -121,7 +121,7 @@
* @param satIndex the index of the satellite in the list.
*/
public float getElevationDegrees(int satIndex) {
- return 0f;
+ return mElevations[satIndex];
}
/**
diff --git a/location/java/android/location/INetInitiatedListener.aidl b/location/java/android/location/INetInitiatedListener.aidl
index f2f5a32..fc64dd6 100644
--- a/location/java/android/location/INetInitiatedListener.aidl
+++ b/location/java/android/location/INetInitiatedListener.aidl
@@ -1,26 +1,26 @@
-/*
-**
-** Copyright 2008, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-package android.location;
-
-/**
- * {@hide}
- */
-interface INetInitiatedListener
-{
- boolean sendNiResponse(int notifId, int userResponse);
-}
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.location;
+
+/**
+ * {@hide}
+ */
+interface INetInitiatedListener
+{
+ boolean sendNiResponse(int notifId, int userResponse);
+}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 3c42161..00eff91 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -2149,7 +2149,7 @@
}
if (listener != null) {
Log.d(TAG, "AudioManager dispatching onAudioFocusChange("
- + msg.what + ") for " + msg.obj);
+ + msg.arg1 + ") for " + msg.obj);
listener.onAudioFocusChange(msg.arg1);
}
break;
@@ -2157,7 +2157,7 @@
final RecordConfigChangeCallbackData cbData =
(RecordConfigChangeCallbackData) msg.obj;
if (cbData.mCb != null) {
- cbData.mCb.onRecordConfigChanged(cbData.mConfigs);
+ cbData.mCb.onRecordingConfigChanged(cbData.mConfigs);
}
break;
default:
@@ -2746,7 +2746,7 @@
* @param configs array containing the results of
* {@link AudioManager#getActiveRecordingConfigurations()}.
*/
- public void onRecordConfigChanged(AudioRecordingConfiguration[] configs) {}
+ public void onRecordingConfigChanged(AudioRecordingConfiguration[] configs) {}
}
private static class AudioRecordingCallbackInfo {
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index d9caf03..621129d 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -596,14 +596,14 @@
* AudioTrack player = new AudioTrack.Builder()
* .setAudioAttributes(new AudioAttributes.Builder()
* .setUsage(AudioAttributes.USAGE_ALARM)
- * .setContentType(CONTENT_TYPE_MUSIC)
+ * .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
* .build())
* .setAudioFormat(new AudioFormat.Builder()
* .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
- * .setSampleRate(441000)
+ * .setSampleRate(44100)
* .setChannelMask(AudioFormat.CHANNEL_OUT_STEREO)
* .build())
- * .setBufferSize(minBuffSize)
+ * .setBufferSizeInBytes(minBuffSize)
* .build();
* </pre>
* <p>
@@ -1120,7 +1120,7 @@
* <p> See also {@link AudioManager#getProperty(String)} for key
* {@link AudioManager#PROPERTY_OUTPUT_FRAMES_PER_BUFFER}.
* @return current size in frames of the <code>AudioTrack</code> buffer.
- * @throws IllegalStateException
+ * @throws IllegalStateException if track is not initialized.
*/
public int getBufferSizeInFrames() {
return native_get_buffer_size_frames();
@@ -1147,7 +1147,7 @@
* @param bufferSizeInFrames requested buffer size
* @return the actual buffer size in frames or an error code,
* {@link #ERROR_BAD_VALUE}, {@link #ERROR_INVALID_OPERATION}
- * @throws IllegalStateException
+ * @throws IllegalStateException if track is not initialized.
*/
public int setBufferSizeInFrames(int bufferSizeInFrames) {
if (mDataLoadMode == MODE_STATIC || mState == STATE_UNINITIALIZED) {
@@ -1176,7 +1176,7 @@
* <p> See also {@link AudioManager#getProperty(String)} for key
* {@link AudioManager#PROPERTY_OUTPUT_FRAMES_PER_BUFFER}.
* @return maximum size in frames of the <code>AudioTrack</code> buffer.
- * @throws IllegalStateException
+ * @throws IllegalStateException if track is not initialized.
*/
public int getBufferCapacityInFrames() {
return native_get_buffer_capacity_frames();
diff --git a/media/java/android/media/DrmInitData.java b/media/java/android/media/DrmInitData.java
index 06fe6ff..170d9de 100644
--- a/media/java/android/media/DrmInitData.java
+++ b/media/java/android/media/DrmInitData.java
@@ -28,6 +28,12 @@
public abstract class DrmInitData {
/**
+ * Prevent public constuctor access
+ */
+ /* package private */ DrmInitData() {
+ }
+
+ /**
* Retrieves initialization data for a given DRM scheme, specified by its UUID.
*
* @param schemeUuid The DRM scheme's UUID.
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 4bf0852..3d7e744 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -161,9 +161,14 @@
public static final String TAG_EXPOSURE_PROGRAM = "ExposureProgram";
/** Type is double. */
public static final String TAG_EXPOSURE_TIME = "ExposureTime";
- /** Type is String. */
+ /** Type is double. */
public static final String TAG_F_NUMBER = "FNumber";
- /** Type is String. */
+ /**
+ * Type is double.
+ *
+ * @deprecated use {@link #TAG_F_NUMBER} instead
+ */
+ @Deprecated
public static final String TAG_APERTURE = "FNumber";
/** Type is String. */
public static final String TAG_FILE_SOURCE = "FileSource";
@@ -185,9 +190,14 @@
public static final String TAG_FOCAL_PLANE_Y_RESOLUTION = "FocalPlaneYResolution";
/** Type is rational. */
public static final String TAG_GAIN_CONTROL = "GainControl";
- /** Type is String. */
+ /** Type is int. */
public static final String TAG_ISO_SPEED_RATINGS = "ISOSpeedRatings";
- /** Type is String. */
+ /**
+ * Type is int.
+ *
+ * @deprecated use {@link #TAG_ISO_SPEED_RATINGS} instead
+ */
+ @Deprecated
public static final String TAG_ISO = "ISOSpeedRatings";
/** Type is String. */
public static final String TAG_IMAGE_UNIQUE_ID = "ImageUniqueID";
@@ -225,8 +235,6 @@
public static final String TAG_SPECTRAL_SENSITIVITY = "SpectralSensitivity";
/** Type is int. */
public static final String TAG_SUBSEC_TIME = "SubSecTime";
- /** Type is int. @hide */
- public static final String TAG_SUBSECTIME = "SubSecTime";
/** Type is int. */
public static final String TAG_SUBSEC_TIME_DIGITIZED = "SubSecTimeDigitized";
/** Type is int. */
@@ -437,7 +445,7 @@
new ExifTag(TAG_F_NUMBER, 33437),
new ExifTag(TAG_EXPOSURE_PROGRAM, 34850),
new ExifTag(TAG_SPECTRAL_SENSITIVITY, 34852),
- new ExifTag(TAG_ISO, 34855),
+ new ExifTag(TAG_ISO_SPEED_RATINGS, 34855),
new ExifTag(TAG_OECF, 34856),
new ExifTag(TAG_EXIF_VERSION, 36864),
new ExifTag(TAG_DATETIME_ORIGINAL, 36867),
@@ -693,7 +701,7 @@
*/
public ExifInterface(FileDescriptor fileDescriptor) throws IOException {
if (fileDescriptor == null) {
- throw new IllegalArgumentException("parcelFileDescriptor cannot be null");
+ throw new IllegalArgumentException("fileDescriptor cannot be null");
}
mAssetInputStream = null;
mFilename = null;
@@ -705,7 +713,7 @@
try {
fileDescriptor = Os.dup(fileDescriptor);
} catch (ErrnoException e) {
- e.rethrowAsIOException();
+ throw e.rethrowAsIOException();
}
} else {
mSeekableFileDescriptor = null;
@@ -811,6 +819,9 @@
*/
public void setAttribute(String tag, String value) {
for (int i = 0 ; i < EXIF_TAGS.length; ++i) {
+ if (i == IFD_THUMBNAIL_HINT && !mHasThumbnail) {
+ continue;
+ }
if (sExifTagMapsForWriting[i].containsKey(tag)) {
mAttributes[i].put(tag, value);
}
@@ -818,6 +829,35 @@
}
/**
+ * Update the values of the tags in the tag groups if any value for the tag already was stored.
+ *
+ * @param tag the name of the tag.
+ * @param value the value of the tag.
+ * @return Returns {@code true} if updating is placed.
+ */
+ private boolean updateAttribute(String tag, String value) {
+ boolean updated = false;
+ for (int i = 0 ; i < EXIF_TAGS.length; ++i) {
+ if (mAttributes[i].containsKey(tag)) {
+ mAttributes[i].put(tag, value);
+ updated = true;
+ }
+ }
+ return updated;
+ }
+
+ /**
+ * Remove any values of the specified tag.
+ *
+ * @param tag the name of the tag.
+ */
+ private void removeAttribute(String tag) {
+ for (int i = 0 ; i < EXIF_TAGS.length; ++i) {
+ mAttributes[i].remove(tag);
+ }
+ }
+
+ /**
* This function decides which parser to read the image data according to the given input stream
* type and the content of the input stream. In each case, it reads the first three bytes to
* determine whether the image data format is JPEG or not.
@@ -853,7 +893,7 @@
} catch (IOException e) {
// Ignore exceptions in order to keep the compatibility with the old versions of
// ExifInterface.
- Log.w(TAG, "Invalid JPEG: ExifInterface got an unsupported image format file"
+ Log.w(TAG, "Invalid image: ExifInterface got an unsupported image format file"
+ "(ExifInterface supports JPEG and some RAW image formats only) "
+ "or a corrupted JPEG file to ExifInterface.", e);
} finally {
@@ -882,27 +922,22 @@
// Mark for disabling the save feature.
mIsRaw = true;
- for (Map.Entry entry : (Set<Map.Entry>) map.entrySet()) {
- String attrName = (String) entry.getKey();
-
- switch (attrName) {
- case TAG_HAS_THUMBNAIL:
- mHasThumbnail = ((String) entry.getValue()).equalsIgnoreCase("true");
- break;
- case TAG_THUMBNAIL_OFFSET:
- mThumbnailOffset = Integer.parseInt((String) entry.getValue());
- break;
- case TAG_THUMBNAIL_LENGTH:
- mThumbnailLength = Integer.parseInt((String) entry.getValue());
- break;
- case TAG_THUMBNAIL_DATA:
- mThumbnailBytes = (byte[]) entry.getValue();
- break;
- default:
- setAttribute(attrName, (String) entry.getValue());
- break;
- }
+ String value = (String) map.remove(TAG_HAS_THUMBNAIL);
+ mHasThumbnail = value != null && value.equalsIgnoreCase("true");
+ value = (String) map.remove(TAG_THUMBNAIL_OFFSET);
+ if (value != null) {
+ mThumbnailOffset = Integer.parseInt(value);
}
+ value = (String) map.remove(TAG_THUMBNAIL_LENGTH);
+ if (value != null) {
+ mThumbnailLength = Integer.parseInt(value);
+ }
+ mThumbnailBytes = (byte[]) map.remove(TAG_THUMBNAIL_DATA);
+
+ for (Map.Entry entry : (Set<Map.Entry>) map.entrySet()) {
+ setAttribute((String) entry.getKey(), (String) entry.getValue());
+ }
+
return true;
}
@@ -928,7 +963,7 @@
/**
* Save the tag data into the original image file. This is expensive because it involves
* copying all the data from one file to another and deleting the old file and renaming the
- * other. It's best to use{@link #setAttribute(String,String)} to set all attributes to write
+ * other. It's best to use {@link #setAttribute(String,String)} to set all attributes to write
* and make a single call rather than multiple calls for each attribute.
*/
public void saveAttributes() throws IOException {
@@ -963,7 +998,7 @@
Streams.copy(in, out);
}
} catch (ErrnoException e) {
- e.rethrowAsIOException();
+ throw e.rethrowAsIOException();
} finally {
IoUtils.closeQuietly(in);
IoUtils.closeQuietly(out);
@@ -982,7 +1017,7 @@
}
saveJpegAttributes(in, out);
} catch (ErrnoException e) {
- e.rethrowAsIOException();
+ throw e.rethrowAsIOException();
} finally {
IoUtils.closeQuietly(in);
IoUtils.closeQuietly(out);
@@ -1052,12 +1087,16 @@
*
* @return two-element array, the offset in the first value, and length in
* the second, or {@code null} if no thumbnail was found.
- * @hide
*/
public long[] getThumbnailRange() {
+ if (!mHasThumbnail) {
+ return null;
+ }
+
long[] range = new long[2];
range[0] = mThumbnailOffset;
range[1] = mThumbnailLength;
+
return range;
}
@@ -1120,7 +1159,7 @@
if (datetime == null) return -1;
long msecs = datetime.getTime();
- String subSecs = getAttribute(TAG_SUBSECTIME);
+ String subSecs = getAttribute(TAG_SUBSEC_TIME);
if (subSecs != null) {
try {
long sub = Long.valueOf(subSecs);
@@ -1276,7 +1315,8 @@
throw new IOException("Invalid exif");
}
length = 0;
- setAttribute("UserComment", new String(bytes, Charset.forName("US-ASCII")));
+ mAttributes[IFD_EXIF_HINT].put(TAG_USER_COMMENT,
+ new String(bytes, Charset.forName("US-ASCII")));
break;
}
@@ -1296,9 +1336,10 @@
if (dataInputStream.skipBytes(1) != 1) {
throw new IOException("Invalid SOFx");
}
- setAttribute("ImageLength",
+ mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_LENGTH,
String.valueOf(dataInputStream.readUnsignedShort()));
- setAttribute("ImageWidth", String.valueOf(dataInputStream.readUnsignedShort()));
+ mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_WIDTH,
+ String.valueOf(dataInputStream.readUnsignedShort()));
length -= 5;
break;
}
@@ -1510,7 +1551,7 @@
convertToDouble(TAG_EXPOSURE_TIME);
convertToDouble(TAG_F_NUMBER);
convertToDouble(TAG_SUBJECT_DISTANCE);
- convertToInt(TAG_ISO);
+ convertToInt(TAG_ISO_SPEED_RATINGS);
convertToDouble(TAG_EXPOSURE_BIAS_VALUE);
convertToInt(TAG_WHITE_BALANCE);
convertToInt(TAG_LIGHT_SOURCE);
@@ -1521,31 +1562,31 @@
convertToInt(TAG_GPS_ALTITUDE_REF);
convertToRational(TAG_GPS_LONGITUDE);
convertToRational(TAG_GPS_LATITUDE);
- convertToTimetamp(TAG_GPS_TIMESTAMP);
+ convertToTimestamp(TAG_GPS_TIMESTAMP);
// The value of DATETIME tag has the same value of DATETIME_ORIGINAL tag.
- String valueOfDateTimeOriginal = getAttribute("DateTimeOriginal");
+ String valueOfDateTimeOriginal = getAttribute(TAG_DATETIME_ORIGINAL);
if (valueOfDateTimeOriginal != null) {
- setAttribute(TAG_DATETIME, valueOfDateTimeOriginal);
+ mAttributes[IFD_TIFF_HINT].put(TAG_DATETIME, valueOfDateTimeOriginal);
}
// Add the default value.
if (getAttribute(TAG_IMAGE_WIDTH) == null) {
- setAttribute(TAG_IMAGE_WIDTH, "0");
+ mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_WIDTH, "0");
}
if (getAttribute(TAG_IMAGE_LENGTH) == null) {
- setAttribute(TAG_IMAGE_LENGTH, "0");
+ mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_LENGTH, "0");
}
if (getAttribute(TAG_ORIENTATION) == null) {
- setAttribute(TAG_ORIENTATION, "0");
+ mAttributes[IFD_TIFF_HINT].put(TAG_ORIENTATION, "0");
}
if (getAttribute(TAG_LIGHT_SOURCE) == null) {
- setAttribute(TAG_LIGHT_SOURCE, "0");
+ mAttributes[IFD_EXIF_HINT].put(TAG_LIGHT_SOURCE, "0");
}
}
// Converts the tag value to timestamp; Otherwise deletes the given tag.
- private void convertToTimetamp(String tagName) {
+ private void convertToTimestamp(String tagName) {
String entryValue = getAttribute(tagName);
if (entryValue == null) return;
int dataFormat = getDataFormatOfExifEntryValue(entryValue);
@@ -1566,9 +1607,9 @@
int value = numerator / denominator;
stringBuilder.append(String.format("%02d", value));
}
- setAttribute(tagName, stringBuilder.toString());
+ updateAttribute(tagName, stringBuilder.toString());
} else if (dataFormat != IFD_FORMAT_STRING) {
- setAttribute(tagName, null);
+ removeAttribute(tagName);
}
}
@@ -1595,14 +1636,14 @@
}
stringBuilder.append((double) numerator / denominator);
}
- setAttribute(tagName, stringBuilder.toString());
+ updateAttribute(tagName, stringBuilder.toString());
break;
}
case IFD_FORMAT_DOUBLE:
// Keep it as is.
break;
default:
- setAttribute(tagName, null);
+ removeAttribute(tagName);
break;
}
}
@@ -1624,14 +1665,14 @@
double doubleValue = Double.parseDouble(component);
stringBuilder.append((int) (doubleValue * 10000.0)).append("/").append(10000);
}
- setAttribute(tagName, stringBuilder.toString());
+ updateAttribute(tagName, stringBuilder.toString());
break;
}
case IFD_FORMAT_SRATIONAL:
// Keep it as is.
break;
default:
- setAttribute(tagName, null);
+ removeAttribute(tagName);
break;
}
}
@@ -1642,7 +1683,7 @@
if (entryValue == null) return;
int dataFormat = getDataFormatOfExifEntryValue(entryValue);
if (dataFormat != IFD_FORMAT_SLONG) {
- setAttribute(tagName, null);
+ removeAttribute(tagName);
}
}
@@ -1758,7 +1799,7 @@
String entryValue = readExifEntryValue(
dataInputStream, dataFormat, numberOfComponents);
if (entryValue != null) {
- setAttribute(tagName, entryValue);
+ mAttributes[hint].put(tagName, entryValue);
}
} else {
StringBuilder entryValueBuilder = new StringBuilder();
@@ -1769,7 +1810,7 @@
entryValueBuilder.append(readExifEntryValue(
dataInputStream, dataFormat, numberOfComponents));
}
- setAttribute(tagName, entryValueBuilder.toString());
+ mAttributes[hint].put(tagName, entryValueBuilder.toString());
}
if (dataInputStream.peek() != nextEntryOffset) {
@@ -1886,11 +1927,11 @@
// Remove IFD pointer tags (we'll re-add it later.)
for (ExifTag tag : IFD_POINTER_TAGS) {
- setAttribute(tag.name, null);
+ removeAttribute(tag.name);
}
// Remove old thumbnail data
- setAttribute(JPEG_INTERCHANGE_FORMAT_TAG.name, null);
- setAttribute(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name, null);
+ removeAttribute(JPEG_INTERCHANGE_FORMAT_TAG.name);
+ removeAttribute(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name);
// Remove null value tags.
for (int hint = 0; hint < EXIF_TAGS.length; ++hint) {
diff --git a/media/java/android/media/MediaActionSound.java b/media/java/android/media/MediaActionSound.java
index 1fee587..983ca75 100644
--- a/media/java/android/media/MediaActionSound.java
+++ b/media/java/android/media/MediaActionSound.java
@@ -45,8 +45,7 @@
private static final int NUM_MEDIA_SOUND_STREAMS = 1;
private SoundPool mSoundPool;
- private int[] mSoundIds;
- private int mSoundIdToPlay;
+ private SoundState[] mSounds;
private static final String[] SOUND_FILES = {
"/system/media/audio/ui/camera_click.ogg",
@@ -88,22 +87,57 @@
*/
public static final int STOP_VIDEO_RECORDING = 3;
- private static final int SOUND_NOT_LOADED = -1;
+ /**
+ * States for SoundState.
+ * STATE_NOT_LOADED : sample not loaded
+ * STATE_LOADING : sample being loaded: waiting for load completion callback
+ * STATE_LOADING_PLAY_REQUESTED : sample being loaded and playback request received
+ * STATE_LOADED : sample loaded, ready for playback
+ */
+ private static final int STATE_NOT_LOADED = 0;
+ private static final int STATE_LOADING = 1;
+ private static final int STATE_LOADING_PLAY_REQUESTED = 2;
+ private static final int STATE_LOADED = 3;
+ private class SoundState {
+ public final int name;
+ public int id;
+ public int state;
+
+ public SoundState(int name) {
+ this.name = name;
+ id = 0; // 0 is an invalid sample ID.
+ state = STATE_NOT_LOADED;
+ }
+ }
/**
* Construct a new MediaActionSound instance. Only a single instance is
* needed for playing any platform media action sound; you do not need a
* separate instance for each sound type.
*/
public MediaActionSound() {
- mSoundPool = new SoundPool(NUM_MEDIA_SOUND_STREAMS,
- AudioManager.STREAM_SYSTEM_ENFORCED, 0);
+ mSoundPool = new SoundPool.Builder()
+ .setMaxStreams(NUM_MEDIA_SOUND_STREAMS)
+ .setAudioAttributes(new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
+ .setFlags(AudioAttributes.FLAG_AUDIBILITY_ENFORCED)
+ .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+ .build())
+ .build();
mSoundPool.setOnLoadCompleteListener(mLoadCompleteListener);
- mSoundIds = new int[SOUND_FILES.length];
- for (int i = 0; i < mSoundIds.length; i++) {
- mSoundIds[i] = SOUND_NOT_LOADED;
+ mSounds = new SoundState[SOUND_FILES.length];
+ for (int i = 0; i < mSounds.length; i++) {
+ mSounds[i] = new SoundState(i);
}
- mSoundIdToPlay = SOUND_NOT_LOADED;
+ }
+
+ private int loadSound(SoundState sound) {
+ int id = mSoundPool.load(SOUND_FILES[sound.name], 1);
+ if (id > 0) {
+ sound.state = STATE_LOADING;
+ sound.id = id;
+ }
+ return id;
}
/**
@@ -118,13 +152,22 @@
* @see #START_VIDEO_RECORDING
* @see #STOP_VIDEO_RECORDING
*/
- public synchronized void load(int soundName) {
+ public void load(int soundName) {
if (soundName < 0 || soundName >= SOUND_FILES.length) {
throw new RuntimeException("Unknown sound requested: " + soundName);
}
- if (mSoundIds[soundName] == SOUND_NOT_LOADED) {
- mSoundIds[soundName] =
- mSoundPool.load(SOUND_FILES[soundName], 1);
+ SoundState sound = mSounds[soundName];
+ synchronized (sound) {
+ switch (sound.state) {
+ case STATE_NOT_LOADED:
+ if (loadSound(sound) <= 0) {
+ Log.e(TAG, "load() error loading sound: " + soundName);
+ }
+ break;
+ default:
+ Log.e(TAG, "load() called in wrong state: " + sound + " for sound: "+ soundName);
+ break;
+ }
}
}
@@ -159,16 +202,31 @@
* @see #START_VIDEO_RECORDING
* @see #STOP_VIDEO_RECORDING
*/
- public synchronized void play(int soundName) {
+ public void play(int soundName) {
if (soundName < 0 || soundName >= SOUND_FILES.length) {
throw new RuntimeException("Unknown sound requested: " + soundName);
}
- if (mSoundIds[soundName] == SOUND_NOT_LOADED) {
- mSoundIdToPlay =
- mSoundPool.load(SOUND_FILES[soundName], 1);
- mSoundIds[soundName] = mSoundIdToPlay;
- } else {
- mSoundPool.play(mSoundIds[soundName], 1.0f, 1.0f, 0, 0, 1.0f);
+ SoundState sound = mSounds[soundName];
+ synchronized (sound) {
+ switch (sound.state) {
+ case STATE_NOT_LOADED:
+ loadSound(sound);
+ if (loadSound(sound) <= 0) {
+ Log.e(TAG, "play() error loading sound: " + soundName);
+ break;
+ }
+ // FALL THROUGH
+
+ case STATE_LOADING:
+ sound.state = STATE_LOADING_PLAY_REQUESTED;
+ break;
+ case STATE_LOADED:
+ mSoundPool.play(sound.id, 1.0f, 1.0f, 0, 0, 1.0f);
+ break;
+ default:
+ Log.e(TAG, "play() called in wrong state: " + sound.state + " for sound: "+ soundName);
+ break;
+ }
}
}
@@ -176,14 +234,37 @@
new SoundPool.OnLoadCompleteListener() {
public void onLoadComplete(SoundPool soundPool,
int sampleId, int status) {
- if (status == 0) {
- if (mSoundIdToPlay == sampleId) {
- soundPool.play(sampleId, 1.0f, 1.0f, 0, 0, 1.0f);
- mSoundIdToPlay = SOUND_NOT_LOADED;
+ for (SoundState sound : mSounds) {
+ if (sound.id != sampleId) {
+ continue;
}
- } else {
- Log.e(TAG, "Unable to load sound for playback (status: " +
- status + ")");
+ int playSoundId = 0;
+ synchronized (sound) {
+ if (status != 0) {
+ sound.state = STATE_NOT_LOADED;
+ sound.id = 0;
+ Log.e(TAG, "OnLoadCompleteListener() error: " + status +
+ " loading sound: "+ sound.name);
+ return;
+ }
+ switch (sound.state) {
+ case STATE_LOADING:
+ sound.state = STATE_LOADED;
+ break;
+ case STATE_LOADING_PLAY_REQUESTED:
+ playSoundId = sound.id;
+ sound.state = STATE_LOADED;
+ break;
+ default:
+ Log.e(TAG, "OnLoadCompleteListener() called in wrong state: "
+ + sound.state + " for sound: "+ sound.name);
+ break;
+ }
+ }
+ if (playSoundId != 0) {
+ soundPool.play(playSoundId, 1.0f, 1.0f, 0, 0, 1.0f);
+ }
+ break;
}
}
};
@@ -195,6 +276,12 @@
*/
public void release() {
if (mSoundPool != null) {
+ for (SoundState sound : mSounds) {
+ synchronized (sound) {
+ sound.state = STATE_NOT_LOADED;
+ sound.id = 0;
+ }
+ }
mSoundPool.release();
mSoundPool = null;
}
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index b1c1b79..2bd9781 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -2666,12 +2666,13 @@
public static final int HEVCHighTierLevel62 = 0x2000000;
// from OMX_VIDEO_DOLBYVISIONPROFILETYPE
- public static final int DolbyVisionProfileDvavDer = 0x1;
- public static final int DolbyVisionProfileDvavDen = 0x2;
- public static final int DolbyVisionProfileDvheDer = 0x3;
- public static final int DolbyVisionProfileDvheDen = 0x4;
- public static final int DolbyVisionProfileDvheDtr = 0x5;
- public static final int DolbyVisionProfileDvheStn = 0x6;
+ public static final int DolbyVisionProfileDvavPer = 0x1;
+ public static final int DolbyVisionProfileDvavPen = 0x2;
+ public static final int DolbyVisionProfileDvheDer = 0x4;
+ public static final int DolbyVisionProfileDvheDen = 0x8;
+ public static final int DolbyVisionProfileDvheDtr = 0x10;
+ public static final int DolbyVisionProfileDvheStn = 0x20;
+ public static final int DolbyVisionProfileDvheDth = 0x40;
// from OMX_VIDEO_DOLBYVISIONLEVELTYPE
public static final int DolbyVisionLevelHd24 = 0x1;
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index 177344a..24a400e4 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -275,16 +275,23 @@
return initDataMap.get(schemeUuid);
}
};
- } else if (formatMap.containsKey("crypto-key")) {
- ByteBuffer buf = (ByteBuffer) formatMap.get("crypto-key");
- buf.rewind();
- final byte[] data = new byte[buf.remaining()];
- buf.get(data);
- return new DrmInitData() {
- public SchemeInitData get(UUID schemeUuid) {
- return new DrmInitData.SchemeInitData("webm", data);
+ } else {
+ int numTracks = getTrackCount();
+ for (int i = 0; i < numTracks; ++i) {
+ Map<String, Object> trackFormatMap = getTrackFormatNative(i);
+ if (!trackFormatMap.containsKey("crypto-key")) {
+ continue;
}
- };
+ ByteBuffer buf = (ByteBuffer) trackFormatMap.get("crypto-key");
+ buf.rewind();
+ final byte[] data = new byte[buf.remaining()];
+ buf.get(data);
+ return new DrmInitData() {
+ public SchemeInitData get(UUID schemeUuid) {
+ return new DrmInitData.SchemeInitData("webm", data);
+ }
+ };
+ }
}
return null;
}
diff --git a/media/java/android/media/MediaMuxer.java b/media/java/android/media/MediaMuxer.java
index ebe509c..7117fbd 100644
--- a/media/java/android/media/MediaMuxer.java
+++ b/media/java/android/media/MediaMuxer.java
@@ -34,7 +34,7 @@
/**
* MediaMuxer facilitates muxing elementary streams. Currently supports mp4 or
* webm file as the output and at most one audio and/or one video elementary
- * stream.
+ * stream. MediaMuxer does not support muxing B-frames.
* <p>
* It is generally used like this:
*
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index fe2796c..7c6adad 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -57,8 +57,9 @@
* <h3>Standard Extra Data</h3>
*
* <p>These are the current standard fields that can be used as extra data via
- * {@link #subscribe(String, Bundle, SubscriptionCallback)}, {@link #unsubscribe(String, Bundle)},
- * and {@link SubscriptionCallback#onChildrenLoaded(String, List, Bundle)}.
+ * {@link #subscribe(String, Bundle, SubscriptionCallback)},
+ * {@link #unsubscribe(String, SubscriptionCallback)}, and
+ * {@link SubscriptionCallback#onChildrenLoaded(String, List, Bundle)}.
*
* <ul>
* <li> {@link #EXTRA_PAGE}
@@ -71,7 +72,7 @@
/**
* Used as an int extra field to denote the page number to subscribe.
- * The value of {@code EXTRA_PAGE} should be greater than or equal to 1.
+ * The value of {@code EXTRA_PAGE} should be greater than or equal to 0.
*
* @see #EXTRA_PAGE_SIZE
*/
@@ -383,7 +384,7 @@
}
/**
- * Unsubscribes for changes to the children of the specified media id.
+ * Unsubscribes for changes to the children of the specified media id through a callback.
* <p>
* The query callback will no longer be invoked for results associated with
* this id once this method returns.
@@ -391,13 +392,13 @@
*
* @param parentId The id of the parent media item whose list of children
* will be unsubscribed.
- * @param options A bundle sent to the media browse service to subscribe.
+ * @param callback A callback sent to the media browse service to subscribe.
*/
- public void unsubscribe(@NonNull String parentId, @NonNull Bundle options) {
- if (options == null) {
- throw new IllegalArgumentException("options are null");
+ public void unsubscribe(@NonNull String parentId, @NonNull SubscriptionCallback callback) {
+ if (callback == null) {
+ throw new IllegalArgumentException("callback is null");
}
- unsubscribeInternal(parentId, options);
+ unsubscribeInternal(parentId, callback);
}
/**
@@ -490,7 +491,7 @@
}
}
- private void unsubscribeInternal(String parentId, Bundle options) {
+ private void unsubscribeInternal(String parentId, SubscriptionCallback callback) {
// Check arguments.
if (TextUtils.isEmpty(parentId)) {
throw new IllegalArgumentException("parentId is empty.");
@@ -500,16 +501,21 @@
Subscription sub = mSubscriptions.get(parentId);
// Tell the service if necessary.
- if (sub != null && sub.removeCallback(options) && mState == CONNECT_STATE_CONNECTED) {
+ if (mState == CONNECT_STATE_CONNECTED && sub != null) {
try {
- // NOTE: Do not call removeSubscriptionWithOptions when options are null. Otherwise,
- // it will break the action of support library which expects removeSubscription will
- // be called when options are null.
- if (options == null) {
+ if (callback == null) {
mServiceBinder.removeSubscription(parentId, mServiceCallbacks);
} else {
- mServiceBinder.removeSubscriptionWithOptions(
- parentId, options, mServiceCallbacks);
+ final List<SubscriptionCallback> callbacks = sub.getCallbacks();
+ final List<Bundle> optionsList = sub.getOptionsList();
+ for (int i = callbacks.size() - 1; i >= 0; --i) {
+ if (callbacks.get(i) == callback) {
+ mServiceBinder.removeSubscriptionWithOptions(
+ parentId, optionsList.get(i), mServiceCallbacks);
+ callbacks.remove(i);
+ optionsList.remove(i);
+ }
+ }
}
} catch (RemoteException ex) {
// Process is crashing. We will disconnect, and upon reconnect we will
@@ -517,7 +523,8 @@
Log.d(TAG, "removeSubscription failed with RemoteException parentId=" + parentId);
}
}
- if (sub != null && sub.isEmpty()) {
+
+ if (sub != null && (sub.isEmpty() || callback == null)) {
mSubscriptions.remove(parentId);
}
}
@@ -1118,16 +1125,5 @@
mCallbacks.add(callback);
mOptionsList.add(options);
}
-
- public boolean removeCallback(Bundle options) {
- for (int i = 0; i < mOptionsList.size(); ++i) {
- if (MediaBrowserUtils.areSameOptions(mOptionsList.get(i), options)) {
- mCallbacks.remove(i);
- mOptionsList.remove(i);
- return true;
- }
- }
- return false;
- }
}
}
diff --git a/media/java/android/media/browse/MediaBrowserUtils.java b/media/java/android/media/browse/MediaBrowserUtils.java
index b06e598..2943e60 100644
--- a/media/java/android/media/browse/MediaBrowserUtils.java
+++ b/media/java/android/media/browse/MediaBrowserUtils.java
@@ -50,7 +50,7 @@
startIndex1 = 0;
endIndex1 = Integer.MAX_VALUE;
} else {
- startIndex1 = pageSize1 * (page1 - 1);
+ startIndex1 = pageSize1 * page1;
endIndex1 = startIndex1 + pageSize1 - 1;
}
@@ -58,7 +58,7 @@
startIndex2 = 0;
endIndex2 = Integer.MAX_VALUE;
} else {
- startIndex2 = pageSize2 * (page2 - 1);
+ startIndex2 = pageSize2 * page2;
endIndex2 = startIndex2 + pageSize2 - 1;
}
diff --git a/media/java/android/media/midi/IMidiDeviceServer.aidl b/media/java/android/media/midi/IMidiDeviceServer.aidl
index c2cc2b9..d5115de 100644
--- a/media/java/android/media/midi/IMidiDeviceServer.aidl
+++ b/media/java/android/media/midi/IMidiDeviceServer.aidl
@@ -28,7 +28,8 @@
void closeDevice();
// connects the input port pfd to the specified output port
- void connectPorts(IBinder token, in ParcelFileDescriptor pfd, int outputPortNumber);
+ // Returns the PID of the called process.
+ int connectPorts(IBinder token, in ParcelFileDescriptor pfd, int outputPortNumber);
MidiDeviceInfo getDeviceInfo();
void setDeviceInfo(in MidiDeviceInfo deviceInfo);
diff --git a/media/java/android/media/midi/MidiDevice.java b/media/java/android/media/midi/MidiDevice.java
index e1990cd..e4588fe 100644
--- a/media/java/android/media/midi/MidiDevice.java
+++ b/media/java/android/media/midi/MidiDevice.java
@@ -19,6 +19,7 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
+import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
@@ -181,9 +182,16 @@
}
try {
IBinder token = new Binder();
- mDeviceServer.connectPorts(token, pfd, outputPortNumber);
- // close our copy of the file descriptor
- IoUtils.closeQuietly(pfd);
+ int calleePid = mDeviceServer.connectPorts(token, pfd, outputPortNumber);
+ // If the service is a different Process then it will duplicate the pfd
+ // and we can safely close this one.
+ // But if the service is in the same Process then closing the pfd will
+ // kill the connection. So don't do that.
+ if (calleePid != Process.myPid()) {
+ // close our copy of the file descriptor
+ IoUtils.closeQuietly(pfd);
+ }
+
return new MidiConnection(token, inputPort);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in connectPorts");
diff --git a/media/java/android/media/midi/MidiDeviceServer.java b/media/java/android/media/midi/MidiDeviceServer.java
index 19ff624..f0abf71 100644
--- a/media/java/android/media/midi/MidiDeviceServer.java
+++ b/media/java/android/media/midi/MidiDeviceServer.java
@@ -254,7 +254,7 @@
}
@Override
- public void connectPorts(IBinder token, ParcelFileDescriptor pfd,
+ public int connectPorts(IBinder token, ParcelFileDescriptor pfd,
int outputPortNumber) {
MidiInputPort inputPort = new MidiInputPort(pfd, outputPortNumber);
MidiDispatcher dispatcher = mOutputPortDispatchers[outputPortNumber];
@@ -270,6 +270,7 @@
synchronized (mPortClients) {
mPortClients.put(token, client);
}
+ return Process.myPid(); // for caller to detect same process ID
}
@Override
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index 7c9591d..03dc699 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -116,10 +116,10 @@
private final int mType;
private final boolean mIsHardwareInput;
- // TODO: Remove mLabel and mIconUri when createTvInputInfo() is removed.
- private String mLabel;
+ // TODO: Remove mIconUri when createTvInputInfo() is removed.
private Uri mIconUri;
+ private final CharSequence mLabel;
private final int mLabelResId;
private final Icon mIcon;
private final Icon mIconStandby;
@@ -161,8 +161,8 @@
TvInputInfo info = new TvInputInfo.Builder(context, service)
.setHdmiDeviceInfo(hdmiDeviceInfo)
.setParentId(parentId)
+ .setLabel(label)
.build();
- info.mLabel = label;
info.mIconUri = iconUri;
return info;
}
@@ -215,8 +215,8 @@
throws XmlPullParserException, IOException {
TvInputInfo info = new TvInputInfo.Builder(context, service)
.setTvInputHardwareInfo(hardwareInfo)
+ .setLabel(label)
.build();
- info.mLabel = label;
info.mIconUri = iconUri;
return info;
}
@@ -247,7 +247,7 @@
}
private TvInputInfo(ResolveInfo service, String id, int type, boolean isHardwareInput,
- int labelResId, Icon icon, Icon iconStandby, Icon iconDisconnected,
+ CharSequence label, int labelResId, Icon icon, Icon iconStandby, Icon iconDisconnected,
String setupActivity, String settingsActivity, boolean canRecord, int tunerCount,
HdmiDeviceInfo hdmiDeviceInfo, boolean isConnectedToHdmiSwitch, String parentId,
Bundle extras) {
@@ -255,6 +255,7 @@
mId = id;
mType = type;
mIsHardwareInput = isHardwareInput;
+ mLabel = label;
mLabelResId = labelResId;
mIcon = icon;
mIconStandby = iconStandby;
@@ -570,7 +571,7 @@
dest.writeString(mId);
dest.writeInt(mType);
dest.writeByte(mIsHardwareInput ? (byte) 1 : 0);
- dest.writeString(mLabel);
+ TextUtils.writeToParcel(mLabel, dest, flags);
dest.writeParcelable(mIconUri, flags);
dest.writeInt(mLabelResId);
dest.writeParcelable(mIcon, flags);
@@ -612,7 +613,7 @@
mId = in.readString();
mType = in.readInt();
mIsHardwareInput = in.readByte() == 1;
- mLabel = in.readString();
+ mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
mIconUri = in.readParcelable(null);
mLabelResId = in.readInt();
mIcon = in.readParcelable(null);
@@ -660,6 +661,7 @@
private final Context mContext;
private final ResolveInfo mResolveInfo;
+ private CharSequence mLabel;
private int mLabelResId;
private Icon mIcon;
private Icon mIconStandby;
@@ -746,12 +748,31 @@
/**
* Sets the label.
*
+ * @param label The text to be used as label.
+ * @return This Builder object to allow for chaining of calls to builder methods.
+ * @hide
+ */
+ @SystemApi
+ public Builder setLabel(CharSequence label) {
+ if (mLabelResId != 0) {
+ throw new IllegalStateException("Resource ID for label is already set.");
+ }
+ this.mLabel = label;
+ return this;
+ }
+
+ /**
+ * Sets the label.
+ *
* @param resId The resource ID of the text to use.
* @return This Builder object to allow for chaining of calls to builder methods.
* @hide
*/
@SystemApi
public Builder setLabel(int resId) {
+ if (mLabel != null) {
+ throw new IllegalStateException("Label text is already set.");
+ }
this.mLabelResId = resId;
return this;
}
@@ -868,8 +889,8 @@
type = TYPE_TUNER;
}
parseServiceMetadata(type);
- return new TvInputInfo(mResolveInfo, id, type, isHardwareInput, mLabelResId, mIcon,
- mIconStandby, mIconDisconnected, mSetupActivity, mSettingsActivity,
+ return new TvInputInfo(mResolveInfo, id, type, isHardwareInput, mLabel, mLabelResId,
+ mIcon, mIconStandby, mIconDisconnected, mSetupActivity, mSettingsActivity,
mCanRecord == null ? false : mCanRecord, mTunerCount == null ? 0 : mTunerCount,
mHdmiDeviceInfo, isConnectedToHdmiSwitch, mParentId, mExtras);
}
@@ -1039,6 +1060,15 @@
}
Settings.Secure.putStringForUser(context.getContentResolver(),
Settings.Secure.TV_INPUT_HIDDEN_INPUTS, builder.toString(), userId);
+
+ // Notify of the TvInputInfo changes.
+ TvInputManager tm = (TvInputManager) context.getSystemService(Context.TV_INPUT_SERVICE);
+ for (String inputId : hiddenInputIds) {
+ TvInputInfo info = tm.getTvInputInfo(inputId);
+ if (info != null) {
+ tm.updateTvInputInfo(info);
+ }
+ }
}
/**
@@ -1069,6 +1099,15 @@
}
Settings.Secure.putStringForUser(context.getContentResolver(),
Settings.Secure.TV_INPUT_CUSTOM_LABELS, builder.toString(), userId);
+
+ // Notify of the TvInputInfo changes.
+ TvInputManager tm = (TvInputManager) context.getSystemService(Context.TV_INPUT_SERVICE);
+ for (String inputId : customLabels.keySet()) {
+ TvInputInfo info = tm.getTvInputInfo(inputId);
+ if (info != null) {
+ tm.updateTvInputInfo(info);
+ }
+ }
}
private static void ensureValidField(String value) {
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index c72a7a0..b4536b1 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -56,7 +56,26 @@
/**
* Central system API to the overall TV input framework (TIF) architecture, which arbitrates
- * interaction between applications and the selected TV inputs.
+ * interaction between applications and the selected TV inputs. You can retrieve an instance of
+ * this interface with {@link android.content.Context#getSystemService
+ * Context.getSystemService(Context.TV_INPUT_SERVICE)}.
+ *
+ * <p>There are three primary parties involved in the TV input framework (TIF) architecture:
+ *
+ * <ul>
+ * <li>The <strong>TV input manager</strong> as expressed by this class is the central point of the
+ * system that manages interaction between all other parts. It is expressed as the client-side API
+ * here which exists in each application context and communicates with a global system service that
+ * manages the interaction across all processes.
+ * <li>A <strong>TV input</strong> implemented by {@link TvInputService} represents an input source
+ * of TV, which can be a pass-through input such as HDMI, or a tuner input which provides broadcast
+ * TV programs. The system binds to the TV input per application’s request.
+ * on implementing TV inputs.
+ * <li><strong>Applications</strong> talk to the TV input manager to list TV inputs and check their
+ * status. Once an application find the input to use, it uses {@link TvView} or
+ * {@link TvRecordingClient} for further interaction such as watching and recording broadcast TV
+ * programs.
+ * </ul>
*/
public final class TvInputManager {
private static final String TAG = "TvInputManager";
@@ -824,11 +843,21 @@
/**
* Interface used to receive events from Hardware objects.
+ *
* @hide
*/
@SystemApi
public abstract static class HardwareCallback {
+ /**
+ * This is called when {@link Hardware} is no longer available for the client.
+ */
public abstract void onReleased();
+
+ /**
+ * This is called when the underlying {@link TvStreamConfig} has been changed.
+ *
+ * @param configs The new {@link TvStreamConfig}s.
+ */
public abstract void onStreamConfigChanged(TvStreamConfig[] configs);
}
@@ -1470,18 +1499,41 @@
}
/**
- * Returns acquired TvInputManager.Hardware object for given deviceId.
+ * Acquires {@link Hardware} object for the given device ID.
*
- * If there are other Hardware object acquired for the same deviceId, calling this method will
- * preempt the previously acquired object and report {@link HardwareCallback#onReleased} to the
- * old object.
+ * <p>A subsequent call to this method on the same {@code deviceId} will release the currently
+ * acquired Hardware.
+ *
+ * @param deviceId The device ID to acquire Hardware for.
+ * @param callback A callback to receive updates on Hardware.
+ * @param info The TV input which will use the acquired Hardware.
+ * @return Hardware on success, {@code null} otherwise.
+ *
+ * @removed
+ */
+ @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
+ public Hardware acquireTvInputHardware(int deviceId, final HardwareCallback callback,
+ TvInputInfo info) {
+ return acquireTvInputHardware(deviceId, info, callback);
+ }
+
+ /**
+ * Acquires {@link Hardware} object for the given device ID.
+ *
+ * <p>A subsequent call to this method on the same {@code deviceId} will release the currently
+ * acquired Hardware.
+ *
+ * @param deviceId The device ID to acquire Hardware for.
+ * @param callback A callback to receive updates on Hardware.
+ * @param info The TV input which will use the acquired Hardware.
+ * @return Hardware on success, {@code null} otherwise.
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
- public Hardware acquireTvInputHardware(int deviceId, final HardwareCallback callback,
- TvInputInfo info) {
+ public Hardware acquireTvInputHardware(int deviceId, TvInputInfo info,
+ final HardwareCallback callback) {
try {
return new Hardware(
mService.acquireTvInputHardware(deviceId, new ITvInputHardwareCallback.Stub() {
@@ -1503,6 +1555,9 @@
/**
* Releases previously acquired hardware object.
*
+ * @param deviceId The device ID this Hardware was acquired for
+ * @param hardware Hardware to release.
+ *
* @hide
*/
@SystemApi
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 612a147..97ef6d8 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -1689,19 +1689,20 @@
public abstract void onTune(Uri channelUri);
/**
- * Called when the application requests to tune to a given channel for TV program recording.
+ * Calls {@link #onTune(Uri)}. Override this method in order to handle domain-specific
+ * features that are only known between certain TV inputs and their clients.
*
* <p>The application may call this method before starting or after stopping recording, but
* not during recording.
*
- * <p>The session must call {@link #notifyTuned()} if the tune request was fulfilled, or
+ * <p>The session must call {@link #notifyTuned(Uri)} if the tune request was fulfilled, or
* {@link #notifyError(int)} otherwise.
*
* @param channelUri The URI of a channel.
- * @param params Extra parameters.
- * @hide
+ * @param params Domain-specific data for this tune request. Keys <em>must</em> be a scoped
+ * name, i.e. prefixed with a package name you own, so that different developers
+ * will not create conflicting keys.
*/
- @SystemApi
public void onTune(Uri channelUri, Bundle params) {
onTune(channelUri);
}
diff --git a/media/java/android/media/tv/TvRecordingClient.java b/media/java/android/media/tv/TvRecordingClient.java
index da1002d..a5ff29f 100644
--- a/media/java/android/media/tv/TvRecordingClient.java
+++ b/media/java/android/media/tv/TvRecordingClient.java
@@ -91,22 +91,23 @@
* Tunes to a given channel for TV program recording. The first tune request will create a new
* recording session for the corresponding TV input and establish a connection between the
* application and the session. If recording has already started in the current recording
- * session, this method throws an exception.
+ * session, this method throws an exception. This can be used to provide domain-specific
+ * features that are only known between certain client and their TV inputs.
*
* <p>The application may call this method before starting or after stopping recording, but not
* during recording.
*
* <p>The recording session will respond by calling
- * {@link RecordingCallback#onTuned()} if the tune request was fulfilled, or
+ * {@link RecordingCallback#onTuned(Uri)} if the tune request was fulfilled, or
* {@link RecordingCallback#onError(int)} otherwise.
*
* @param inputId The ID of the TV input for the given channel.
* @param channelUri The URI of a channel.
- * @param params Extra parameters.
+ * @param params Domain-specific data for this tune request. Keys <em>must</em> be a scoped
+ * name, i.e. prefixed with a package name you own, so that different developers will
+ * not create conflicting keys.
* @throws IllegalStateException If recording is already started.
- * @hide
*/
- @SystemApi
public void tune(String inputId, Uri channelUri, Bundle params) {
if (DEBUG) Log.d(TAG, "tune(" + channelUri + ")");
if (TextUtils.isEmpty(inputId)) {
diff --git a/media/java/android/mtp/MtpDevice.java b/media/java/android/mtp/MtpDevice.java
index 0e7013c..d0ef37c 100644
--- a/media/java/android/mtp/MtpDevice.java
+++ b/media/java/android/mtp/MtpDevice.java
@@ -355,6 +355,19 @@
}
}
+ /**
+ * Returns object size in 64-bit integer.
+ *
+ * Though MtpObjectInfo#getCompressedSize returns the object size in 32-bit unsigned integer,
+ * this method returns the object size in 64-bit integer from the object property. Thus it can
+ * fetch 4GB+ object size correctly. If the device does not support objectSize property, it
+ * throws IOException.
+ * @hide
+ */
+ public long getObjectSizeLong(int handle, int format) throws IOException {
+ return native_get_object_size_long(handle, format);
+ }
+
// used by the JNI code
private long mNativeContext;
@@ -381,4 +394,5 @@
private native int native_submit_event_request();
private native MtpEvent native_reap_event_request(int handle);
private native void native_discard_event_request(int handle);
+ private native long native_get_object_size_long(int handle, int format) throws IOException;
}
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index f593685..6954045 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -574,6 +574,9 @@
* Remove the subscription.
*/
private boolean removeSubscription(String id, ConnectionRecord connection, Bundle options) {
+ if (options == null) {
+ return connection.subscriptions.remove(id) != null;
+ }
boolean removed = false;
List<Bundle> optionsList = connection.subscriptions.get(id);
if (optionsList != null) {
@@ -654,9 +657,9 @@
if (page == -1 && pageSize == -1) {
return list;
}
- int fromIndex = pageSize * (page - 1);
+ int fromIndex = pageSize * page;
int toIndex = fromIndex + pageSize;
- if (page < 1 || pageSize < 1 || fromIndex >= list.size()) {
+ if (page < 0 || pageSize < 1 || fromIndex >= list.size()) {
return Collections.EMPTY_LIST;
}
if (toIndex > list.size()) {
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index 5722cb0..39f2a32 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -882,7 +882,7 @@
break;
}
- info.mThumbCompressedSize = image_data.thumbnail_length;
+ info.mThumbCompressedSize = image_data.thumbnail.length;
info.mThumbFormat = MTP_FORMAT_EXIF_JPEG;
info.mImagePixWidth = image_data.full_width;
info.mImagePixHeight = image_data.full_height;
@@ -932,19 +932,19 @@
break;
}
- if (image_data.thumbnail_length == 0) {
+ if (image_data.thumbnail.length == 0) {
// No thumbnail.
break;
}
- result = malloc(image_data.thumbnail_length);
+ result = malloc(image_data.thumbnail.length);
if (result) {
piex::Error err = stream.get()->GetData(
- image_data.thumbnail_offset,
- image_data.thumbnail_length,
+ image_data.thumbnail.offset,
+ image_data.thumbnail.length,
(std::uint8_t *)result);
if (err == piex::Error::kOk) {
- outThumbSize = image_data.thumbnail_length;
+ outThumbSize = image_data.thumbnail.length;
} else {
free(result);
}
diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp
index 0ecb750..6e434b2 100644
--- a/media/jni/android_mtp_MtpDevice.cpp
+++ b/media/jni/android_mtp_MtpDevice.cpp
@@ -42,6 +42,7 @@
#include "MtpDeviceInfo.h"
#include "MtpStorageInfo.h"
#include "MtpObjectInfo.h"
+#include "MtpProperty.h"
using namespace android;
@@ -700,6 +701,42 @@
device->discardEventRequest(seq);
}
+// Returns object size in 64-bit integer. If the MTP device does not support the property, it
+// throws IOException.
+static jlong android_mtp_MtpDevice_get_object_size_long(
+ JNIEnv *env, jobject thiz, jint handle, jint format) {
+ MtpDevice* const device = get_device_from_object(env, thiz);
+ if (!device) {
+ env->ThrowNew(clazz_io_exception, "Failed to obtain MtpDevice.");
+ return 0;
+ }
+
+ std::unique_ptr<MtpProperty> property(
+ device->getObjectPropDesc(MTP_PROPERTY_OBJECT_SIZE, format));
+ if (!property) {
+ env->ThrowNew(clazz_io_exception, "Failed to obtain property desc.");
+ return 0;
+ }
+
+ if (property->getDataType() != MTP_TYPE_UINT64) {
+ env->ThrowNew(clazz_io_exception, "Unexpected property data type.");
+ return 0;
+ }
+
+ if (!device->getObjectPropValue(handle, property.get())) {
+ env->ThrowNew(clazz_io_exception, "Failed to obtain property value.");
+ return 0;
+ }
+
+ const jlong object_size = static_cast<jlong>(property->getCurrentValue().u.u64);
+ if (object_size < 0) {
+ env->ThrowNew(clazz_io_exception, "Object size is too large to express as jlong.");
+ return 0;
+ }
+
+ return object_size;
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = {
@@ -733,6 +770,8 @@
{"native_reap_event_request", "(I)Landroid/mtp/MtpEvent;",
(void *)android_mtp_MtpDevice_reap_event_request},
{"native_discard_event_request", "(I)V", (void *)android_mtp_MtpDevice_discard_event_request},
+
+ {"native_get_object_size_long", "(II)J", (void *)android_mtp_MtpDevice_get_object_size_long},
};
int register_android_mtp_MtpDevice(JNIEnv *env)
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
index b63df6f..d2dc440 100644
--- a/media/jni/soundpool/SoundPool.cpp
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -554,6 +554,10 @@
if (bufidx >= 0) {
size_t bufsize;
uint8_t *buf = AMediaCodec_getInputBuffer(codec, bufidx, &bufsize);
+ if (buf == nullptr) {
+ ALOGE("AMediaCodec_getInputBuffer returned nullptr, short decode");
+ break;
+ }
int sampleSize = AMediaExtractor_readSampleData(ex, buf, bufsize);
ALOGV("read %d", sampleSize);
if (sampleSize < 0) {
@@ -563,10 +567,16 @@
}
int64_t presentationTimeUs = AMediaExtractor_getSampleTime(ex);
- AMediaCodec_queueInputBuffer(codec, bufidx,
+ media_status_t mstatus = AMediaCodec_queueInputBuffer(codec, bufidx,
0 /* offset */, sampleSize, presentationTimeUs,
sawInputEOS ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
- AMediaExtractor_advance(ex);
+ if (mstatus != AMEDIA_OK) {
+ // AMEDIA_ERROR_UNKNOWN == { -ERANGE -EINVAL -EACCES }
+ ALOGE("AMediaCodec_queueInputBuffer returned status %d, short decode",
+ (int)mstatus);
+ break;
+ }
+ (void)AMediaExtractor_advance(ex);
}
}
@@ -581,6 +591,10 @@
ALOGV("got decoded buffer size %d", info.size);
uint8_t *buf = AMediaCodec_getOutputBuffer(codec, status, NULL /* out_size */);
+ if (buf == nullptr) {
+ ALOGE("AMediaCodec_getOutputBuffer returned nullptr, short decode");
+ break;
+ }
size_t dataSize = info.size;
if (dataSize > available) {
dataSize = available;
@@ -589,7 +603,14 @@
writePos += dataSize;
written += dataSize;
available -= dataSize;
- AMediaCodec_releaseOutputBuffer(codec, status, false /* render */);
+ media_status_t mstatus = AMediaCodec_releaseOutputBuffer(
+ codec, status, false /* render */);
+ if (mstatus != AMEDIA_OK) {
+ // AMEDIA_ERROR_UNKNOWN == { -ERANGE -EINVAL -EACCES }
+ ALOGE("AMediaCodec_releaseOutputBuffer returned status %d, short decode",
+ (int)mstatus);
+ break;
+ }
if (available == 0) {
// there might be more data, but there's no space for it
sawOutputEOS = true;
@@ -610,21 +631,21 @@
}
}
- AMediaCodec_stop(codec);
- AMediaCodec_delete(codec);
- AMediaExtractor_delete(ex);
+ (void)AMediaCodec_stop(codec);
+ (void)AMediaCodec_delete(codec);
+ (void)AMediaExtractor_delete(ex);
if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, (int32_t*) rate) ||
!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, numChannels)) {
- AMediaFormat_delete(format);
+ (void)AMediaFormat_delete(format);
return UNKNOWN_ERROR;
}
- AMediaFormat_delete(format);
+ (void)AMediaFormat_delete(format);
*memsize = written;
return OK;
}
- AMediaFormat_delete(format);
+ (void)AMediaFormat_delete(format);
}
- AMediaExtractor_delete(ex);
+ (void)AMediaExtractor_delete(ex);
return UNKNOWN_ERROR;
}
diff --git a/media/tests/MediaFrameworkTest/res/values/exifinterface.xml b/media/tests/MediaFrameworkTest/res/values/exifinterface.xml
index d556ad3..3c5dd37 100644
--- a/media/tests/MediaFrameworkTest/res/values/exifinterface.xml
+++ b/media/tests/MediaFrameworkTest/res/values/exifinterface.xml
@@ -103,7 +103,7 @@
<item>600</item>
<item>800</item>
<item>1</item>
- <item />
+ <item>0</item>
</array>
<array name="volantis_jpg">
<item>false</item>
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ExifInterfaceTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ExifInterfaceTest.java
index 6207f7d..312d9aa 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ExifInterfaceTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ExifInterfaceTest.java
@@ -45,7 +45,7 @@
private static final String TAG = ExifInterface.class.getSimpleName();
private static final boolean VERBOSE = false; // lots of logging
- private static final double DIFFERENCE_TOLERANCE = .005;
+ private static final double DIFFERENCE_TOLERANCE = .001;
// List of files.
private static final String EXIF_BYTE_ORDER_II_JPEG = "image_exif_byte_order_ii.jpg";
@@ -61,7 +61,7 @@
private static final String[] EXIF_TAGS = {
ExifInterface.TAG_MAKE,
ExifInterface.TAG_MODEL,
- ExifInterface.TAG_APERTURE,
+ ExifInterface.TAG_F_NUMBER,
ExifInterface.TAG_DATETIME,
ExifInterface.TAG_EXPOSURE_TIME,
ExifInterface.TAG_FLASH,
@@ -77,7 +77,7 @@
ExifInterface.TAG_GPS_TIMESTAMP,
ExifInterface.TAG_IMAGE_LENGTH,
ExifInterface.TAG_IMAGE_WIDTH,
- ExifInterface.TAG_ISO,
+ ExifInterface.TAG_ISO_SPEED_RATINGS,
ExifInterface.TAG_ORIENTATION,
ExifInterface.TAG_WHITE_BALANCE
};
@@ -111,11 +111,11 @@
public final String gpsLongitudeRef;
public final String gpsProcessingMethod;
public final String gpsTimestamp;
- public final String imageLength;
- public final String imageWidth;
+ public final int imageLength;
+ public final int imageWidth;
public final String iso;
- public final String whiteBalance;
- public final String orientation;
+ public final int orientation;
+ public final int whiteBalance;
private static String getString(TypedArray typedArray, int index) {
String stringValue = typedArray.getString(index);
@@ -137,7 +137,7 @@
longitude = typedArray.getFloat(5, 0f);
altitude = typedArray.getFloat(6, 0f);
- // Read values.
+ // Reads values.
make = getString(typedArray, 7);
model = getString(typedArray, 8);
aperture = typedArray.getFloat(9, 0f);
@@ -154,11 +154,11 @@
gpsLongitudeRef = getString(typedArray, 20);
gpsProcessingMethod = getString(typedArray, 21);
gpsTimestamp = getString(typedArray, 22);
- imageLength = getString(typedArray, 23);
- imageWidth = getString(typedArray, 24);
+ imageLength = typedArray.getInt(23, 0);
+ imageWidth = typedArray.getInt(24, 0);
iso = getString(typedArray, 25);
- orientation = getString(typedArray, 26);
- whiteBalance = getString(typedArray, 27);
+ orientation = typedArray.getInt(26, 0);
+ whiteBalance = typedArray.getInt(27, 0);
typedArray.recycle();
}
@@ -208,11 +208,13 @@
+ bitmap.getHeight());
}
} else {
- Log.e(TAG, fileName + " Corrupted image (no thumbnail)");
+ Log.e(TAG, fileName + " Unexpected result: No thumbnails were found. "
+ + "A thumbnail is expected.");
}
} else {
if (exifInterface.getThumbnail() != null) {
- Log.e(TAG, fileName + " Corrupted image (a thumbnail exists)");
+ Log.e(TAG, fileName + " Unexpected result: A thumbnail was found. "
+ + "No thumbnail is expected.");
} else {
Log.v(TAG, fileName + " No thumbnail");
}
@@ -226,28 +228,27 @@
Log.v(TAG, fileName + " Latitude = " + latLong[0]);
Log.v(TAG, fileName + " Longitude = " + latLong[1]);
} else {
- Log.v(TAG, fileName + "No latlong data");
+ Log.v(TAG, fileName + " No latlong data");
}
// Prints values.
for (String tagKey : EXIF_TAGS) {
String tagValue = exifInterface.getAttribute(tagKey);
- Log.v(TAG, fileName + "Key{" + tagKey + "} = '" + tagValue + "'");
+ Log.v(TAG, fileName + " Key{" + tagKey + "} = '" + tagValue + "'");
}
}
- private void compareFloatTag(ExifInterface exifInterface, String tag, float expectedValue) {
- String stringValue = exifInterface.getAttribute(tag);
- float floatValue = 0f;
-
- if (stringValue != null) {
- floatValue = Float.parseFloat(stringValue);
- }
-
- assertEquals(expectedValue, floatValue, DIFFERENCE_TOLERANCE);
+ private void assertIntTag(ExifInterface exifInterface, String tag, int expectedValue) {
+ int intValue = exifInterface.getAttributeInt(tag, 0);
+ assertEquals(expectedValue, intValue);
}
- private void compareStringTag(ExifInterface exifInterface, String tag, String expectedValue) {
+ private void assertFloatTag(ExifInterface exifInterface, String tag, float expectedValue) {
+ double doubleValue = exifInterface.getAttributeDouble(tag, 0.0);
+ assertEquals(expectedValue, doubleValue, DIFFERENCE_TOLERANCE);
+ }
+
+ private void assertStringTag(ExifInterface exifInterface, String tag, String expectedValue) {
String stringValue = exifInterface.getAttribute(tag);
if (stringValue != null) {
stringValue = stringValue.trim();
@@ -257,7 +258,10 @@
}
private void compareWithExpectedValue(ExifInterface exifInterface,
- ExpectedValue expectedValue) {
+ ExpectedValue expectedValue, String verboseTag) {
+ if (VERBOSE) {
+ printExifTagsAndValues(verboseTag, exifInterface);
+ }
// Checks a thumbnail image.
assertEquals(expectedValue.hasThumbnail, exifInterface.hasThumbnail());
if (expectedValue.hasThumbnail) {
@@ -282,78 +286,144 @@
assertEquals(expectedValue.altitude, exifInterface.getAltitude(.0), DIFFERENCE_TOLERANCE);
// Checks values.
- compareStringTag(exifInterface, ExifInterface.TAG_MAKE, expectedValue.make);
- compareStringTag(exifInterface, ExifInterface.TAG_MODEL, expectedValue.model);
- compareFloatTag(exifInterface, ExifInterface.TAG_APERTURE, expectedValue.aperture);
- compareStringTag(exifInterface, ExifInterface.TAG_DATETIME, expectedValue.datetime);
- compareFloatTag(exifInterface, ExifInterface.TAG_EXPOSURE_TIME, expectedValue.exposureTime);
- compareFloatTag(exifInterface, ExifInterface.TAG_FLASH, expectedValue.flash);
- compareStringTag(exifInterface, ExifInterface.TAG_FOCAL_LENGTH, expectedValue.focalLength);
- compareStringTag(exifInterface, ExifInterface.TAG_GPS_ALTITUDE, expectedValue.gpsAltitude);
- compareStringTag(exifInterface, ExifInterface.TAG_GPS_ALTITUDE_REF,
+ assertStringTag(exifInterface, ExifInterface.TAG_MAKE, expectedValue.make);
+ assertStringTag(exifInterface, ExifInterface.TAG_MODEL, expectedValue.model);
+ assertFloatTag(exifInterface, ExifInterface.TAG_F_NUMBER, expectedValue.aperture);
+ assertStringTag(exifInterface, ExifInterface.TAG_DATETIME, expectedValue.datetime);
+ assertFloatTag(exifInterface, ExifInterface.TAG_EXPOSURE_TIME, expectedValue.exposureTime);
+ assertFloatTag(exifInterface, ExifInterface.TAG_FLASH, expectedValue.flash);
+ assertStringTag(exifInterface, ExifInterface.TAG_FOCAL_LENGTH, expectedValue.focalLength);
+ assertStringTag(exifInterface, ExifInterface.TAG_GPS_ALTITUDE, expectedValue.gpsAltitude);
+ assertStringTag(exifInterface, ExifInterface.TAG_GPS_ALTITUDE_REF,
expectedValue.gpsAltitudeRef);
- compareStringTag(exifInterface, ExifInterface.TAG_GPS_DATESTAMP,
- expectedValue.gpsDatestamp);
- compareStringTag(exifInterface, ExifInterface.TAG_GPS_LATITUDE, expectedValue.gpsLatitude);
- compareStringTag(exifInterface, ExifInterface.TAG_GPS_LATITUDE_REF,
+ assertStringTag(exifInterface, ExifInterface.TAG_GPS_DATESTAMP, expectedValue.gpsDatestamp);
+ assertStringTag(exifInterface, ExifInterface.TAG_GPS_LATITUDE, expectedValue.gpsLatitude);
+ assertStringTag(exifInterface, ExifInterface.TAG_GPS_LATITUDE_REF,
expectedValue.gpsLatitudeRef);
- compareStringTag(exifInterface, ExifInterface.TAG_GPS_LONGITUDE,
- expectedValue.gpsLongitude);
- compareStringTag(exifInterface, ExifInterface.TAG_GPS_LONGITUDE_REF,
+ assertStringTag(exifInterface, ExifInterface.TAG_GPS_LONGITUDE, expectedValue.gpsLongitude);
+ assertStringTag(exifInterface, ExifInterface.TAG_GPS_LONGITUDE_REF,
expectedValue.gpsLongitudeRef);
- compareStringTag(exifInterface, ExifInterface.TAG_GPS_PROCESSING_METHOD,
+ assertStringTag(exifInterface, ExifInterface.TAG_GPS_PROCESSING_METHOD,
expectedValue.gpsProcessingMethod);
- compareStringTag(exifInterface, ExifInterface.TAG_GPS_TIMESTAMP,
- expectedValue.gpsTimestamp);
- compareStringTag(exifInterface, ExifInterface.TAG_IMAGE_LENGTH, expectedValue.imageLength);
- compareStringTag(exifInterface, ExifInterface.TAG_IMAGE_WIDTH, expectedValue.imageWidth);
- compareStringTag(exifInterface, ExifInterface.TAG_ISO, expectedValue.iso);
- compareStringTag(exifInterface, ExifInterface.TAG_ORIENTATION, expectedValue.orientation);
- compareStringTag(exifInterface, ExifInterface.TAG_WHITE_BALANCE,
- expectedValue.whiteBalance);
+ assertStringTag(exifInterface, ExifInterface.TAG_GPS_TIMESTAMP, expectedValue.gpsTimestamp);
+ assertIntTag(exifInterface, ExifInterface.TAG_IMAGE_LENGTH, expectedValue.imageLength);
+ assertIntTag(exifInterface, ExifInterface.TAG_IMAGE_WIDTH, expectedValue.imageWidth);
+ assertStringTag(exifInterface, ExifInterface.TAG_ISO_SPEED_RATINGS, expectedValue.iso);
+ assertIntTag(exifInterface, ExifInterface.TAG_ORIENTATION, expectedValue.orientation);
+ assertIntTag(exifInterface, ExifInterface.TAG_WHITE_BALANCE, expectedValue.whiteBalance);
}
private void testExifInterfaceCommon(File imageFile, ExpectedValue expectedValue)
throws IOException {
- // Created via path.
+ String verboseTag = imageFile.getName();
+
+ // Creates via path.
ExifInterface exifInterface = new ExifInterface(imageFile.getAbsolutePath());
- if (VERBOSE) {
- printExifTagsAndValues(imageFile.getName(), exifInterface);
- }
- compareWithExpectedValue(exifInterface, expectedValue);
+ compareWithExpectedValue(exifInterface, expectedValue, verboseTag);
- // Created from an asset file.
- InputStream in = mContext.getAssets().open(imageFile.getName());
- exifInterface = new ExifInterface(in);
- if (VERBOSE) {
- printExifTagsAndValues(imageFile.getName(), exifInterface);
- }
- compareWithExpectedValue(exifInterface, expectedValue);
-
- // Created via InputStream.
- in = null;
+ // Creates from an asset file.
+ InputStream in = null;
try {
- in = new BufferedInputStream(new FileInputStream(imageFile.getAbsolutePath()));
+ in = mContext.getAssets().open(imageFile.getName());
exifInterface = new ExifInterface(in);
- if (VERBOSE) {
- printExifTagsAndValues(imageFile.getName(), exifInterface);
- }
- compareWithExpectedValue(exifInterface, expectedValue);
+ compareWithExpectedValue(exifInterface, expectedValue, verboseTag);
} finally {
IoUtils.closeQuietly(in);
}
- // Created via FileDescriptor.
+ // Creates via InputStream.
+ in = null;
try {
- FileDescriptor fd = Os.open(imageFile.getAbsolutePath(), OsConstants.O_RDONLY, 0600);
- exifInterface = new ExifInterface(fd);
- if (VERBOSE) {
- printExifTagsAndValues(imageFile.getName(), exifInterface);
- }
- compareWithExpectedValue(exifInterface, expectedValue);
- } catch (ErrnoException e) {
- e.rethrowAsIOException();
+ in = new BufferedInputStream(new FileInputStream(imageFile.getAbsolutePath()));
+ exifInterface = new ExifInterface(in);
+ compareWithExpectedValue(exifInterface, expectedValue, verboseTag);
+ } finally {
+ IoUtils.closeQuietly(in);
}
+
+ // Creates via FileDescriptor.
+ FileDescriptor fd = null;
+ try {
+ fd = Os.open(imageFile.getAbsolutePath(), OsConstants.O_RDONLY, 0600);
+ exifInterface = new ExifInterface(fd);
+ compareWithExpectedValue(exifInterface, expectedValue, verboseTag);
+ } catch (ErrnoException e) {
+ throw e.rethrowAsIOException();
+ } finally {
+ IoUtils.closeQuietly(fd);
+ }
+ }
+
+ private void testSaveAttributes_withFileName(File imageFile, ExpectedValue expectedValue)
+ throws IOException {
+ String verboseTag = imageFile.getName();
+
+ ExifInterface exifInterface = new ExifInterface(imageFile.getAbsolutePath());
+ exifInterface.saveAttributes();
+ exifInterface = new ExifInterface(imageFile.getAbsolutePath());
+ compareWithExpectedValue(exifInterface, expectedValue, verboseTag);
+
+ // Test for modifying one attribute.
+ String backupValue = exifInterface.getAttribute(ExifInterface.TAG_MAKE);
+ exifInterface.setAttribute(ExifInterface.TAG_MAKE, "abc");
+ exifInterface.saveAttributes();
+ exifInterface = new ExifInterface(imageFile.getAbsolutePath());
+ assertEquals("abc", exifInterface.getAttribute(ExifInterface.TAG_MAKE));
+ // Restore the backup value.
+ exifInterface.setAttribute(ExifInterface.TAG_MAKE, backupValue);
+ exifInterface.saveAttributes();
+ exifInterface = new ExifInterface(imageFile.getAbsolutePath());
+ compareWithExpectedValue(exifInterface, expectedValue, verboseTag);
+ }
+
+ private void testSaveAttributes_withFileDescriptor(File imageFile, ExpectedValue expectedValue)
+ throws IOException {
+ String verboseTag = imageFile.getName();
+
+ FileDescriptor fd = null;
+ try {
+ fd = Os.open(imageFile.getAbsolutePath(), OsConstants.O_RDWR, 0600);
+ ExifInterface exifInterface = new ExifInterface(fd);
+ exifInterface.saveAttributes();
+ Os.lseek(fd, 0, OsConstants.SEEK_SET);
+ exifInterface = new ExifInterface(fd);
+ compareWithExpectedValue(exifInterface, expectedValue, verboseTag);
+
+ // Test for modifying one attribute.
+ String backupValue = exifInterface.getAttribute(ExifInterface.TAG_MAKE);
+ exifInterface.setAttribute(ExifInterface.TAG_MAKE, "abc");
+ exifInterface.saveAttributes();
+ Os.lseek(fd, 0, OsConstants.SEEK_SET);
+ exifInterface = new ExifInterface(fd);
+ assertEquals("abc", exifInterface.getAttribute(ExifInterface.TAG_MAKE));
+ // Restore the backup value.
+ exifInterface.setAttribute(ExifInterface.TAG_MAKE, backupValue);
+ exifInterface.saveAttributes();
+ Os.lseek(fd, 0, OsConstants.SEEK_SET);
+ exifInterface = new ExifInterface(fd);
+ compareWithExpectedValue(exifInterface, expectedValue, verboseTag);
+ } catch (ErrnoException e) {
+ throw e.rethrowAsIOException();
+ } finally {
+ IoUtils.closeQuietly(fd);
+ }
+ }
+
+ private void testSaveAttributes_withInputStream(File imageFile, ExpectedValue expectedValue)
+ throws IOException {
+ InputStream in = null;
+ try {
+ in = getContext().getAssets().open(imageFile.getName());
+ ExifInterface exifInterface = new ExifInterface(in);
+ exifInterface.saveAttributes();
+ } catch (UnsupportedOperationException e) {
+ // Expected. saveAttributes is not supported with an ExifInterface object which was
+ // created with InputStream.
+ return;
+ } finally {
+ IoUtils.closeQuietly(in);
+ }
+ fail("Should not reach here!");
}
private void testExifInterfaceForJpeg(String fileName, int typedArrayResourceId)
@@ -366,30 +436,9 @@
testExifInterfaceCommon(imageFile, expectedValue);
// Test for saving attributes.
- ExifInterface exifInterface;
- try {
- FileDescriptor fd = Os.open(imageFile.getAbsolutePath(), OsConstants.O_RDWR, 0600);
- exifInterface = new ExifInterface(fd);
- exifInterface.saveAttributes();
- fd = Os.open(imageFile.getAbsolutePath(), OsConstants.O_RDWR, 0600);
- exifInterface = new ExifInterface(fd);
- if (VERBOSE) {
- printExifTagsAndValues(fileName, exifInterface);
- }
- compareWithExpectedValue(exifInterface, expectedValue);
- } catch (ErrnoException e) {
- e.rethrowAsIOException();
- }
-
- // Test for modifying one attribute.
- exifInterface = new ExifInterface(imageFile.getAbsolutePath());
- exifInterface.setAttribute(ExifInterface.TAG_MAKE, "abc");
- exifInterface.saveAttributes();
- exifInterface = new ExifInterface(imageFile.getAbsolutePath());
- if (VERBOSE) {
- printExifTagsAndValues(fileName, exifInterface);
- }
- assertEquals("abc", exifInterface.getAttribute(ExifInterface.TAG_MAKE));
+ testSaveAttributes_withFileName(imageFile, expectedValue);
+ testSaveAttributes_withFileDescriptor(imageFile, expectedValue);
+ testSaveAttributes_withInputStream(imageFile, expectedValue);
}
private void testExifInterfaceForRaw(String fileName, int typedArrayResourceId)
diff --git a/packages/CaptivePortalLogin/AndroidManifest.xml b/packages/CaptivePortalLogin/AndroidManifest.xml
index aea8585..f21fd88 100644
--- a/packages/CaptivePortalLogin/AndroidManifest.xml
+++ b/packages/CaptivePortalLogin/AndroidManifest.xml
@@ -27,7 +27,8 @@
<activity
android:name="com.android.captiveportallogin.CaptivePortalLoginActivity"
android:label="@string/action_bar_label"
- android:theme="@style/AppTheme" >
+ android:theme="@style/AppTheme"
+ android:configChanges="keyboardHidden|orientation|screenSize" >
<intent-filter>
<action android:name="android.net.conn.CAPTIVE_PORTAL"/>
<category android:name="android.intent.category.DEFAULT"/>
diff --git a/packages/CtsShim/Android.mk b/packages/CtsShim/Android.mk
new file mode 100644
index 0000000..537b171
--- /dev/null
+++ b/packages/CtsShim/Android.mk
@@ -0,0 +1,61 @@
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+###########################################################
+# Variant: Privileged app
+
+include $(CLEAR_VARS)
+# this needs to be a privileged application
+LOCAL_PRIVILEGED_MODULE := true
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_SDK_VERSION := current
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_DEX_PREOPT := false
+
+LOCAL_PACKAGE_NAME := CtsShimPriv
+
+#TODO need to find the correct certificate
+#Change in conjunction with cts/hostsidetests/appsecurity/test-apps/IntentFilterApp
+LOCAL_CERTIFICATE := platform
+LOCAL_MANIFEST_FILE := priv_shim/AndroidManifest.xml
+
+include $(BUILD_PACKAGE)
+
+
+
+###########################################################
+# Variant: System app
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_SDK_VERSION := current
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_DEX_PREOPT := false
+
+LOCAL_PACKAGE_NAME := CtsShim
+
+#TODO need to find the correct certificate
+#Change in conjunction with cts/hostsidetests/appsecurity/test-apps/IntentFilterApp
+LOCAL_CERTIFICATE := platform
+LOCAL_MANIFEST_FILE := shim/AndroidManifest.xml
+
+include $(BUILD_PACKAGE)
+
+
diff --git a/packages/CtsShim/priv_shim/AndroidManifest.xml b/packages/CtsShim/priv_shim/AndroidManifest.xml
new file mode 100644
index 0000000..0a3f823
--- /dev/null
+++ b/packages/CtsShim/priv_shim/AndroidManifest.xml
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Manifest for the privileged CTS shim -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.priv.ctsshim">
+ <application android:label="CtsShim">
+
+ <!-- These activities don't actually exist; define them just to test the filters !-->
+
+ <!-- install test; [some] high priority filters granted -->
+ <activity android:name=".InstallPriority">
+ <!-- normal actions; priority will be granted -->
+ <intent-filter android:priority="100">
+ <action android:name="android.intent.action.SEARCH" />
+ <category android:name="android.intent.category.INFO" />
+ </intent-filter>
+
+ <!-- protected actions; priority will be denied -->
+ <intent-filter android:priority="100">
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ </intent-filter>
+ <intent-filter android:priority="100">
+ <action android:name="android.intent.action.SEND" />
+ </intent-filter>
+ <intent-filter android:priority="100">
+ <action android:name="android.intent.action.SEND_MULTIPLE" />
+ </intent-filter>
+ <intent-filter android:priority="100">
+ <action android:name="android.intent.action.SENDTO" />
+ </intent-filter>
+ </activity>
+
+ <!-- upgrade test; single, equivalent filter -->
+ <activity android:name=".UpgradeMatch">
+ <intent-filter android:priority="100">
+ <action android:name="com.android.cts.action.MATCH" />
+ <category android:name="android.intent.category.INFO" />
+ </intent-filter>
+ </activity>
+
+ <!-- upgrade test; multiple, equivalent filters -->
+ <activity android:name=".UpgradeMatchMultiple">
+ <intent-filter android:priority="100">
+ <action android:name="com.android.cts.action.MATCH_MULTIPLE" />
+ <category android:name="android.intent.category.INFO" />
+ </intent-filter>
+
+ <intent-filter android:priority="150">
+ <action android:name="com.android.cts.action.MATCH_MULTIPLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="http" />
+ <data android:scheme="https" />
+ <data android:host="www.google.com" android:port="80" />
+ <data android:host="www.google.com" android:port="8080" />
+ <data android:host="goo.gl" android:port="443" />
+ </intent-filter>
+ </activity>
+
+ <!-- upgrade test; lower priority -->
+ <activity android:name=".UpgradeLowerPriority">
+ <intent-filter android:priority="100">
+ <action android:name="com.android.cts.action.LOWER_PRIORITY" />
+ <category android:name="android.intent.category.INFO" />
+ </intent-filter>
+ </activity>
+
+ <!-- upgrade test; action subset -->
+ <activity android:name=".UpgradeActionSubset">
+ <intent-filter android:priority="100">
+ <action android:name="com.android.cts.action.ACTION_SUB" />
+ <action android:name="com.android.cts.action.ACTION_SUB_2" />
+ <action android:name="com.android.cts.action.ACTION_SUB_3" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ <!-- upgrade test; category subset -->
+ <activity android:name=".UpgradeCategorySubset">
+ <intent-filter android:priority="100">
+ <action android:name="com.android.cts.action.CATEGORY_SUB" />
+ <category android:name="android.intent.category.INFO" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ <!-- upgrade test; scheme subset -->
+ <activity android:name=".UpgradeSchemeSubset">
+ <intent-filter android:priority="100">
+ <action android:name="com.android.cts.action.SCHEME_SUB" />
+ <data android:scheme="content" />
+ <data android:scheme="flubber" />
+ <data android:scheme="zoodle" />
+ </intent-filter>
+ </activity>
+
+ <!-- upgrade test; authority subset -->
+ <activity android:name=".UpgradeAuthoritySubset">
+ <intent-filter android:priority="100">
+ <action android:name="com.android.cts.action.AUTHORITY_SUB" />
+ <data android:host="www.google.com" android:port="80" />
+ <data android:host="www.google.com" android:port="8080" />
+ <data android:host="mail.google.com" android:port="80" />
+ <data android:host="goo.gl" android:port="443" />
+ </intent-filter>
+ </activity>
+
+
+ <!-- upgrade test; new action -->
+ <activity android:name=".UpgradeNewAction">
+ <intent-filter android:priority="100">
+ <action android:name="com.android.cts.action.NEW_ACTION" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ <!-- upgrade test; new category -->
+ <activity android:name=".UpgradeNewCategory">
+ <intent-filter android:priority="100">
+ <action android:name="com.android.cts.action.NEW_CATEGORY" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ <!-- upgrade test; new scheme -->
+ <activity android:name=".UpgradeNewScheme">
+ <intent-filter android:priority="100">
+ <action android:name="com.android.cts.action.NEW_SCHEME" />
+ <data android:scheme="content" />
+ </intent-filter>
+ </activity>
+
+ <!-- upgrade test; new authority -->
+ <activity android:name=".UpgradeNewAuthority">
+ <intent-filter android:priority="100">
+ <action android:name="com.android.cts.action.NEW_AUTHORITY" />
+ <data android:host="www.google.com" android:port="80" />
+ </intent-filter>
+ </activity>
+
+ </application>
+</manifest>
+
diff --git a/packages/CtsShim/shim/AndroidManifest.xml b/packages/CtsShim/shim/AndroidManifest.xml
new file mode 100644
index 0000000..ee4b547
--- /dev/null
+++ b/packages/CtsShim/shim/AndroidManifest.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Manifest for the system CTS shim -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.system.ctsshim">
+ <application android:label="CtsShim">
+
+ <!-- These activities don't actually exist; define them just to test the filters !-->
+
+ <!-- install test; high priority filter DENIED -->
+ <activity android:name=".InstallPriority">
+ <intent-filter android:priority="100">
+ <action android:name="android.intent.action.SEARCH" />
+ <category android:name="android.intent.category.INFO" />
+ </intent-filter>
+ <intent-filter android:priority="100">
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ </intent-filter>
+ <intent-filter android:priority="100">
+ <action android:name="android.intent.action.SEND" />
+ </intent-filter>
+ <intent-filter android:priority="100">
+ <action android:name="android.intent.action.SEND_MULTIPLE" />
+ </intent-filter>
+ <intent-filter android:priority="100">
+ <action android:name="android.intent.action.SENDTO" />
+ </intent-filter>
+ </activity>
+
+ </application>
+</manifest>
+
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index 14609b2..69912ab 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -43,9 +43,9 @@
<activity
android:name=".LauncherActivity"
- android:theme="@android:style/Theme.NoDisplay"
- android:icon="@mipmap/ic_launcher_download"
- android:label="@string/downloads_label">
+ android:label="@string/downloads_label"
+ android:icon="@mipmap/ic_launcher_downloads"
+ android:theme="@android:style/Theme.NoDisplay">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
@@ -54,23 +54,23 @@
<activity
android:name=".FilesActivity"
- android:theme="@style/DocumentsTheme"
- android:icon="@mipmap/ic_launcher_download"
android:label="@string/downloads_label"
- android:documentLaunchMode="intoExisting">
+ android:icon="@mipmap/ic_launcher_downloads"
+ android:documentLaunchMode="intoExisting"
+ android:theme="@style/DocumentsTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
<intent-filter>
- <action android:name="android.intent.action.VIEW_DOWNLOADS" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- <intent-filter>
<action android:name="android.provider.action.BROWSE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.document/root" />
</intent-filter>
<intent-filter>
+ <action android:name="android.intent.action.VIEW_DOWNLOADS" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/zip"
diff --git a/packages/DocumentsUI/app-perf-tests/AndroidManifest.xml b/packages/DocumentsUI/app-perf-tests/AndroidManifest.xml
index 1c3ed80..0013b6b 100644
--- a/packages/DocumentsUI/app-perf-tests/AndroidManifest.xml
+++ b/packages/DocumentsUI/app-perf-tests/AndroidManifest.xml
@@ -2,6 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.documentsui.appperftests">
+ <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
+
<application>
<uses-library android:name="android.test.runner" />
diff --git a/packages/DocumentsUI/app-perf-tests/src/com/android/documentsui/FilesAppPerfTest.java b/packages/DocumentsUI/app-perf-tests/src/com/android/documentsui/FilesAppPerfTest.java
index d6e8a96..ce2fc13 100644
--- a/packages/DocumentsUI/app-perf-tests/src/com/android/documentsui/FilesAppPerfTest.java
+++ b/packages/DocumentsUI/app-perf-tests/src/com/android/documentsui/FilesAppPerfTest.java
@@ -17,6 +17,8 @@
package com.android.documentsui;
import android.app.Activity;
+import android.app.ActivityManager;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -91,12 +93,15 @@
}
private void killProviders() throws Exception {
- final PackageManager pm = getInstrumentation().getContext().getPackageManager();
+ final Context context = getInstrumentation().getContext();
+ final PackageManager pm = context.getPackageManager();
+ final ActivityManager am = (ActivityManager) context.getSystemService(
+ Context.ACTIVITY_SERVICE);
final Intent intent = new Intent(DocumentsContract.PROVIDER_INTERFACE);
final List<ResolveInfo> providers = pm.queryIntentContentProviders(intent, 0);
for (ResolveInfo info : providers) {
final String packageName = info.providerInfo.packageName;
- mDevice.executeShellCommand("am force-stop " + packageName);
+ am.killBackgroundProcesses(packageName);
}
}
}
diff --git a/packages/DocumentsUI/perf-tests/Android.mk b/packages/DocumentsUI/perf-tests/Android.mk
index 11c163b..5ebf85f 100644
--- a/packages/DocumentsUI/perf-tests/Android.mk
+++ b/packages/DocumentsUI/perf-tests/Android.mk
@@ -2,8 +2,8 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
-#LOCAL_SDK_VERSION := current
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_SRC_FILES := $(call all-java-files-under, src) \
$(call all-java-files-under, ../tests/src/com/android/documentsui/bots) \
../tests/src/com/android/documentsui/ActivityTest.java \
@@ -11,7 +11,7 @@
../tests/src/com/android/documentsui/StubProvider.java
LOCAL_JAVA_LIBRARIES := android-support-v4 android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := mockito-target ub-uiautomator
+LOCAL_STATIC_JAVA_LIBRARIES := mockito-target ub-uiautomator ub-janktesthelper
LOCAL_PACKAGE_NAME := DocumentsUIPerfTests
LOCAL_INSTRUMENTATION_FOR := DocumentsUI
diff --git a/packages/DocumentsUI/perf-tests/res/raw/earth_small.jpg b/packages/DocumentsUI/perf-tests/res/raw/earth_small.jpg
new file mode 100644
index 0000000..dd2da3e
--- /dev/null
+++ b/packages/DocumentsUI/perf-tests/res/raw/earth_small.jpg
Binary files differ
diff --git a/packages/DocumentsUI/perf-tests/src/com/android/documentsui/FilesJankPerfTest.java b/packages/DocumentsUI/perf-tests/src/com/android/documentsui/FilesJankPerfTest.java
new file mode 100644
index 0000000..cb2d904
--- /dev/null
+++ b/packages/DocumentsUI/perf-tests/src/com/android/documentsui/FilesJankPerfTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import static com.android.documentsui.StressProvider.DEFAULT_AUTHORITY;
+import static com.android.documentsui.StressProvider.STRESS_ROOT_0_ID;
+import static com.android.documentsui.StressProvider.STRESS_ROOT_2_ID;
+
+import android.app.Activity;
+import android.os.RemoteException;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import android.content.Intent;
+import android.content.Context;
+import android.support.test.jank.JankTest;
+import android.support.test.jank.JankTestBase;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.jank.GfxMonitor;
+import android.support.test.uiautomator.UiScrollable;
+import android.util.Log;
+
+import com.android.documentsui.FilesActivity;
+import com.android.documentsui.bots.RootsListBot;
+import com.android.documentsui.bots.DirectoryListBot;
+
+@LargeTest
+public class FilesJankPerfTest extends JankTestBase {
+ private static final String DOCUMENTSUI_PACKAGE = "com.android.documentsui";
+ private static final int MAX_FLINGS = 10;
+ private static final int BOT_TIMEOUT = 5000;
+
+ private RootsListBot mRootsListBot;
+ private DirectoryListBot mDirListBot;
+ private Activity mActivity = null;
+
+ public void setUpInLoop() {
+ final UiDevice device = UiDevice.getInstance(getInstrumentation());
+ final Context context = getInstrumentation().getTargetContext();
+ mRootsListBot = new RootsListBot(device, context, BOT_TIMEOUT);
+ mDirListBot = new DirectoryListBot(device, context, BOT_TIMEOUT);
+
+ final Intent intent = new Intent(context, FilesActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mActivity = getInstrumentation().startActivitySync(intent);
+ }
+
+ public void tearDownInLoop() {
+ if (mActivity != null) {
+ mActivity.finish();
+ mActivity = null;
+ }
+ }
+
+ public void setupAndOpenInLoop() throws Exception {
+ setUpInLoop();
+ openRoot();
+ }
+
+ public void openRoot() throws Exception {
+ mRootsListBot.openRoot(STRESS_ROOT_2_ID);
+ }
+
+ @JankTest(expectedFrames=0, beforeLoop="setUpInLoop", afterLoop="tearDownInLoop")
+ @GfxMonitor(processName=DOCUMENTSUI_PACKAGE)
+ public void testOpenRootJankPerformance() throws Exception {
+ openRoot();
+ getInstrumentation().waitForIdleSync();
+ }
+
+ @JankTest(expectedFrames=0, beforeLoop="setupAndOpenInLoop", afterLoop="tearDownInLoop")
+ @GfxMonitor(processName=DOCUMENTSUI_PACKAGE)
+ public void testFlingJankPerformance() throws Exception {
+ new UiScrollable(mDirListBot.findDocumentsList().getSelector()).flingToEnd(MAX_FLINGS);
+ getInstrumentation().waitForIdleSync();
+ }
+}
diff --git a/packages/DocumentsUI/perf-tests/src/com/android/documentsui/StressProvider.java b/packages/DocumentsUI/perf-tests/src/com/android/documentsui/StressProvider.java
index 1bc802a..f9b06f8 100644
--- a/packages/DocumentsUI/perf-tests/src/com/android/documentsui/StressProvider.java
+++ b/packages/DocumentsUI/perf-tests/src/com/android/documentsui/StressProvider.java
@@ -18,9 +18,11 @@
import android.content.Context;
import android.content.pm.ProviderInfo;
+import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.database.MatrixCursor.RowBuilder;
import android.database.MatrixCursor;
+import android.graphics.Point;
import android.os.CancellationSignal;
import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
@@ -31,8 +33,11 @@
import java.io.File;
import java.io.FileNotFoundException;
+import java.io.IOException;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Random;
/**
@@ -46,11 +51,21 @@
// Empty root.
public static final String STRESS_ROOT_0_ID = "STRESS_ROOT_0";
- // Root with thousands of items.
+ // Root with thousands of directories.
public static final String STRESS_ROOT_1_ID = "STRESS_ROOT_1";
+ // Root with hundreds of files.
+ public static final String STRESS_ROOT_2_ID = "STRESS_ROOT_2";
+
private static final String STRESS_ROOT_0_DOC_ID = "STRESS_ROOT_0_DOC";
private static final String STRESS_ROOT_1_DOC_ID = "STRESS_ROOT_1_DOC";
+ private static final String STRESS_ROOT_2_DOC_ID = "STRESS_ROOT_2_DOC";
+
+ private static final int STRESS_ROOT_1_ITEMS = 10000;
+ private static final int STRESS_ROOT_2_ITEMS = 300;
+
+ private static final String MIME_TYPE_IMAGE = "image/jpeg";
+ private static final long REFERENCE_TIMESTAMP = 1459159369359L;
private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_TITLE, Root.COLUMN_DOCUMENT_ID,
@@ -62,7 +77,12 @@
};
private String mAuthority = DEFAULT_AUTHORITY;
- private ArrayList<String> mIds = new ArrayList<>();
+
+ // Map from a root document id to children document ids.
+ private Map<String, ArrayList<StubDocument>> mChildDocuments = new HashMap<>();
+
+ private Map<String, StubDocument> mDocuments = new HashMap<>();
+ private Map<String, StubRoot> mRoots = new HashMap<>();
@Override
public void attachInfo(Context context, ProviderInfo info) {
@@ -72,20 +92,48 @@
@Override
public boolean onCreate() {
- mIds = new ArrayList();
- for (int i = 0; i < 10000; i++) {
- mIds.add(createRandomId(i));
+ StubDocument document;
+
+ ArrayList<StubDocument> children = new ArrayList<StubDocument>();
+ mChildDocuments.put(STRESS_ROOT_1_DOC_ID, children);
+ for (int i = 0; i < STRESS_ROOT_1_ITEMS; i++) {
+ document = StubDocument.createDirectory(i);
+ mDocuments.put(document.id, document);
+ children.add(document);
}
- mIds.add(STRESS_ROOT_0_DOC_ID);
- mIds.add(STRESS_ROOT_1_DOC_ID);
+
+ children = new ArrayList<StubDocument>();
+ mChildDocuments.put(STRESS_ROOT_2_DOC_ID, children);
+ for (int i = 0; i < STRESS_ROOT_2_ITEMS; i++) {
+ try {
+ document = StubDocument.createFile(
+ getContext(), MIME_TYPE_IMAGE,
+ com.android.documentsui.perftests.R.raw.earth_small,
+ STRESS_ROOT_1_ITEMS + i);
+ } catch (IOException e) {
+ return false;
+ }
+ mDocuments.put(document.id, document);
+ children.add(document);
+ }
+
+ mRoots.put(STRESS_ROOT_0_ID, new StubRoot(STRESS_ROOT_0_ID, STRESS_ROOT_0_DOC_ID));
+ mRoots.put(STRESS_ROOT_1_ID, new StubRoot(STRESS_ROOT_1_ID, STRESS_ROOT_1_DOC_ID));
+ mRoots.put(STRESS_ROOT_2_ID, new StubRoot(STRESS_ROOT_2_ID, STRESS_ROOT_2_DOC_ID));
+
+ mDocuments.put(STRESS_ROOT_0_DOC_ID, StubDocument.createDirectory(STRESS_ROOT_0_DOC_ID));
+ mDocuments.put(STRESS_ROOT_1_DOC_ID, StubDocument.createDirectory(STRESS_ROOT_1_DOC_ID));
+ mDocuments.put(STRESS_ROOT_2_DOC_ID, StubDocument.createDirectory(STRESS_ROOT_2_DOC_ID));
+
return true;
}
@Override
public Cursor queryRoots(String[] projection) throws FileNotFoundException {
final MatrixCursor result = new MatrixCursor(DEFAULT_ROOT_PROJECTION);
- includeRoot(result, STRESS_ROOT_0_ID, STRESS_ROOT_0_DOC_ID);
- includeRoot(result, STRESS_ROOT_1_ID, STRESS_ROOT_1_DOC_ID);
+ for (StubRoot root : mRoots.values()) {
+ includeRoot(result, root);
+ }
return result;
}
@@ -93,57 +141,125 @@
public Cursor queryDocument(String documentId, String[] projection)
throws FileNotFoundException {
final MatrixCursor result = new MatrixCursor(DEFAULT_DOCUMENT_PROJECTION);
- includeDocument(result, documentId);
+ final StubDocument document = mDocuments.get(documentId);
+ includeDocument(result, document);
return result;
}
@Override
- public Cursor queryChildDocuments(String parentDocumentId, String[] projection, String sortOrder)
+ public Cursor queryChildDocuments(String parentDocumentId, String[] projection,
+ String sortOrder)
throws FileNotFoundException {
final MatrixCursor result = new MatrixCursor(DEFAULT_DOCUMENT_PROJECTION);
- if (STRESS_ROOT_1_DOC_ID.equals(parentDocumentId)) {
- for (String id : mIds) {
- includeDocument(result, id);
+ final ArrayList<StubDocument> childDocuments = mChildDocuments.get(parentDocumentId);
+ if (childDocuments != null) {
+ for (StubDocument document : childDocuments) {
+ includeDocument(result, document);
}
}
return result;
}
@Override
- public ParcelFileDescriptor openDocument(String docId, String mode, CancellationSignal signal)
+ public AssetFileDescriptor openDocumentThumbnail(String docId, Point sizeHint,
+ CancellationSignal signal)
+ throws FileNotFoundException {
+ final StubDocument document = mDocuments.get(docId);
+ return getContext().getResources().openRawResourceFd(document.thumbnail);
+ }
+
+ @Override
+ public ParcelFileDescriptor openDocument(String docId, String mode,
+ CancellationSignal signal)
throws FileNotFoundException {
throw new UnsupportedOperationException();
}
- private void includeRoot(MatrixCursor result, String rootId, String docId) {
+ private void includeRoot(MatrixCursor result, StubRoot root) {
final RowBuilder row = result.newRow();
- row.add(Root.COLUMN_ROOT_ID, rootId);
+ row.add(Root.COLUMN_ROOT_ID, root.id);
row.add(Root.COLUMN_FLAGS, 0);
- row.add(Root.COLUMN_TITLE, rootId);
- row.add(Root.COLUMN_DOCUMENT_ID, docId);
+ row.add(Root.COLUMN_TITLE, root.id);
+ row.add(Root.COLUMN_DOCUMENT_ID, root.documentId);
}
- private void includeDocument(MatrixCursor result, String id) {
+ private void includeDocument(MatrixCursor result, StubDocument document) {
final RowBuilder row = result.newRow();
- row.add(Document.COLUMN_DOCUMENT_ID, id);
- row.add(Document.COLUMN_DISPLAY_NAME, id);
- row.add(Document.COLUMN_SIZE, 0);
- row.add(Document.COLUMN_MIME_TYPE, DocumentsContract.Document.MIME_TYPE_DIR);
- row.add(Document.COLUMN_FLAGS, 0);
- row.add(Document.COLUMN_LAST_MODIFIED, null);
+ row.add(Document.COLUMN_DOCUMENT_ID, document.id);
+ row.add(Document.COLUMN_DISPLAY_NAME, document.id);
+ row.add(Document.COLUMN_SIZE, document.size);
+ row.add(Document.COLUMN_MIME_TYPE, document.mimeType);
+ row.add(Document.COLUMN_FLAGS,
+ document.thumbnail != -1 ? Document.FLAG_SUPPORTS_THUMBNAIL : 0);
+ row.add(Document.COLUMN_LAST_MODIFIED, document.lastModified);
}
- private static String getDocumentIdForFile(File file) {
+ private static String getStubDocumentIdForFile(File file) {
return file.getAbsolutePath();
}
- private String createRandomId(int index) {
- final Random random = new Random(index);
- final StringBuilder builder = new StringBuilder();
- for (int i = 0; i < 20; i++) {
- builder.append((char) (random.nextInt(96) + 32));
+ private static class StubDocument {
+ final String mimeType;
+ final String id;
+ final int size;
+ final long lastModified;
+ final int thumbnail;
+
+ private StubDocument(String mimeType, String id, int size, long lastModified,
+ int thumbnail) {
+ this.mimeType = mimeType;
+ this.id = id;
+ this.size = size;
+ this.lastModified = lastModified;
+ this.thumbnail = thumbnail;
}
- builder.append(index); // Append a number to guarantee uniqueness.
- return builder.toString();
+
+ public static StubDocument createDirectory(int index) {
+ return new StubDocument(
+ DocumentsContract.Document.MIME_TYPE_DIR, createRandomId(index), 0,
+ createRandomTime(index), -1);
+ }
+
+ public static StubDocument createDirectory(String id) {
+ return new StubDocument(DocumentsContract.Document.MIME_TYPE_DIR, id, 0, 0, -1);
+ }
+
+ public static StubDocument createFile(Context context, String mimeType, int thumbnail,
+ int index) throws IOException {
+ return new StubDocument(
+ mimeType, createRandomId(index), createRandomSize(index),
+ createRandomTime(index), thumbnail);
+ }
+
+ private static String createRandomId(int index) {
+ final Random random = new Random(index);
+ final StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < 20; i++) {
+ builder.append((char) (random.nextInt(96) + 32));
+ }
+ builder.append(index); // Append a number to guarantee uniqueness.
+ return builder.toString();
+ }
+
+ private static int createRandomSize(int index) {
+ final Random random = new Random(index);
+ return random.nextInt(1024 * 1024 * 100); // Up to 100 MB.
+ }
+
+ private static long createRandomTime(int index) {
+ final Random random = new Random(index);
+ // Up to 30 days backwards from REFERENCE_TIMESTAMP.
+ return REFERENCE_TIMESTAMP - random.nextLong() % 1000L * 60 * 60 * 24 * 30;
+ }
+ }
+
+ private static class StubRoot {
+ final String id;
+ final String documentId;
+
+ public StubRoot(String id, String documentId) {
+ this.id = id;
+ this.documentId = documentId;
+ }
}
}
diff --git a/packages/DocumentsUI/res/drawable/drag_shadow_background.xml b/packages/DocumentsUI/res/drawable/drag_shadow_background.xml
new file mode 100644
index 0000000..49465cb
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable/drag_shadow_background.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="@color/item_doc_background" />
+ <stroke
+ android:width="1dp"
+ android:color="#ff9f9f9f" />
+ <corners
+ android:bottomRightRadius="3dp"
+ android:bottomLeftRadius="3dp"
+ android:topLeftRadius="3dp"
+ android:topRightRadius="3dp"/>
+</shape>
diff --git a/packages/DocumentsUI/res/layout/dialog_file_name.xml b/packages/DocumentsUI/res/layout/dialog_file_name.xml
index 5ed476f..3a95a13 100644
--- a/packages/DocumentsUI/res/layout/dialog_file_name.xml
+++ b/packages/DocumentsUI/res/layout/dialog_file_name.xml
@@ -17,6 +17,7 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:fitsSystemWindows="true"
android:padding="?android:attr/listPreferredItemPaddingEnd">
<EditText
diff --git a/packages/DocumentsUI/res/layout/drag_shadow_layout.xml b/packages/DocumentsUI/res/layout/drag_shadow_layout.xml
new file mode 100644
index 0000000..26613ef
--- /dev/null
+++ b/packages/DocumentsUI/res/layout/drag_shadow_layout.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingStart="8dp"
+ android:paddingEnd="8dp"
+ android:orientation="horizontal"
+ android:gravity="center_vertical|left"
+ android:background="@drawable/drag_shadow_background">
+
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="@dimen/root_icon_size"
+ android:layout_height="@dimen/root_icon_size"
+ android:scaleType="centerInside"
+ android:contentDescription="@null"
+ android:duplicateParentState="true"/>
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:maxLines="1"
+ android:ellipsize="end"
+ android:textAlignment="viewStart"
+ android:textColor="@color/item_title"
+ android:paddingStart="8dp"/>
+
+</LinearLayout>
diff --git a/packages/DocumentsUI/res/layout/fragment_save.xml b/packages/DocumentsUI/res/layout/fragment_save.xml
index 7aac620..a889b9f 100644
--- a/packages/DocumentsUI/res/layout/fragment_save.xml
+++ b/packages/DocumentsUI/res/layout/fragment_save.xml
@@ -21,6 +21,7 @@
android:orientation="horizontal"
android:baselineAligned="false"
android:gravity="center_vertical"
+ android:fitsSystemWindows="true"
android:minHeight="?android:attr/listPreferredItemHeightSmall">
<FrameLayout
diff --git a/packages/DocumentsUI/res/menu/activity.xml b/packages/DocumentsUI/res/menu/activity.xml
index 2aee569..85e7a7a 100644
--- a/packages/DocumentsUI/res/menu/activity.xml
+++ b/packages/DocumentsUI/res/menu/activity.xml
@@ -31,61 +31,68 @@
android:actionViewClass="android.widget.SearchView"
android:imeOptions="actionSearch"
android:visible="false" />
- <item
- android:id="@+id/menu_grid"
- android:title="@string/menu_grid"
- android:icon="@drawable/ic_menu_view_grid"
- android:showAsAction="always" />
- <item
- android:id="@+id/menu_list"
- android:title="@string/menu_list"
- android:icon="@drawable/ic_menu_view_list"
- android:showAsAction="always" />
+<!-- This group is being hidden when searching is in full bar mode-->
+ <group android:id="@+id/group_hide_when_searching">
+ <item
+ android:id="@+id/menu_grid"
+ android:title="@string/menu_grid"
+ android:icon="@drawable/ic_menu_view_grid"
+ android:showAsAction="always" />
+ <item
+ android:id="@+id/menu_list"
+ android:title="@string/menu_list"
+ android:icon="@drawable/ic_menu_view_list"
+ android:showAsAction="always" />
- <item
- android:id="@+id/menu_new_window"
- android:title="@string/menu_new_window"
- android:alphabeticShortcut="n"
- android:showAsAction="never"
- android:visible="false" />
- <item
- android:id="@+id/menu_create_dir"
- android:title="@string/menu_create_dir"
- android:icon="@drawable/ic_menu_new_folder"
- android:alphabeticShortcut="e"
- android:showAsAction="never"
- android:visible="false" />
- <item
- android:id="@+id/menu_paste_from_clipboard"
- android:title="@string/menu_paste_from_clipboard"
- android:alphabeticShortcut="v"
- android:showAsAction="never"
- android:visible="false" />
- <!-- Copy action is defined in mode_directory.xml -->
- <item
- android:id="@+id/menu_sort"
- android:title="@string/menu_sort"
- android:icon="@drawable/ic_menu_sortby"
- android:showAsAction="ifRoom">
- <menu>
- <item
- android:id="@+id/menu_sort_name"
- android:title="@string/sort_name" />
- <item
- android:id="@+id/menu_sort_date"
- android:title="@string/sort_date" />
- <item
- android:id="@+id/menu_sort_size"
- android:title="@string/sort_size" />
- </menu>
- </item>
- <item
- android:id="@+id/menu_file_size"
- android:showAsAction="never"
- android:visible="false" />
- <item
- android:id="@+id/menu_settings"
- android:title="@string/menu_settings"
- android:showAsAction="never"
- android:visible="false" />
+ <item
+ android:id="@+id/menu_new_window"
+ android:title="@string/menu_new_window"
+ android:alphabeticShortcut="n"
+ android:showAsAction="never"
+ android:visible="false" />
+ <item
+ android:id="@+id/menu_create_dir"
+ android:title="@string/menu_create_dir"
+ android:icon="@drawable/ic_menu_new_folder"
+ android:alphabeticShortcut="e"
+ android:showAsAction="never"
+ android:visible="false" />
+ <item
+ android:id="@+id/menu_paste_from_clipboard"
+ android:title="@string/menu_paste_from_clipboard"
+ android:alphabeticShortcut="v"
+ android:showAsAction="never"
+ android:visible="false" />
+ <!-- Copy action is defined in mode_directory.xml -->
+ <item
+ android:id="@+id/menu_sort"
+ android:title="@string/menu_sort"
+ android:icon="@drawable/ic_menu_sortby"
+ android:showAsAction="ifRoom">
+ <menu>
+ <item
+ android:id="@+id/menu_sort_name"
+ android:title="@string/sort_name" />
+ <item
+ android:id="@+id/menu_sort_date"
+ android:title="@string/sort_date" />
+ <item
+ android:id="@+id/menu_sort_size"
+ android:title="@string/sort_size" />
+ </menu>
+ </item>
+ <item
+ android:id="@+id/menu_file_size"
+ android:showAsAction="never"
+ android:visible="false" />
+ <item
+ android:id="@+id/menu_advanced"
+ android:showAsAction="never"
+ android:visible="false" />
+ <item
+ android:id="@+id/menu_settings"
+ android:title="@string/menu_settings"
+ android:showAsAction="never"
+ android:visible="false" />
+ </group>
</menu>
diff --git a/packages/DocumentsUI/res/mipmap-hdpi/ic_launcher_download.png b/packages/DocumentsUI/res/mipmap-hdpi/ic_launcher_downloads.png
similarity index 100%
rename from packages/DocumentsUI/res/mipmap-hdpi/ic_launcher_download.png
rename to packages/DocumentsUI/res/mipmap-hdpi/ic_launcher_downloads.png
Binary files differ
diff --git a/packages/DocumentsUI/res/mipmap-mdpi/ic_launcher_download.png b/packages/DocumentsUI/res/mipmap-mdpi/ic_launcher_downloads.png
similarity index 100%
rename from packages/DocumentsUI/res/mipmap-mdpi/ic_launcher_download.png
rename to packages/DocumentsUI/res/mipmap-mdpi/ic_launcher_downloads.png
Binary files differ
diff --git a/packages/DocumentsUI/res/mipmap-xhdpi/ic_launcher_download.png b/packages/DocumentsUI/res/mipmap-xhdpi/ic_launcher_downloads.png
similarity index 100%
rename from packages/DocumentsUI/res/mipmap-xhdpi/ic_launcher_download.png
rename to packages/DocumentsUI/res/mipmap-xhdpi/ic_launcher_downloads.png
Binary files differ
diff --git a/packages/DocumentsUI/res/mipmap-xxhdpi/ic_launcher_download.png b/packages/DocumentsUI/res/mipmap-xxhdpi/ic_launcher_downloads.png
similarity index 100%
rename from packages/DocumentsUI/res/mipmap-xxhdpi/ic_launcher_download.png
rename to packages/DocumentsUI/res/mipmap-xxhdpi/ic_launcher_downloads.png
Binary files differ
diff --git a/packages/DocumentsUI/res/mipmap-xxxhdpi/ic_launcher_download.png b/packages/DocumentsUI/res/mipmap-xxxhdpi/ic_launcher_downloads.png
similarity index 100%
rename from packages/DocumentsUI/res/mipmap-xxxhdpi/ic_launcher_download.png
rename to packages/DocumentsUI/res/mipmap-xxxhdpi/ic_launcher_downloads.png
Binary files differ
diff --git a/packages/DocumentsUI/res/values-af/strings.xml b/packages/DocumentsUI/res/values-af/strings.xml
index 5abb9d7..1a7c620 100644
--- a/packages/DocumentsUI/res/values-af/strings.xml
+++ b/packages/DocumentsUI/res/values-af/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Kon nie dokument hernoem nie"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Sommige lêers is omgeskakel"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Gee <xliff:g id="APPNAME"><b>^1</b></xliff:g> toegang tot <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>-gids op <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Gee <xliff:g id="APPNAME"><b>^1</b></xliff:g> toegang tot <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>-gids?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Gee <xliff:g id="APPNAME"><b>^1</b></xliff:g> toegang tot jou data, insluitend foto\'s en video\'s, op <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Moenie weer vra nie"</string>
<string name="allow" msgid="7225948811296386551">"Laat toe"</string>
<string name="deny" msgid="2081879885755434506">"Weier"</string>
@@ -118,11 +120,18 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> gekies</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> gekies</item>
</plurals>
- <!-- no translation found for delete_filename_confirmation_message (5312817725577537488) -->
- <skip />
- <!-- no translation found for delete_foldername_confirmation_message (5885501832257285329) -->
- <skip />
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Vee \"<xliff:g id="NAME">%1$s</xliff:g>\" uit?"</string>
+ <string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Vee vouer \"<xliff:g id="NAME">%1$s</xliff:g>\" en sy inhoud uit?"</string>
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other">Vee <xliff:g id="COUNT_1">%1$d</xliff:g> lêers uit?</item>
+ <item quantity="one">Vee <xliff:g id="COUNT_0">%1$d</xliff:g> lêer uit?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other">Vee <xliff:g id="COUNT_1">%1$d</xliff:g> vouers en hul inhoud uit?</item>
+ <item quantity="one">Vee <xliff:g id="COUNT_0">%1$d</xliff:g> vouer en sy inhoud uit?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other">Vee <xliff:g id="COUNT_1">%1$d</xliff:g> items uit?</item>
+ <item quantity="one">Vee <xliff:g id="COUNT_0">%1$d</xliff:g> item uit?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-am/strings.xml b/packages/DocumentsUI/res/values-am/strings.xml
index b77b82cc..ca43dab 100644
--- a/packages/DocumentsUI/res/values-am/strings.xml
+++ b/packages/DocumentsUI/res/values-am/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"ሰነዱን ዳግም መሰየም አልተሳካም"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"አንዳንድ ፋይሎች ተለውጠዋል"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> በ<xliff:g id="STORAGE"><i>^3</i></xliff:g> ላይ የ<xliff:g id="DIRECTORY"><i>^2</i></xliff:g> ማውጫ መደረሻ ይሰጠው?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"የ<xliff:g id="DIRECTORY"><i>^2</i></xliff:g> ማውጫ መዳረሻ ለ<xliff:g id="APPNAME"><b>^1</b></xliff:g> ይሰጠው?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"በ<xliff:g id="STORAGE"><i>^2</i></xliff:g> ላይ ያሉትን ፎቶዎች እና ቪዲዮዎች ጨምሮ የውሂብዎ መዳረሻ ለ<xliff:g id="APPNAME"><b>^1</b></xliff:g> ይሰጥ?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"ዳግም አትጠይቅ"</string>
<string name="allow" msgid="7225948811296386551">"ይፍቀዱ"</string>
<string name="deny" msgid="2081879885755434506">"ያስተባብሉ"</string>
@@ -118,11 +120,18 @@
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ተመርጠዋል</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ተመርጠዋል</item>
</plurals>
- <!-- no translation found for delete_filename_confirmation_message (5312817725577537488) -->
- <skip />
- <!-- no translation found for delete_foldername_confirmation_message (5885501832257285329) -->
- <skip />
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <string name="delete_filename_confirmation_message" msgid="5312817725577537488">"«<xliff:g id="NAME">%1$s</xliff:g>» ይሰረዝ?"</string>
+ <string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"አቃፊ «<xliff:g id="NAME">%1$s</xliff:g>» እና ይዘቶቹ ይሰረዙ?"</string>
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ፋይሎች ይሰረዙ?</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ፋይሎች ይሰረዙ?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> አቃፊዎች እና ይዘቶቻቸው ይሰረዙ?</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> አቃፊዎች እና ይዘቶቻቸው ይሰረዙ?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ንጥሎች ይሰረዙ?</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ንጥሎች ይሰረዙ?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-ar/strings.xml b/packages/DocumentsUI/res/values-ar/strings.xml
index 1625043..e6fac50 100644
--- a/packages/DocumentsUI/res/values-ar/strings.xml
+++ b/packages/DocumentsUI/res/values-ar/strings.xml
@@ -139,6 +139,8 @@
<string name="rename_error" msgid="4203041674883412606">"أخفقت إعادة تسمية المستند."</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"تم تحويل بعض الملفات"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"هل تريد منح التطبيق <xliff:g id="APPNAME"><b>^1</b></xliff:g> حق الوصول إلى الدليل <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> على <xliff:g id="STORAGE"><i>^3</i></xliff:g>؟"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"هل تريد تمكين <xliff:g id="APPNAME"><b>^1</b></xliff:g> من الدخول إلى دليل <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>؟"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"هل تريد منح <xliff:g id="APPNAME"><b>^1</b></xliff:g> حق الوصول إلى بياناتك، بما في ذلك الصور ومقاطع الفيديو على <xliff:g id="STORAGE"><i>^2</i></xliff:g>؟"</string>
<string name="never_ask_again" msgid="4295278542972859268">"عدم السؤال مرة أخرى"</string>
<string name="allow" msgid="7225948811296386551">"السماح"</string>
<string name="deny" msgid="2081879885755434506">"رفض"</string>
@@ -152,7 +154,28 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"هل تريد حذف \"<xliff:g id="NAME">%1$s</xliff:g>\"؟"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"هل تريد حذف المجلد \"<xliff:g id="NAME">%1$s</xliff:g>\" ومحتوياته؟"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="zero">هل تريد حذف <xliff:g id="COUNT_1">%1$d</xliff:g> ملف؟</item>
+ <item quantity="two">هل تريد حذف ملفين (<xliff:g id="COUNT_1">%1$d</xliff:g>)؟</item>
+ <item quantity="few">هل تريد حذف <xliff:g id="COUNT_1">%1$d</xliff:g> ملفات؟</item>
+ <item quantity="many">هل تريد حذف <xliff:g id="COUNT_1">%1$d</xliff:g> ملفًا؟</item>
+ <item quantity="other">هل تريد حذف <xliff:g id="COUNT_1">%1$d</xliff:g> ملف؟</item>
+ <item quantity="one">هل تريد حذف <xliff:g id="COUNT_0">%1$d</xliff:g> ملف؟</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="zero">هل تريد حذف <xliff:g id="COUNT_1">%1$d</xliff:g> مجلد ومحتوياته؟</item>
+ <item quantity="two">هل تريد حذف مجلدين (<xliff:g id="COUNT_1">%1$d</xliff:g>) ومحتوياتهما؟</item>
+ <item quantity="few">هل تريد حذف <xliff:g id="COUNT_1">%1$d</xliff:g> مجلدات ومحتوياتها؟</item>
+ <item quantity="many">هل تريد حذف <xliff:g id="COUNT_1">%1$d</xliff:g> مجلدًا ومحتويات هذه المجلدات؟</item>
+ <item quantity="other">هل تريد حذف <xliff:g id="COUNT_1">%1$d</xliff:g> مجلد ومحتويات هذه المجلدات؟</item>
+ <item quantity="one">هل تريد حذف <xliff:g id="COUNT_0">%1$d</xliff:g> مجلد ومحتوياته؟</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="zero">هل تريد حذف <xliff:g id="COUNT_1">%1$d</xliff:g> عنصر؟</item>
+ <item quantity="two">هل تريد حذف عنصرين (<xliff:g id="COUNT_1">%1$d</xliff:g>)؟</item>
+ <item quantity="few">هل تريد حذف <xliff:g id="COUNT_1">%1$d</xliff:g> عناصر؟</item>
+ <item quantity="many">هل تريد حذف <xliff:g id="COUNT_1">%1$d</xliff:g> عنصرًا؟</item>
+ <item quantity="other">هل تريد حذف <xliff:g id="COUNT_1">%1$d</xliff:g> عنصر؟</item>
+ <item quantity="one">هل تريد حذف <xliff:g id="COUNT_0">%1$d</xliff:g> عنصر؟</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-az-rAZ/strings.xml b/packages/DocumentsUI/res/values-az-rAZ/strings.xml
index 0f0eda4..75a0686 100644
--- a/packages/DocumentsUI/res/values-az-rAZ/strings.xml
+++ b/packages/DocumentsUI/res/values-az-rAZ/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Sənəd adını dəyişmək uğursuz oldu"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Bəzi fayllar konvertasiya edilib"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="STORAGE"><i>^3</i></xliff:g> yaddaşında <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> kataloquna <xliff:g id="APPNAME"><b>^1</b></xliff:g> girişi təqdim edilsin?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"<xliff:g id="DIRECTORY"><i>^2</i></xliff:g> kataloquna <xliff:g id="APPNAME"><b>^1</b></xliff:g> girişi təqdim edilsin?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="STORAGE"><i>^2</i></xliff:g> yaddaşında foto və videolar daxil olmaqla datanıza <xliff:g id="APPNAME"><b>^1</b></xliff:g> girişi təmin edilsin?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Bir daha soruşmayın"</string>
<string name="allow" msgid="7225948811296386551">"İcazə verin"</string>
<string name="deny" msgid="2081879885755434506">"Rədd et"</string>
@@ -118,11 +120,18 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> seçilib</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> seçilib</item>
</plurals>
- <!-- no translation found for delete_filename_confirmation_message (5312817725577537488) -->
- <skip />
- <!-- no translation found for delete_foldername_confirmation_message (5885501832257285329) -->
- <skip />
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <string name="delete_filename_confirmation_message" msgid="5312817725577537488">"\"<xliff:g id="NAME">%1$s</xliff:g>\" silinsin?"</string>
+ <string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"\"<xliff:g id="NAME">%1$s</xliff:g>\" qovluğu və onun məzmunu silinsin?"</string>
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other"> <xliff:g id="COUNT_1">%1$d</xliff:g> fayl silinsin?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> fayl silinsin?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> qovluq və onun məzmunu silinsin?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> qovluq və onun məzmunu silinsin?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> element silinsin?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> element silinsin?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-b+sr+Latn/strings.xml b/packages/DocumentsUI/res/values-b+sr+Latn/strings.xml
index 7bbf942..34c08bd 100644
--- a/packages/DocumentsUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/DocumentsUI/res/values-b+sr+Latn/strings.xml
@@ -118,6 +118,8 @@
<string name="rename_error" msgid="4203041674883412606">"Preimenovanje dokumenta nije uspelo"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Neke datoteke su konvertovane"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Želite li da aplikaciji <xliff:g id="APPNAME"><b>^1</b></xliff:g> odobrite pristup direktorijumu <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> na memorijskom prostoru <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Želite da dozvolite da <xliff:g id="APPNAME"><b>^1</b></xliff:g> pristupa direktorijumu <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Želite da li da dozvolite da aplikacija <xliff:g id="APPNAME"><b>^1</b></xliff:g> pristupa podacima, uključujući slike i video snimke, na lokaciji <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Ne pitaj ponovo"</string>
<string name="allow" msgid="7225948811296386551">"Dozvoli"</string>
<string name="deny" msgid="2081879885755434506">"Odbij"</string>
@@ -128,7 +130,19 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Želite li da izbrišete „<xliff:g id="NAME">%1$s</xliff:g>“?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Želite li da izbrišete direktorijum „<xliff:g id="NAME">%1$s</xliff:g>“ i njegov sadržaj?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="one">Želite li da izbrišete <xliff:g id="COUNT_1">%1$d</xliff:g> datoteku?</item>
+ <item quantity="few">Želite li da izbrišete <xliff:g id="COUNT_1">%1$d</xliff:g> datoteke?</item>
+ <item quantity="other">Želite li da izbrišete <xliff:g id="COUNT_1">%1$d</xliff:g> datoteka?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="one">Želite li da izbrišete <xliff:g id="COUNT_1">%1$d</xliff:g> direktorijum i njihov sadržaj?</item>
+ <item quantity="few">Želite li da izbrišete <xliff:g id="COUNT_1">%1$d</xliff:g> direktorijuma i njihov sadržaj?</item>
+ <item quantity="other">Želite li da izbrišete <xliff:g id="COUNT_1">%1$d</xliff:g> direktorijuma i njihov sadržaj?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="one">Želite li da izbrišete <xliff:g id="COUNT_1">%1$d</xliff:g> stavku?</item>
+ <item quantity="few">Želite li da izbrišete <xliff:g id="COUNT_1">%1$d</xliff:g> stavke?</item>
+ <item quantity="other">Želite li da izbrišete <xliff:g id="COUNT_1">%1$d</xliff:g> stavki?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-be-rBY/strings.xml b/packages/DocumentsUI/res/values-be-rBY/strings.xml
index 4621d9b..8493c477 100644
--- a/packages/DocumentsUI/res/values-be-rBY/strings.xml
+++ b/packages/DocumentsUI/res/values-be-rBY/strings.xml
@@ -125,6 +125,8 @@
<string name="rename_error" msgid="4203041674883412606">"Не атрымалася перайменаваць дакумент"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Некаторыя файлы былі сканвертаваныя"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Даць праграме <xliff:g id="APPNAME"><b>^1</b></xliff:g> доступ да дырэкторыі <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> у <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Даць праграме <xliff:g id="APPNAME"><b>^1</b></xliff:g> доступ да дырэкторыі <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Даць <xliff:g id="APPNAME"><b>^1</b></xliff:g> доступ да вашых даных, у тым ліку фатаграфій і відэа, на <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Больш не пытацца"</string>
<string name="allow" msgid="7225948811296386551">"Дазволіць"</string>
<string name="deny" msgid="2081879885755434506">"Забараніць"</string>
@@ -134,11 +136,24 @@
<item quantity="many">Выбрана <xliff:g id="COUNT_1">%1$d</xliff:g></item>
<item quantity="other">Выбрана <xliff:g id="COUNT_1">%1$d</xliff:g></item>
</plurals>
- <!-- no translation found for delete_filename_confirmation_message (5312817725577537488) -->
- <skip />
- <!-- no translation found for delete_foldername_confirmation_message (5885501832257285329) -->
- <skip />
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Выдаліць \"<xliff:g id="NAME">%1$s</xliff:g>\"?"</string>
+ <string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Выдаліць папку \"<xliff:g id="NAME">%1$s</xliff:g>\" і яе змесціва?"</string>
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="one">Выдаліць <xliff:g id="COUNT_1">%1$d</xliff:g> файл?</item>
+ <item quantity="few">Выдаліць <xliff:g id="COUNT_1">%1$d</xliff:g> файлы?</item>
+ <item quantity="many">Выдаліць <xliff:g id="COUNT_1">%1$d</xliff:g> файлаў?</item>
+ <item quantity="other">Выдаліць <xliff:g id="COUNT_1">%1$d</xliff:g> файла?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="one">Выдаліць <xliff:g id="COUNT_1">%1$d</xliff:g> папку і іх змесціва?</item>
+ <item quantity="few">Выдаліць <xliff:g id="COUNT_1">%1$d</xliff:g> папкі і іх змесціва?</item>
+ <item quantity="many">Выдаліць <xliff:g id="COUNT_1">%1$d</xliff:g> папак і іх змесціва?</item>
+ <item quantity="other">Выдаліць <xliff:g id="COUNT_1">%1$d</xliff:g> папкі і іх змесціва?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="one">Выдаліць <xliff:g id="COUNT_1">%1$d</xliff:g> элемент?</item>
+ <item quantity="few">Выдаліць <xliff:g id="COUNT_1">%1$d</xliff:g> элементы?</item>
+ <item quantity="many">Выдаліць <xliff:g id="COUNT_1">%1$d</xliff:g> элементаў?</item>
+ <item quantity="other">Выдаліць <xliff:g id="COUNT_1">%1$d</xliff:g> элемента?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-bg/strings.xml b/packages/DocumentsUI/res/values-bg/strings.xml
index 329f92f..1914bf5 100644
--- a/packages/DocumentsUI/res/values-bg/strings.xml
+++ b/packages/DocumentsUI/res/values-bg/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Преименуването на документа не бе успешно"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Някои файлове бяха преобразувани"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Да се предостави ли на <xliff:g id="APPNAME"><b>^1</b></xliff:g> достъп до директорията „<xliff:g id="DIRECTORY"><i>^2</i></xliff:g>“ в/ъв <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Да се предостави ли на <xliff:g id="APPNAME"><b>^1</b></xliff:g> достъп до директорията „<xliff:g id="DIRECTORY"><i>^2</i></xliff:g>“?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Да се предостави ли на <xliff:g id="APPNAME"><b>^1</b></xliff:g> достъп до данните ви в хранилището (<xliff:g id="STORAGE"><i>^2</i></xliff:g>), включително снимки и видеоклипове?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Без повторно питане"</string>
<string name="allow" msgid="7225948811296386551">"Разрешаване"</string>
<string name="deny" msgid="2081879885755434506">"Отказване"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Искате ли да изтриете „<xliff:g id="NAME">%1$s</xliff:g>“?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Искате ли да изтриете папката „<xliff:g id="NAME">%1$s</xliff:g>“ и съдържанието в нея?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other">Искате ли да изтриете <xliff:g id="COUNT_1">%1$d</xliff:g> файла?</item>
+ <item quantity="one">Искате ли да изтриете <xliff:g id="COUNT_0">%1$d</xliff:g> файл?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other">Искате ли да изтриете <xliff:g id="COUNT_1">%1$d</xliff:g> папки и съдържанието в тях?</item>
+ <item quantity="one">Искате ли да изтриете <xliff:g id="COUNT_0">%1$d</xliff:g> папка и съдържанието в нея?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other">Искате ли да изтриете <xliff:g id="COUNT_1">%1$d</xliff:g> елемента?</item>
+ <item quantity="one">Искате ли да изтриете <xliff:g id="COUNT_0">%1$d</xliff:g> елемент?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-bn-rBD/strings.xml b/packages/DocumentsUI/res/values-bn-rBD/strings.xml
index 936e2dd..d931c2b 100644
--- a/packages/DocumentsUI/res/values-bn-rBD/strings.xml
+++ b/packages/DocumentsUI/res/values-bn-rBD/strings.xml
@@ -32,7 +32,7 @@
<string name="menu_share" msgid="3075149983979628146">"শেয়ার করুন"</string>
<string name="menu_delete" msgid="8138799623850614177">"মুছুন"</string>
<string name="menu_select_all" msgid="8323579667348729928">"সবগুলি নির্বাচন করুন"</string>
- <string name="menu_copy" msgid="3612326052677229148">"এতে অনুলিপি করুন…"</string>
+ <string name="menu_copy" msgid="3612326052677229148">"এতে কপি করুন…"</string>
<string name="menu_move" msgid="1828090633118079817">"এতে সরান..."</string>
<string name="menu_new_window" msgid="1226032889278727538">"নতুন উইন্ডো"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"প্রতিলিপি করুন"</string>
@@ -42,7 +42,7 @@
<string name="menu_file_size_show" msgid="3240323619260823076">"ফাইলের আকার দেখান"</string>
<string name="menu_file_size_hide" msgid="8881975928502581042">"ফাইলের আকার লুকান"</string>
<string name="button_select" msgid="527196987259139214">"নির্বাচন করুন"</string>
- <string name="button_copy" msgid="8706475544635021302">"অনুলিপি করুন"</string>
+ <string name="button_copy" msgid="8706475544635021302">"কপি করুন"</string>
<string name="button_move" msgid="2202666023104202232">"সরান"</string>
<string name="button_dismiss" msgid="3714065566893946085">"খারিজ করুন"</string>
<string name="button_retry" msgid="4392027584153752797">"আবার চেষ্টা করুন"</string>
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"দস্তাবেজের পুনঃনামকরণ ব্যর্থ হয়েছে৷"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"কিছু ফাইল রূপান্তরিত হয়েছে"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> কে <xliff:g id="STORAGE"><i>^3</i></xliff:g> এ <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> সংগ্রহ অ্যাক্সেস করার মঞ্জুরি দিতে চান?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> কে <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> সংগ্রহ অ্যাক্সেস করার অনুমতি দেবেন?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="STORAGE"><i>^2</i></xliff:g> এ থাকা ফটো ও ভিডিওগুলি সমেত <xliff:g id="APPNAME"><b>^1</b></xliff:g> কে আপনার ডেটা অ্যাক্সেস করার অনুমতি দেবেন?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"আর জিজ্ঞাসা করবেন না"</string>
<string name="allow" msgid="7225948811296386551">"অনুমতি দিন"</string>
<string name="deny" msgid="2081879885755434506">"আস্বীকার করুন"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"\"<xliff:g id="NAME">%1$s</xliff:g>\" মুছবেন?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"\"<xliff:g id="NAME">%1$s</xliff:g>\" ফোল্ডার এবং এটির সামগ্রীগুলিকে মুছবেন?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g>টি ফাইল মুছবেন?</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g>টি ফাইল মুছবেন?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g>টি ফোল্ডার এবং সেগুলির সামগ্রী মুছবেন?</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g>টি ফোল্ডার এবং সেগুলির সামগ্রী মুছবেন?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g>টি আইটেম মুছবেন?</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g>টি আইটেম মুছবেন?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-bs-rBA/strings.xml b/packages/DocumentsUI/res/values-bs-rBA/strings.xml
index d23c884..47ff436 100644
--- a/packages/DocumentsUI/res/values-bs-rBA/strings.xml
+++ b/packages/DocumentsUI/res/values-bs-rBA/strings.xml
@@ -118,6 +118,8 @@
<string name="rename_error" msgid="4203041674883412606">"Nije uspjelo preimenovanje dokumenta"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Neke od datoteka su pretvorene"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Omogućiti <xliff:g id="APPNAME"><b>^1</b></xliff:g> pristup direktoriju <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> sa <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Odobriti aplikaciji <xliff:g id="APPNAME"><b>^1</b></xliff:g> pristup direktoriju <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Želite li odobriti aplikaciji <xliff:g id="APPNAME"><b>^1</b></xliff:g> pristup svojim podacima, uključujući fotografije i video zapise, na <xliff:g id="STORAGE"><i>^2</i></xliff:g> ?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Ne pitaj ponovo"</string>
<string name="allow" msgid="7225948811296386551">"Dozvoli"</string>
<string name="deny" msgid="2081879885755434506">"Odbijte"</string>
@@ -126,11 +128,21 @@
<item quantity="few"><xliff:g id="COUNT_1">%1$d</xliff:g> stavke su odabrane</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> stavki je odabrano</item>
</plurals>
- <!-- no translation found for delete_filename_confirmation_message (5312817725577537488) -->
- <skip />
- <!-- no translation found for delete_foldername_confirmation_message (5885501832257285329) -->
- <skip />
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Želite li izbrisati \"<xliff:g id="NAME">%1$s</xliff:g>\"?"</string>
+ <string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Želite li izbrisati folder \"<xliff:g id="NAME">%1$s</xliff:g>\" i njegov sadržaj?"</string>
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="one">Želite li izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> fajl?</item>
+ <item quantity="few">Želite li izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> fajla?</item>
+ <item quantity="other">Želite li izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> fajlova?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="one">Želite li izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> folder i njihov sadržaj?</item>
+ <item quantity="few">Želite li izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> foldera i njihov sadržaj?</item>
+ <item quantity="other">Želite li izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> foldera i njihov sadržaj?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="one">Želite li izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> stavku?</item>
+ <item quantity="few">Želite li izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> stavke?</item>
+ <item quantity="other">Želite li izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> stavki?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-ca/strings.xml b/packages/DocumentsUI/res/values-ca/strings.xml
index 044350a..09af97d 100644
--- a/packages/DocumentsUI/res/values-ca/strings.xml
+++ b/packages/DocumentsUI/res/values-ca/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"No s\'ha pogut canviar el nom del document"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"S\'han convertit alguns fitxers"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Vols que l\'aplicació <xliff:g id="APPNAME"><b>^1</b></xliff:g> tingui accés al directori <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> de l\'emmagatzematge <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Vols que l\'aplicació <xliff:g id="APPNAME"><b>^1</b></xliff:g> tingui accés al directori <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Vols que l\'aplicació <xliff:g id="APPNAME"><b>^1</b></xliff:g> tingui accés a les dades de <xliff:g id="STORAGE"><i>^2</i></xliff:g>, incloses les fotos i els vídeos?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"No m\'ho demanis més"</string>
<string name="allow" msgid="7225948811296386551">"Permet"</string>
<string name="deny" msgid="2081879885755434506">"Denega"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Vols suprimir el fitxer <xliff:g id="NAME">%1$s</xliff:g>?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Vols suprimir la carpeta <xliff:g id="NAME">%1$s</xliff:g> i el seu contingut?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other">Vols suprimir <xliff:g id="COUNT_1">%1$d</xliff:g> fitxers?</item>
+ <item quantity="one">Vols suprimir <xliff:g id="COUNT_0">%1$d</xliff:g> fitxer?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other">Vols suprimir <xliff:g id="COUNT_1">%1$d</xliff:g> carpetes i el seu contingut?</item>
+ <item quantity="one">Vols suprimir <xliff:g id="COUNT_0">%1$d</xliff:g> carpeta i el seu contingut?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other">Vols suprimir <xliff:g id="COUNT_1">%1$d</xliff:g> elements?</item>
+ <item quantity="one">Vols suprimir <xliff:g id="COUNT_0">%1$d</xliff:g> element?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-cs/strings.xml b/packages/DocumentsUI/res/values-cs/strings.xml
index ca6f3f1..cba2e0e 100644
--- a/packages/DocumentsUI/res/values-cs/strings.xml
+++ b/packages/DocumentsUI/res/values-cs/strings.xml
@@ -125,6 +125,8 @@
<string name="rename_error" msgid="4203041674883412606">"Dokument se nepodařilo přejmenovat."</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Některé soubory byly převedeny"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Chcete aplikaci <xliff:g id="APPNAME"><b>^1</b></xliff:g> udělit přístup k adresáři <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> v úložišti <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Chcete aplikaci <xliff:g id="APPNAME"><b>^1</b></xliff:g> udělit přístup k adresáři <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Chcete aplikaci <xliff:g id="APPNAME"><b>^1</b></xliff:g> udělit přístup ke svým datům v úložišti <xliff:g id="STORAGE"><i>^2</i></xliff:g>, včetně fotek a videí?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Příště se neptat"</string>
<string name="allow" msgid="7225948811296386551">"Povolit"</string>
<string name="deny" msgid="2081879885755434506">"Odepřít"</string>
@@ -136,7 +138,22 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Smazat soubor <xliff:g id="NAME">%1$s</xliff:g>?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Smazat složku <xliff:g id="NAME">%1$s</xliff:g> a její obsah?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="few">Smazat <xliff:g id="COUNT_1">%1$d</xliff:g> soubory?</item>
+ <item quantity="many">Smazat <xliff:g id="COUNT_1">%1$d</xliff:g> souboru?</item>
+ <item quantity="other">Smazat <xliff:g id="COUNT_1">%1$d</xliff:g> souborů?</item>
+ <item quantity="one">Smazat <xliff:g id="COUNT_0">%1$d</xliff:g> soubor?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="few">Smazat <xliff:g id="COUNT_1">%1$d</xliff:g> složky a jejich obsah?</item>
+ <item quantity="many">Smazat <xliff:g id="COUNT_1">%1$d</xliff:g> složky a jejich obsah?</item>
+ <item quantity="other">Smazat <xliff:g id="COUNT_1">%1$d</xliff:g> složek a jejich obsah?</item>
+ <item quantity="one">Smazat <xliff:g id="COUNT_0">%1$d</xliff:g> složku a její obsah?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="few">Smazat <xliff:g id="COUNT_1">%1$d</xliff:g> položky?</item>
+ <item quantity="many">Smazat <xliff:g id="COUNT_1">%1$d</xliff:g> položky?</item>
+ <item quantity="other">Smazat <xliff:g id="COUNT_1">%1$d</xliff:g> položek?</item>
+ <item quantity="one">Smazat <xliff:g id="COUNT_0">%1$d</xliff:g> položku?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-da/strings.xml b/packages/DocumentsUI/res/values-da/strings.xml
index c4e7770..f048e34 100644
--- a/packages/DocumentsUI/res/values-da/strings.xml
+++ b/packages/DocumentsUI/res/values-da/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Dokumentet kunne ikke omdøbes"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Nogle filer er konverteret"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Vil du give <xliff:g id="APPNAME"><b>^1</b></xliff:g> adgang til mappen <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> på <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Vil du give <xliff:g id="APPNAME"><b>^1</b></xliff:g> adgang til indekset <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Vil du give <xliff:g id="APPNAME"><b>^1</b></xliff:g> adgang til dine data, herunder billeder og videoer på <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Spørg ikke igen"</string>
<string name="allow" msgid="7225948811296386551">"Tillad"</string>
<string name="deny" msgid="2081879885755434506">"Afvis"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Vil du slette \"<xliff:g id="NAME">%1$s</xliff:g>\"?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Vil du slette mappen \"<xliff:g id="NAME">%1$s</xliff:g>\" og dens indhold?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="one">Vil du slette <xliff:g id="COUNT_1">%1$d</xliff:g> fil?</item>
+ <item quantity="other">Vil du slette <xliff:g id="COUNT_1">%1$d</xliff:g> filer?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="one">Vil du slette <xliff:g id="COUNT_1">%1$d</xliff:g> mappe og dens indhold?</item>
+ <item quantity="other">Vil du slette <xliff:g id="COUNT_1">%1$d</xliff:g> mapper og deres indhold?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="one">Vil du slette <xliff:g id="COUNT_1">%1$d</xliff:g> element?</item>
+ <item quantity="other">Vil du slette <xliff:g id="COUNT_1">%1$d</xliff:g> elementer?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-de/strings.xml b/packages/DocumentsUI/res/values-de/strings.xml
index 766222c..a6eae70 100644
--- a/packages/DocumentsUI/res/values-de/strings.xml
+++ b/packages/DocumentsUI/res/values-de/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Dokument konnte nicht umbenannt werden"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Einige Dateien wurden konvertiert"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> Zugriff auf das Verzeichnis <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> auf <xliff:g id="STORAGE"><i>^3</i></xliff:g> geben?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Möchtest du <xliff:g id="APPNAME"><b>^1</b></xliff:g> Zugriff auf das Verzeichnis <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> geben?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Möchtest du <xliff:g id="APPNAME"><b>^1</b></xliff:g> Zugriff auf deine Daten auf <xliff:g id="STORAGE"><i>^2</i></xliff:g> geben, einschließlich Fotos und Videos?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Nicht mehr fragen"</string>
<string name="allow" msgid="7225948811296386551">"Zulassen"</string>
<string name="deny" msgid="2081879885755434506">"Ablehnen"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"\"<xliff:g id="NAME">%1$s</xliff:g>\" löschen?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Ordner \"<xliff:g id="NAME">%1$s</xliff:g>\" und dessen Inhalte löschen?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other">Möchtest du <xliff:g id="COUNT_1">%1$d</xliff:g> Dateien löschen?</item>
+ <item quantity="one">Möchtest du <xliff:g id="COUNT_0">%1$d</xliff:g> Datei löschen?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other">Möchtest du <xliff:g id="COUNT_1">%1$d</xliff:g> Ordner und deren Inhalte löschen?</item>
+ <item quantity="one">Möchtest du <xliff:g id="COUNT_0">%1$d</xliff:g> Ordner und dessen Inhalte löschen?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other">Möchtest du <xliff:g id="COUNT_1">%1$d</xliff:g> Elemente löschen?</item>
+ <item quantity="one">Möchtest du <xliff:g id="COUNT_0">%1$d</xliff:g> Element löschen?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-el/strings.xml b/packages/DocumentsUI/res/values-el/strings.xml
index 3bbcf776..7dc2067 100644
--- a/packages/DocumentsUI/res/values-el/strings.xml
+++ b/packages/DocumentsUI/res/values-el/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Αποτυχία μετονομασίας εγγράφου"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Ορισμένα αρχεία μετατράπηκαν"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Να εκχωρηθεί στην εφαρμογή <xliff:g id="APPNAME"><b>^1</b></xliff:g> πρόσβαση στον κατάλογο <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> στον αποθηκευτικό χώρο <xliff:g id="STORAGE"><i>^3</i></xliff:g>;"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Εκχώρηση πρόσβασης στην εφαρμογή <xliff:g id="APPNAME"><b>^1</b></xliff:g> στον κατάλογο <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>;"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Θέλετε να εκχωρήσετε πρόσβαση στα δεδομένα σας στην εφαρμογή <xliff:g id="APPNAME"><b>^1</b></xliff:g>, συμπεριλαμβανομένων των φωτογραφιών και των βίντεό σας, στον αποθηκευτικό χώρο <xliff:g id="STORAGE"><i>^2</i></xliff:g>;"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Να μην ερωτηθώ ξανά"</string>
<string name="allow" msgid="7225948811296386551">"Να επιτρέπεται"</string>
<string name="deny" msgid="2081879885755434506">"Άρνηση"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Να διαγραφεί το αρχείο \"<xliff:g id="NAME">%1$s</xliff:g>\";"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Να διαγραφεί ο φάκελος \"<xliff:g id="NAME">%1$s</xliff:g>\" και τα περιεχόμενά του;"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other">Να διαγραφούν <xliff:g id="COUNT_1">%1$d</xliff:g> αρχεία;</item>
+ <item quantity="one">Να διαγραφεί <xliff:g id="COUNT_0">%1$d</xliff:g> αρχείο;</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other">Να διαγραφούν <xliff:g id="COUNT_1">%1$d</xliff:g> φάκελοι και τα περιεχόμενά τους;</item>
+ <item quantity="one">Να διαγραφεί <xliff:g id="COUNT_0">%1$d</xliff:g> φάκελος και τα περιεχόμενά του;</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other">Να διαγραφούν <xliff:g id="COUNT_1">%1$d</xliff:g> στοιχεία;</item>
+ <item quantity="one">Να διαγραφεί <xliff:g id="COUNT_0">%1$d</xliff:g> στοιχείο;</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-en-rAU/strings.xml b/packages/DocumentsUI/res/values-en-rAU/strings.xml
index d28b675..8524de5 100644
--- a/packages/DocumentsUI/res/values-en-rAU/strings.xml
+++ b/packages/DocumentsUI/res/values-en-rAU/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Failed to rename document"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Some files were converted"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Grant <xliff:g id="APPNAME"><b>^1</b></xliff:g> access to <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> directory on <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Grant <xliff:g id="APPNAME"><b>^1</b></xliff:g> access to <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> directory?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Grant <xliff:g id="APPNAME"><b>^1</b></xliff:g> access to your data, including photos and videos, on <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Don\'t ask again"</string>
<string name="allow" msgid="7225948811296386551">"Allow"</string>
<string name="deny" msgid="2081879885755434506">"Deny"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Delete \"<xliff:g id="NAME">%1$s</xliff:g>\"?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Delete folder \"<xliff:g id="NAME">%1$s</xliff:g>\" and its contents?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other">Delete <xliff:g id="COUNT_1">%1$d</xliff:g> files?</item>
+ <item quantity="one">Delete <xliff:g id="COUNT_0">%1$d</xliff:g> file?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other">Delete <xliff:g id="COUNT_1">%1$d</xliff:g> folders and their contents?</item>
+ <item quantity="one">Delete <xliff:g id="COUNT_0">%1$d</xliff:g> folder and its contents?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other">Delete <xliff:g id="COUNT_1">%1$d</xliff:g> items?</item>
+ <item quantity="one">Delete <xliff:g id="COUNT_0">%1$d</xliff:g> item?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-en-rGB/strings.xml b/packages/DocumentsUI/res/values-en-rGB/strings.xml
index d28b675..8524de5 100644
--- a/packages/DocumentsUI/res/values-en-rGB/strings.xml
+++ b/packages/DocumentsUI/res/values-en-rGB/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Failed to rename document"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Some files were converted"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Grant <xliff:g id="APPNAME"><b>^1</b></xliff:g> access to <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> directory on <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Grant <xliff:g id="APPNAME"><b>^1</b></xliff:g> access to <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> directory?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Grant <xliff:g id="APPNAME"><b>^1</b></xliff:g> access to your data, including photos and videos, on <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Don\'t ask again"</string>
<string name="allow" msgid="7225948811296386551">"Allow"</string>
<string name="deny" msgid="2081879885755434506">"Deny"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Delete \"<xliff:g id="NAME">%1$s</xliff:g>\"?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Delete folder \"<xliff:g id="NAME">%1$s</xliff:g>\" and its contents?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other">Delete <xliff:g id="COUNT_1">%1$d</xliff:g> files?</item>
+ <item quantity="one">Delete <xliff:g id="COUNT_0">%1$d</xliff:g> file?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other">Delete <xliff:g id="COUNT_1">%1$d</xliff:g> folders and their contents?</item>
+ <item quantity="one">Delete <xliff:g id="COUNT_0">%1$d</xliff:g> folder and its contents?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other">Delete <xliff:g id="COUNT_1">%1$d</xliff:g> items?</item>
+ <item quantity="one">Delete <xliff:g id="COUNT_0">%1$d</xliff:g> item?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-en-rIN/strings.xml b/packages/DocumentsUI/res/values-en-rIN/strings.xml
index d28b675..8524de5 100644
--- a/packages/DocumentsUI/res/values-en-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-en-rIN/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Failed to rename document"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Some files were converted"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Grant <xliff:g id="APPNAME"><b>^1</b></xliff:g> access to <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> directory on <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Grant <xliff:g id="APPNAME"><b>^1</b></xliff:g> access to <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> directory?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Grant <xliff:g id="APPNAME"><b>^1</b></xliff:g> access to your data, including photos and videos, on <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Don\'t ask again"</string>
<string name="allow" msgid="7225948811296386551">"Allow"</string>
<string name="deny" msgid="2081879885755434506">"Deny"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Delete \"<xliff:g id="NAME">%1$s</xliff:g>\"?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Delete folder \"<xliff:g id="NAME">%1$s</xliff:g>\" and its contents?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other">Delete <xliff:g id="COUNT_1">%1$d</xliff:g> files?</item>
+ <item quantity="one">Delete <xliff:g id="COUNT_0">%1$d</xliff:g> file?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other">Delete <xliff:g id="COUNT_1">%1$d</xliff:g> folders and their contents?</item>
+ <item quantity="one">Delete <xliff:g id="COUNT_0">%1$d</xliff:g> folder and its contents?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other">Delete <xliff:g id="COUNT_1">%1$d</xliff:g> items?</item>
+ <item quantity="one">Delete <xliff:g id="COUNT_0">%1$d</xliff:g> item?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-es-rUS/strings.xml b/packages/DocumentsUI/res/values-es-rUS/strings.xml
index 6d847b5..87641a7 100644
--- a/packages/DocumentsUI/res/values-es-rUS/strings.xml
+++ b/packages/DocumentsUI/res/values-es-rUS/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"No se pudo cambiar el nombre del documento"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Se convirtieron algunos archivos"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"¿Otorgar acceso a <xliff:g id="APPNAME"><b>^1</b></xliff:g> al directorio <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> en <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"¿Quieres otorgar acceso a <xliff:g id="APPNAME"><b>^1</b></xliff:g> al directorio <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"¿Quieres otorgar acceso a la app de <xliff:g id="APPNAME"><b>^1</b></xliff:g> a tus datos, incluidas tus fotos y videos en <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"No volver a preguntar"</string>
<string name="allow" msgid="7225948811296386551">"Permitir"</string>
<string name="deny" msgid="2081879885755434506">"Denegar"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"¿Deseas borrar \"<xliff:g id="NAME">%1$s</xliff:g>\"?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"¿Deseas borrar la carpeta \"<xliff:g id="NAME">%1$s</xliff:g>\" y su contenido?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other">¿Deseas borrar <xliff:g id="COUNT_1">%1$d</xliff:g> archivos?</item>
+ <item quantity="one">¿Deseas borrar <xliff:g id="COUNT_0">%1$d</xliff:g> archivo?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other">¿Deseas borrar <xliff:g id="COUNT_1">%1$d</xliff:g> carpetas y su contenido?</item>
+ <item quantity="one">¿Deseas borrar <xliff:g id="COUNT_0">%1$d</xliff:g> carpeta y su contenido?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other">¿Deseas borrar <xliff:g id="COUNT_1">%1$d</xliff:g> elementos?</item>
+ <item quantity="one">¿Deseas borrar <xliff:g id="COUNT_0">%1$d</xliff:g> elemento?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-es/strings.xml b/packages/DocumentsUI/res/values-es/strings.xml
index 15b5694..9054561 100644
--- a/packages/DocumentsUI/res/values-es/strings.xml
+++ b/packages/DocumentsUI/res/values-es/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Error al cambiar el nombre del documento"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Se han convertido algunos archivos"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"¿Permitir que <xliff:g id="APPNAME"><b>^1</b></xliff:g> acceda al directorio <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> (<xliff:g id="STORAGE"><i>^3</i></xliff:g>)?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"¿Permitir que <xliff:g id="APPNAME"><b>^1</b></xliff:g> acceda al directorio <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"¿Permitir que <xliff:g id="APPNAME"><b>^1</b></xliff:g> acceda a tus datos, incluidos los vídeos y las fotos, de <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"No volver a preguntar"</string>
<string name="allow" msgid="7225948811296386551">"Permitir"</string>
<string name="deny" msgid="2081879885755434506">"Denegar"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"¿Eliminar <xliff:g id="NAME">%1$s</xliff:g>?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"¿Eliminar la carpeta <xliff:g id="NAME">%1$s</xliff:g> y su contenido?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other">¿Eliminar <xliff:g id="COUNT_1">%1$d</xliff:g> archivos?</item>
+ <item quantity="one">¿Eliminar <xliff:g id="COUNT_0">%1$d</xliff:g> archivo?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other">¿Eliminar <xliff:g id="COUNT_1">%1$d</xliff:g> carpetas y su contenido?</item>
+ <item quantity="one">¿Eliminar <xliff:g id="COUNT_0">%1$d</xliff:g> carpeta y su contenido?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other">¿Eliminar <xliff:g id="COUNT_1">%1$d</xliff:g> elementos?</item>
+ <item quantity="one">¿Eliminar <xliff:g id="COUNT_0">%1$d</xliff:g> elemento?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-et-rEE/strings.xml b/packages/DocumentsUI/res/values-et-rEE/strings.xml
index ed60aa9..4bb2a9b 100644
--- a/packages/DocumentsUI/res/values-et-rEE/strings.xml
+++ b/packages/DocumentsUI/res/values-et-rEE/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Dokumendi ümbernimetamine ebaõnnestus"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Mõned failid teisendati"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Kas anda rakendusele <xliff:g id="APPNAME"><b>^1</b></xliff:g> juurdepääs kataloogile <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> salvestusruumis <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Kas anda rakendusele <xliff:g id="APPNAME"><b>^1</b></xliff:g> juurdepääs kataloogile <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Kas anda rakendusele <xliff:g id="APPNAME"><b>^1</b></xliff:g> juurdepääs teie andmetele (sh fotod ja videod) salvestusruumis <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Ära enam küsi"</string>
<string name="allow" msgid="7225948811296386551">"Luba"</string>
<string name="deny" msgid="2081879885755434506">"Keela"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Kas kustutada fail „<xliff:g id="NAME">%1$s</xliff:g>”?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Kas kustutada kaust „<xliff:g id="NAME">%1$s</xliff:g>” ja selle sisu?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other">Kas kustutada <xliff:g id="COUNT_1">%1$d</xliff:g> faili?</item>
+ <item quantity="one">Kas kustutada <xliff:g id="COUNT_0">%1$d</xliff:g> fail?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other">Kas kustutada <xliff:g id="COUNT_1">%1$d</xliff:g> kausta ja nende sisu?</item>
+ <item quantity="one">Kas kustutada <xliff:g id="COUNT_0">%1$d</xliff:g> kaust ja selle sisu?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other">Kas kustutada <xliff:g id="COUNT_1">%1$d</xliff:g> üksust?</item>
+ <item quantity="one">Kas kustutada <xliff:g id="COUNT_0">%1$d</xliff:g> üksus?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-eu-rES/strings.xml b/packages/DocumentsUI/res/values-eu-rES/strings.xml
index f3d68b0d..d2bf89d 100644
--- a/packages/DocumentsUI/res/values-eu-rES/strings.xml
+++ b/packages/DocumentsUI/res/values-eu-rES/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Ezin izan zaio aldatu izena dokumentuari"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Artxibo batzuk bihurtu dira"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> aplikazioari <xliff:g id="STORAGE"><i>^3</i></xliff:g> unitateko <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> direktorioa atzitzeko baimena eman nahi diozu?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> aplikazioari <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> direktoriorako sarbidea eman nahi diozu?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> aplikazioari zure datuak atzitzea baimendu nahi diozu, besteak beste, <xliff:g id="STORAGE"><i>^2</i></xliff:g> biltegian dituzun argazkiak eta bideoak?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Ez galdetu berriro"</string>
<string name="allow" msgid="7225948811296386551">"Onartu"</string>
<string name="deny" msgid="2081879885755434506">"Ukatu"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"\"<xliff:g id="NAME">%1$s</xliff:g>\" ezabatu nahi duzu?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"\"<xliff:g id="NAME">%1$s</xliff:g>\" karpeta eta bertako edukia ezabatu nahi duzu?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> fitxategi ezabatu nahi dituzu?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> fitxategi ezabatu nahi duzu?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> karpeta eta beren eduki guztia ezabatu nahi duzu?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> karpeta eta bere eduki guztia ezabatu nahi duzu?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> elementu ezabatu nahi dituzu?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> elementu ezabatu nahi duzu?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-fa/strings.xml b/packages/DocumentsUI/res/values-fa/strings.xml
index 80e1aa8..1a4c035 100644
--- a/packages/DocumentsUI/res/values-fa/strings.xml
+++ b/packages/DocumentsUI/res/values-fa/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"نام سند تغییر نکرد"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"بعضی از فایلها تبدیل شدند"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"به <xliff:g id="APPNAME"><b>^1</b></xliff:g> اجازه داده شود به فهرست راهنمای <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> در <xliff:g id="STORAGE"><i>^3</i></xliff:g> دسترسی داشته باشد؟"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"به <xliff:g id="APPNAME"><b>^1</b></xliff:g> اجازه دسترسی به دایرکتوری <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> داده شود؟"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"به <xliff:g id="APPNAME"><b>^1</b></xliff:g> اجازه میدهید به دادههایتان دسترسی پیدا کند، از جمله عکسها و ویدیوهایتان در <xliff:g id="STORAGE"><i>^2</i></xliff:g>؟"</string>
<string name="never_ask_again" msgid="4295278542972859268">"دوباره سؤال نشود"</string>
<string name="allow" msgid="7225948811296386551">"ارزیابیشده"</string>
<string name="deny" msgid="2081879885755434506">"اجازه ندارد"</string>
@@ -118,11 +120,18 @@
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> مورد انتخاب شد</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> مورد انتخاب شد</item>
</plurals>
- <!-- no translation found for delete_filename_confirmation_message (5312817725577537488) -->
- <skip />
- <!-- no translation found for delete_foldername_confirmation_message (5885501832257285329) -->
- <skip />
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <string name="delete_filename_confirmation_message" msgid="5312817725577537488">"«<xliff:g id="NAME">%1$s</xliff:g>» حذف شود؟"</string>
+ <string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"پوشه «<xliff:g id="NAME">%1$s</xliff:g>» و محتوای آن حذف شود؟"</string>
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> فایل حذف شود؟</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> فایل حذف شود؟</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="one"> <xliff:g id="COUNT_1">%1$d</xliff:g> پوشه و محتوای آنها حذف شود؟</item>
+ <item quantity="other"> <xliff:g id="COUNT_1">%1$d</xliff:g> پوشه و محتوای آنها حذف شود؟</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> مورد حذف شود؟</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> مورد حذف شود؟</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-fi/strings.xml b/packages/DocumentsUI/res/values-fi/strings.xml
index 279aad7..dfcfe89 100644
--- a/packages/DocumentsUI/res/values-fi/strings.xml
+++ b/packages/DocumentsUI/res/values-fi/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Dokumentin nimen muuttaminen epäonnistui."</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Joitakin tiedostoja muunnettiin."</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Myönnetäänkö sovellukselle <xliff:g id="APPNAME"><b>^1</b></xliff:g> sijainnissa <xliff:g id="STORAGE"><i>^3</i></xliff:g> olevan hakemiston <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> käyttöoikeus?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Saako <xliff:g id="APPNAME"><b>^1</b></xliff:g> käyttää hakemistoa <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Myönnetäänkö sovellukselle <xliff:g id="APPNAME"><b>^1</b></xliff:g> sijainnissa <xliff:g id="STORAGE"><i>^2</i></xliff:g> olevien tietojesi, mukaan lukien valokuviesi ja videoidesi, käyttöoikeus?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Älä kysy uudestaan"</string>
<string name="allow" msgid="7225948811296386551">"Salli"</string>
<string name="deny" msgid="2081879885755434506">"Kiellä"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Poistetaanko <xliff:g id="NAME">%1$s</xliff:g>?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Poistetaanko kansio <xliff:g id="NAME">%1$s</xliff:g> ja sen sisältö?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other">Poistetaanko <xliff:g id="COUNT_1">%1$d</xliff:g> tiedostoa?</item>
+ <item quantity="one">Poistetaanko <xliff:g id="COUNT_0">%1$d</xliff:g> tiedosto?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other">Poistetaanko <xliff:g id="COUNT_1">%1$d</xliff:g> kansiota ja niiden sisältö?</item>
+ <item quantity="one">Poistetaanko <xliff:g id="COUNT_0">%1$d</xliff:g> kansio ja sen sisältö?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other">Poistetaanko <xliff:g id="COUNT_1">%1$d</xliff:g> kohdetta?</item>
+ <item quantity="one">Poistetaanko <xliff:g id="COUNT_0">%1$d</xliff:g> kohde?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-fr-rCA/strings.xml b/packages/DocumentsUI/res/values-fr-rCA/strings.xml
index 347a3f2..543e226 100644
--- a/packages/DocumentsUI/res/values-fr-rCA/strings.xml
+++ b/packages/DocumentsUI/res/values-fr-rCA/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Impossible de renommer le document"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Certains fichiers ont été convertis"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Accorder à <xliff:g id="APPNAME"><b>^1</b></xliff:g> l\'accès au répertoire <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> sur <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Accorder à <xliff:g id="APPNAME"><b>^1</b></xliff:g> l\'accès au répertoire <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Voulez-vous accorder l\'accès à vos données à <xliff:g id="APPNAME"><b>^1</b></xliff:g>, y compris vos photos et vos vidéos, sur <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Ne plus me demander"</string>
<string name="allow" msgid="7225948811296386551">"Autoriser"</string>
<string name="deny" msgid="2081879885755434506">"Refuser"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Supprimer « <xliff:g id="NAME">%1$s</xliff:g> »?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Supprimer le dossier « <xliff:g id="NAME">%1$s</xliff:g> » et son contenu?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="one">Supprimer <xliff:g id="COUNT_1">%1$d</xliff:g> fichier?</item>
+ <item quantity="other">Supprimer <xliff:g id="COUNT_1">%1$d</xliff:g> fichiers?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="one">Supprimer <xliff:g id="COUNT_1">%1$d</xliff:g> dossier et son contenu?</item>
+ <item quantity="other">Supprimer <xliff:g id="COUNT_1">%1$d</xliff:g> dossiers et leur contenu?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="one">Supprimer <xliff:g id="COUNT_1">%1$d</xliff:g> élément?</item>
+ <item quantity="other">Supprimer <xliff:g id="COUNT_1">%1$d</xliff:g> éléments?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-fr/strings.xml b/packages/DocumentsUI/res/values-fr/strings.xml
index 6030bde..05716d7 100644
--- a/packages/DocumentsUI/res/values-fr/strings.xml
+++ b/packages/DocumentsUI/res/values-fr/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Échec du changement de nom du document."</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Certains fichiers ont été convertis"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Autoriser <xliff:g id="APPNAME"><b>^1</b></xliff:g> à accéder à l\'annuaire \"<xliff:g id="DIRECTORY"><i>^2</i></xliff:g>\" sur <xliff:g id="STORAGE"><i>^3</i></xliff:g> ?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Autoriser <xliff:g id="APPNAME"><b>^1</b></xliff:g> à accéder à l\'annuaire \"<xliff:g id="DIRECTORY"><i>^2</i></xliff:g>\" ?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Autoriser <xliff:g id="APPNAME"><b>^1</b></xliff:g> à accéder à vos données, y compris les photos et les vidéos, sur <xliff:g id="STORAGE"><i>^2</i></xliff:g> ?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Ne plus demander"</string>
<string name="allow" msgid="7225948811296386551">"Autoriser"</string>
<string name="deny" msgid="2081879885755434506">"Refuser"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Supprimer \"<xliff:g id="NAME">%1$s</xliff:g>\" ?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Supprimer le dossier \"<xliff:g id="NAME">%1$s</xliff:g>\" et son contenu ?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="one">Supprimer <xliff:g id="COUNT_1">%1$d</xliff:g> fichier ?</item>
+ <item quantity="other">Supprimer <xliff:g id="COUNT_1">%1$d</xliff:g> fichiers ?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="one">Supprimer <xliff:g id="COUNT_1">%1$d</xliff:g> dossier et son contenu ?</item>
+ <item quantity="other">Supprimer <xliff:g id="COUNT_1">%1$d</xliff:g> dossiers et leur contenu ?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="one">Supprimer <xliff:g id="COUNT_1">%1$d</xliff:g> élément ?</item>
+ <item quantity="other">Supprimer <xliff:g id="COUNT_1">%1$d</xliff:g> éléments ?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-gl-rES/strings.xml b/packages/DocumentsUI/res/values-gl-rES/strings.xml
index 58181f8..5797a96 100644
--- a/packages/DocumentsUI/res/values-gl-rES/strings.xml
+++ b/packages/DocumentsUI/res/values-gl-rES/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Non se puido cambiar o nome do documento"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Convertéronse algúns ficheiros"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Queres outorgar acceso a <xliff:g id="APPNAME"><b>^1</b></xliff:g> ao directorio <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> no almacenamento de <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Queres darlle acceso a <xliff:g id="APPNAME"><b>^1</b></xliff:g> ao directorio <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Queres darlle acceso a <xliff:g id="APPNAME"><b>^1</b></xliff:g> aos teus datos almacenados en <xliff:g id="STORAGE"><i>^2</i></xliff:g>, incluídos vídeos e fotos?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Non preguntar de novo"</string>
<string name="allow" msgid="7225948811296386551">"Permitir"</string>
<string name="deny" msgid="2081879885755434506">"Rexeitar"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Queres eliminar \"<xliff:g id="NAME">%1$s</xliff:g>\"?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Queres eliminar o cartafol \"<xliff:g id="NAME">%1$s</xliff:g>\" e o seu contido?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other">Queres eliminar <xliff:g id="COUNT_1">%1$d</xliff:g> ficheiros?</item>
+ <item quantity="one">Queres eliminar <xliff:g id="COUNT_0">%1$d</xliff:g> ficheiro?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other">Queres eliminar <xliff:g id="COUNT_1">%1$d</xliff:g> cartafoles e os seus contidos?</item>
+ <item quantity="one">Queres eliminar <xliff:g id="COUNT_0">%1$d</xliff:g> cartafol e os seus contidos?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other">Queres eliminar <xliff:g id="COUNT_1">%1$d</xliff:g> elementos?</item>
+ <item quantity="one">Queres eliminar <xliff:g id="COUNT_0">%1$d</xliff:g> elemento?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-gu-rIN/strings.xml b/packages/DocumentsUI/res/values-gu-rIN/strings.xml
index 2cce5a3..48e43c4 100644
--- a/packages/DocumentsUI/res/values-gu-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-gu-rIN/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"દસ્તાવેજનું નામ બદલવામાં નિષ્ફળ થયાં"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"કેટલીક ફાઇલો રૂપાંતરિત કરી હતી"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> ને <xliff:g id="STORAGE"><i>^3</i></xliff:g> પર <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> નિર્દેશિકાની ઍક્સેસ આપીએ?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> ને <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> નિર્દેશિકાની ઍક્સેસ આપીએ?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> ને <xliff:g id="STORAGE"><i>^2</i></xliff:g> પર ફોટા અને વિડિઓઝ સહિત તમારા ડેટાની અૅક્સેસ આપીએ?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"ફરીથી પૂછશો નહીં"</string>
<string name="allow" msgid="7225948811296386551">"મંજૂરી આપો"</string>
<string name="deny" msgid="2081879885755434506">"નકારો"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"\"<xliff:g id="NAME">%1$s</xliff:g>\" ને કાઢી નાખીએ?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"\"<xliff:g id="NAME">%1$s</xliff:g>\" ફોલ્ડર અને તેની સામગ્રીઓને કાઢી નાખીએ?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ફાઇલ કાઢી નાખીએ?</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ફાઇલ કાઢી નાખીએ?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ફોલ્ડર અને તેમની સામગ્રીઓ કાઢી નાખીએ?</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ફોલ્ડર અને તેમની સામગ્રીઓ કાઢી નાખીએ?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> આઇટમ કાઢી નાખીએ?</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> આઇટમ કાઢી નાખીએ?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-hi/strings.xml b/packages/DocumentsUI/res/values-hi/strings.xml
index 9f57f0b..fa82ee8 100644
--- a/packages/DocumentsUI/res/values-hi/strings.xml
+++ b/packages/DocumentsUI/res/values-hi/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"दस्तावेज़ का नाम बदलना विफल रहा"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"कुछ फ़ाइलें रूपांतरित हो गई थीं"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> को <xliff:g id="STORAGE"><i>^3</i></xliff:g> पर <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> निर्देशिका का एक्सेस दें?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> को <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> निर्देशिका का एक्सेस प्रदान करें?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> को <xliff:g id="STORAGE"><i>^2</i></xliff:g> पर मौजूद फ़ोटो और वीडियो सहित, अपने डेटा का एक्सेस प्रदान करें?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"फिर से ना पूछें"</string>
<string name="allow" msgid="7225948811296386551">"अनुमति दें"</string>
<string name="deny" msgid="2081879885755434506">"अस्वीकारें"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"\"<xliff:g id="NAME">%1$s</xliff:g>\" को हटाएं?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"\"<xliff:g id="NAME">%1$s</xliff:g>\" फ़ोल्डर और उसकी सामग्रियां हटाएं?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> फ़ाइलें हटाएं?</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> फ़ाइलें हटाएं?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> फ़ोल्डर और उनकी सामग्री हटाएं?</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> फ़ोल्डर और उनकी सामग्री हटाएं?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> आइटम हटाएं?</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> आइटम हटाएं?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-hr/strings.xml b/packages/DocumentsUI/res/values-hr/strings.xml
index 0d42d5d..7988c71 100644
--- a/packages/DocumentsUI/res/values-hr/strings.xml
+++ b/packages/DocumentsUI/res/values-hr/strings.xml
@@ -118,6 +118,8 @@
<string name="rename_error" msgid="4203041674883412606">"Naziv dokumenta nije promijenjen"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Neke su datoteke konvertirane"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Želite li aplikaciji <xliff:g id="APPNAME"><b>^1</b></xliff:g> odobriti pristup direktoriju <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> na pohrani <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Želite li aplikaciji <xliff:g id="APPNAME"><b>^1</b></xliff:g> odobriti da pristupa direktoriju <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Želite li aplikaciji <xliff:g id="APPNAME"><b>^1</b></xliff:g> dopustiti pristup podacima, uključujući fotografije i videozapise na vanjskoj pohrani (<xliff:g id="STORAGE"><i>^2</i></xliff:g>)?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Više me ne pitaj"</string>
<string name="allow" msgid="7225948811296386551">"Dopusti"</string>
<string name="deny" msgid="2081879885755434506">"Odbij"</string>
@@ -128,7 +130,19 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Želite li izbrisati datoteku \"<xliff:g id="NAME">%1$s</xliff:g>\"?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Želite li izbrisati mapu \"<xliff:g id="NAME">%1$s</xliff:g>\" i njezin sadržaj?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="one">Želite li izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> datoteku?</item>
+ <item quantity="few">Želite li izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> datoteke?</item>
+ <item quantity="other">Želite li izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> datoteka?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="one">Želite li izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> mapu i njihov sadržaj?</item>
+ <item quantity="few">Želite li izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> mape i njihov sadržaj?</item>
+ <item quantity="other">Želite li izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> mapa i njihov sadržaj?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="one">Želite li izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> stavku?</item>
+ <item quantity="few">Želite li izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> stavke?</item>
+ <item quantity="other">Želite li izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> stavki?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-hu/strings.xml b/packages/DocumentsUI/res/values-hu/strings.xml
index 37bfcfa..fb666b5 100644
--- a/packages/DocumentsUI/res/values-hu/strings.xml
+++ b/packages/DocumentsUI/res/values-hu/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Nem sikerült átnevezni a dokumentumot"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Egyes fájlokat konvertált a rendszer"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Hozzáférést biztosít a(z) <xliff:g id="APPNAME"><b>^1</b></xliff:g> számára a(z) <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> könyvtárhoz itt: <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Hozzáférést biztosít a(z) <xliff:g id="APPNAME"><b>^1</b></xliff:g> alkalmazásnak a(z) <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> könyvtárhoz?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Hozzáférést biztosít a(z) <xliff:g id="APPNAME"><b>^1</b></xliff:g> számára az Ön adataihoz, beleértve a következő tárhelyen található képekhez és videókhoz: <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Ne jelenjen meg többé"</string>
<string name="allow" msgid="7225948811296386551">"Engedélyezés"</string>
<string name="deny" msgid="2081879885755434506">"Elutasítás"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Törli a következőt: „<xliff:g id="NAME">%1$s</xliff:g>”?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Törli „<xliff:g id="NAME">%1$s</xliff:g>” mappát a tartalmával együtt?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other">Töröl <xliff:g id="COUNT_1">%1$d</xliff:g> fájlt?</item>
+ <item quantity="one">Töröl <xliff:g id="COUNT_0">%1$d</xliff:g> fájlt?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other">Töröl <xliff:g id="COUNT_1">%1$d</xliff:g> mappát a tartalmukkal együtt?</item>
+ <item quantity="one">Töröl <xliff:g id="COUNT_0">%1$d</xliff:g> mappát a tartalmával együtt?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other">Töröl <xliff:g id="COUNT_1">%1$d</xliff:g> elemet?</item>
+ <item quantity="one">Töröl <xliff:g id="COUNT_0">%1$d</xliff:g> elemet?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-hy-rAM/strings.xml b/packages/DocumentsUI/res/values-hy-rAM/strings.xml
index 22e44b0f..f6c3ad5 100644
--- a/packages/DocumentsUI/res/values-hy-rAM/strings.xml
+++ b/packages/DocumentsUI/res/values-hy-rAM/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Չհաջողվեց վերանվանել փաստաթուղթը"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Որոշ ֆայլեր փոխարկվել են"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> հավելվածին տրամադրե՞լ <xliff:g id="STORAGE"><i>^3</i></xliff:g>-ի <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> գրացուցակն օգտագործելու թույլտվություն:"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> հավելվածին տրամադրե՞լ <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> գրացուցակն օգտագործելու թույլտվություն:"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> հավելվածին տրամադրե՞լ <xliff:g id="STORAGE"><i>^2</i></xliff:g>-ում պահվող ձեր տվյալները, այդ թվում նաև լուսանկարները և տեսանյութերը, օգտագործելու թույլտվություն:"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Այլևս չհարցնել"</string>
<string name="allow" msgid="7225948811296386551">"Թույլատրել"</string>
<string name="deny" msgid="2081879885755434506">"Մերժել"</string>
@@ -118,11 +120,18 @@
<item quantity="one">Ընտրված է՝ <xliff:g id="COUNT_1">%1$d</xliff:g></item>
<item quantity="other">Ընտրված է՝ <xliff:g id="COUNT_1">%1$d</xliff:g></item>
</plurals>
- <!-- no translation found for delete_filename_confirmation_message (5312817725577537488) -->
- <skip />
- <!-- no translation found for delete_foldername_confirmation_message (5885501832257285329) -->
- <skip />
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Ջնջե՞լ «<xliff:g id="NAME">%1$s</xliff:g>» ֆայլը:"</string>
+ <string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Ջնջե՞լ «<xliff:g id="NAME">%1$s</xliff:g>» պանակը՝ բովանդակության հետ մեկտեղ:"</string>
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="one">Ջնջե՞լ <xliff:g id="COUNT_1">%1$d</xliff:g> ֆայլ:</item>
+ <item quantity="other">Ջնջե՞լ <xliff:g id="COUNT_1">%1$d</xliff:g> ֆայլ:</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="one">Ջնջե՞լ <xliff:g id="COUNT_1">%1$d</xliff:g> պանակ՝ բովանդակության հետ մեկտեղ:</item>
+ <item quantity="other">Ջնջե՞լ <xliff:g id="COUNT_1">%1$d</xliff:g> պանակ՝ բովանդակության հետ մեկտեղ:</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="one">Ջնջե՞լ <xliff:g id="COUNT_1">%1$d</xliff:g> տարր:</item>
+ <item quantity="other">Ջնջե՞լ <xliff:g id="COUNT_1">%1$d</xliff:g> տարր:</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-in/strings.xml b/packages/DocumentsUI/res/values-in/strings.xml
index f9aae9d..a8aee52 100644
--- a/packages/DocumentsUI/res/values-in/strings.xml
+++ b/packages/DocumentsUI/res/values-in/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Gagal mengganti nama dokumen"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Beberapa file dikonversi"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Beri <xliff:g id="APPNAME"><b>^1</b></xliff:g> akses ke direktori <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> di <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Beri <xliff:g id="APPNAME"><b>^1</b></xliff:g> akses ke direktori <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Beri <xliff:g id="APPNAME"><b>^1</b></xliff:g> akses ke data Anda, termasuk foto dan video, di <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Jangan tanya lagi"</string>
<string name="allow" msgid="7225948811296386551">"Izinkan"</string>
<string name="deny" msgid="2081879885755434506">"Tolak"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Hapus \"<xliff:g id="NAME">%1$s</xliff:g>\"?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Hapus folder \"<xliff:g id="NAME">%1$s</xliff:g>\" dan kontennya?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other">Hapus <xliff:g id="COUNT_1">%1$d</xliff:g> file?</item>
+ <item quantity="one">Hapus <xliff:g id="COUNT_0">%1$d</xliff:g> file?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other">Hapus <xliff:g id="COUNT_1">%1$d</xliff:g> folder dan kontennya?</item>
+ <item quantity="one">Hapus <xliff:g id="COUNT_0">%1$d</xliff:g> folder dan kontennya?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other">Hapus <xliff:g id="COUNT_1">%1$d</xliff:g> item?</item>
+ <item quantity="one">Hapus <xliff:g id="COUNT_0">%1$d</xliff:g> item?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-is-rIS/strings.xml b/packages/DocumentsUI/res/values-is-rIS/strings.xml
index a2a0d9b..88aaced 100644
--- a/packages/DocumentsUI/res/values-is-rIS/strings.xml
+++ b/packages/DocumentsUI/res/values-is-rIS/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Ekki tókst að endurnefna skjalið"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Sumum skrám var umbreytt"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Veita <xliff:g id="APPNAME"><b>^1</b></xliff:g> aðgang að skráasafninu <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> á <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Viltu veita <xliff:g id="APPNAME"><b>^1</b></xliff:g> aðgang að skráasafninu <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Veita <xliff:g id="APPNAME"><b>^1</b></xliff:g> aðgang að gögnunum þínum, þar á meðal myndum og myndskeiðum, á <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Ekki spyrja aftur"</string>
<string name="allow" msgid="7225948811296386551">"Leyfa"</string>
<string name="deny" msgid="2081879885755434506">"Hafna"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Eyða „<xliff:g id="NAME">%1$s</xliff:g>“?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Eyða möppunni „<xliff:g id="NAME">%1$s</xliff:g>“ og öllu innihaldi hennar?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="one">Eyða <xliff:g id="COUNT_1">%1$d</xliff:g> skrá?</item>
+ <item quantity="other">Eyða <xliff:g id="COUNT_1">%1$d</xliff:g> skrám?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="one">Eyða <xliff:g id="COUNT_1">%1$d</xliff:g> möppu og innihaldi þeirra?</item>
+ <item quantity="other">Eyða <xliff:g id="COUNT_1">%1$d</xliff:g> möppum og innihaldi þeirra?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="one">Eyða <xliff:g id="COUNT_1">%1$d</xliff:g> atriði?</item>
+ <item quantity="other">Eyða <xliff:g id="COUNT_1">%1$d</xliff:g> atriðum?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-it/strings.xml b/packages/DocumentsUI/res/values-it/strings.xml
index ddede45..b7497db 100644
--- a/packages/DocumentsUI/res/values-it/strings.xml
+++ b/packages/DocumentsUI/res/values-it/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Ridenominazione documento non riuscita"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Alcuni file sono stati convertiti"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Concedere all\'app <xliff:g id="APPNAME"><b>^1</b></xliff:g> l\'accesso alla directory <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> su <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Concedere all\'app <xliff:g id="APPNAME"><b>^1</b></xliff:g> l\'accesso alla directory <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Concedere all\'app <xliff:g id="APPNAME"><b>^1</b></xliff:g> l\'accesso ai tuoi dati, inclusi video e foto, sull\'unità <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Non chiedermelo più"</string>
<string name="allow" msgid="7225948811296386551">"Consenti"</string>
<string name="deny" msgid="2081879885755434506">"Nega"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Eliminare \"<xliff:g id="NAME">%1$s</xliff:g>\"?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Eliminare la cartella \"<xliff:g id="NAME">%1$s</xliff:g>\" e i relativi contenuti?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other">Eliminare <xliff:g id="COUNT_1">%1$d</xliff:g> file?</item>
+ <item quantity="one">Eliminare <xliff:g id="COUNT_0">%1$d</xliff:g> file?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other">Eliminare <xliff:g id="COUNT_1">%1$d</xliff:g> cartelle e i relativi contenuti?</item>
+ <item quantity="one">Eliminare <xliff:g id="COUNT_0">%1$d</xliff:g> cartella e i relativi contenuti?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other">Eliminare <xliff:g id="COUNT_1">%1$d</xliff:g> elementi?</item>
+ <item quantity="one">Eliminare <xliff:g id="COUNT_0">%1$d</xliff:g> elemento?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-iw/strings.xml b/packages/DocumentsUI/res/values-iw/strings.xml
index e2658c3..4498f8c 100644
--- a/packages/DocumentsUI/res/values-iw/strings.xml
+++ b/packages/DocumentsUI/res/values-iw/strings.xml
@@ -125,6 +125,8 @@
<string name="rename_error" msgid="4203041674883412606">"ניסיון שינוי שם המסמך נכשל"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"קבצים מסוימים הומרו"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"האם להעניק לאפליקציה <xliff:g id="APPNAME"><b>^1</b></xliff:g> גישה לספריה <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> באחסון <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"האם להעניק לאפליקציה <xliff:g id="APPNAME"><b>^1</b></xliff:g> גישה אל ספריית <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"האם להעניק לאפליקציה <xliff:g id="APPNAME"><b>^1</b></xliff:g> גישה לנתונים שלך, כולל תמונות וסרטונים, השמורים ב<xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"אל תשאל שוב"</string>
<string name="allow" msgid="7225948811296386551">"אפשר"</string>
<string name="deny" msgid="2081879885755434506">"דחה"</string>
@@ -134,11 +136,24 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> נבחרו</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> נבחר</item>
</plurals>
- <!-- no translation found for delete_filename_confirmation_message (5312817725577537488) -->
- <skip />
- <!-- no translation found for delete_foldername_confirmation_message (5885501832257285329) -->
- <skip />
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <string name="delete_filename_confirmation_message" msgid="5312817725577537488">"האם למחוק את \"<xliff:g id="NAME">%1$s</xliff:g>\"?"</string>
+ <string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"האם למחוק את התיקייה \"<xliff:g id="NAME">%1$s</xliff:g>\" ואת התוכן שלה?"</string>
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="two">האם למחוק <xliff:g id="COUNT_1">%1$d</xliff:g> קבצים?</item>
+ <item quantity="many">האם למחוק <xliff:g id="COUNT_1">%1$d</xliff:g> קבצים?</item>
+ <item quantity="other">האם למחוק <xliff:g id="COUNT_1">%1$d</xliff:g> קבצים?</item>
+ <item quantity="one">האם למחוק <xliff:g id="COUNT_0">%1$d</xliff:g> קובץ?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="two">האם למחוק <xliff:g id="COUNT_1">%1$d</xliff:g> תיקיות ואת התוכן שלהן?</item>
+ <item quantity="many">האם למחוק <xliff:g id="COUNT_1">%1$d</xliff:g> תיקיות ואת התוכן שלהן?</item>
+ <item quantity="other">האם למחוק <xliff:g id="COUNT_1">%1$d</xliff:g> תיקיות ואת התוכן שלהן?</item>
+ <item quantity="one">האם למחוק <xliff:g id="COUNT_0">%1$d</xliff:g> תיקייה ואת התוכן שלה?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="two">האם למחוק <xliff:g id="COUNT_1">%1$d</xliff:g> פריטים?</item>
+ <item quantity="many">האם למחוק <xliff:g id="COUNT_1">%1$d</xliff:g> פריטים?</item>
+ <item quantity="other">האם למחוק <xliff:g id="COUNT_1">%1$d</xliff:g> פריטים?</item>
+ <item quantity="one">האם למחוק <xliff:g id="COUNT_0">%1$d</xliff:g> פריט?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-ja/strings.xml b/packages/DocumentsUI/res/values-ja/strings.xml
index 3cad318..bfb1c3a 100644
--- a/packages/DocumentsUI/res/values-ja/strings.xml
+++ b/packages/DocumentsUI/res/values-ja/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"ドキュメントの名前を変更できませんでした"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"一部のファイルが変換されました"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"「<xliff:g id="STORAGE"><i>^3</i></xliff:g>」の「<xliff:g id="DIRECTORY"><i>^2</i></xliff:g>」ディレクトリに「<xliff:g id="APPNAME"><b>^1</b></xliff:g>」へのアクセスを許可しますか?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"「<xliff:g id="APPNAME"><b>^1</b></xliff:g>」アプリに「<xliff:g id="DIRECTORY"><i>^2</i></xliff:g>」ディレクトリへのアクセスを許可しますか?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="STORAGE"><i>^2</i></xliff:g>の写真や動画などのデータへのアクセスを「<xliff:g id="APPNAME"><b>^1</b></xliff:g>」に許可しますか?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"今後表示しない"</string>
<string name="allow" msgid="7225948811296386551">"許可"</string>
<string name="deny" msgid="2081879885755434506">"拒否"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"「<xliff:g id="NAME">%1$s</xliff:g>」を削除しますか?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"フォルダ「<xliff:g id="NAME">%1$s</xliff:g>」とそのコンテンツを削除しますか?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> 個のファイルを削除しますか?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> 個のファイルを削除しますか?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> 個のフォルダとそのコンテンツを削除しますか?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> 個のフォルダとそのコンテンツを削除しますか?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> 個の項目を削除しますか?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> 個の項目を削除しますか?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-ka-rGE/strings.xml b/packages/DocumentsUI/res/values-ka-rGE/strings.xml
index afd4198..f0e9e86 100644
--- a/packages/DocumentsUI/res/values-ka-rGE/strings.xml
+++ b/packages/DocumentsUI/res/values-ka-rGE/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"დოკუმენტის გადარქმევა ვერ მოხერხდა"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"ზოგიერთი ფაილი გარდაქმნილია"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"გსურთ, <xliff:g id="APPNAME"><b>^1</b></xliff:g> სარგებლობდეს <xliff:g id="STORAGE"><i>^3</i></xliff:g>-ის დირექტორიაზე „<xliff:g id="DIRECTORY"><i>^2</i></xliff:g>“ წვდომის უფლებით?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"გსურთ, <xliff:g id="APPNAME"><b>^1</b></xliff:g> სარგებლობდეს დირექტორიაზე „<xliff:g id="DIRECTORY"><i>^2</i></xliff:g>“ წვდომის უფლებით?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"გსურთ, <xliff:g id="APPNAME"><b>^1</b></xliff:g> სარგებლობდეს <xliff:g id="STORAGE"><i>^2</i></xliff:g>-ზე არსებულ მონაცემებზე, მათ შორის, ფოტოებსა და ვიდეოებზე, წვდომის უფლებით?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"აღარ მკითხოთ"</string>
<string name="allow" msgid="7225948811296386551">"უფლების მიცემა"</string>
<string name="deny" msgid="2081879885755434506">"აკრძალვა"</string>
@@ -118,11 +120,18 @@
<item quantity="other">არჩეულია <xliff:g id="COUNT_1">%1$d</xliff:g></item>
<item quantity="one">არჩეულია <xliff:g id="COUNT_0">%1$d</xliff:g></item>
</plurals>
- <!-- no translation found for delete_filename_confirmation_message (5312817725577537488) -->
- <skip />
- <!-- no translation found for delete_foldername_confirmation_message (5885501832257285329) -->
- <skip />
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <string name="delete_filename_confirmation_message" msgid="5312817725577537488">"გსურთ, წაშალოთ „<xliff:g id="NAME">%1$s</xliff:g>“?"</string>
+ <string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"გსურთ, წაშალოთ საქაღალდე „<xliff:g id="NAME">%1$s</xliff:g>“ და მისი შიგთავსი?"</string>
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other">გსურთ <xliff:g id="COUNT_1">%1$d</xliff:g> ფაილის წაშლა?</item>
+ <item quantity="one">გსურთ <xliff:g id="COUNT_0">%1$d</xliff:g> ფაილის წაშლა?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other">გსურთ <xliff:g id="COUNT_1">%1$d</xliff:g> საქაღალდისა და მათი შიგთავსის წაშლა?</item>
+ <item quantity="one">გსურთ <xliff:g id="COUNT_0">%1$d</xliff:g> საქაღალდისა და მისი შიგთავსის წაშლა?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other">გსურთ <xliff:g id="COUNT_1">%1$d</xliff:g> ერთეულის წაშლა?</item>
+ <item quantity="one">გსურთ <xliff:g id="COUNT_0">%1$d</xliff:g> ერთეულის წაშლა?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-kk-rKZ/strings.xml b/packages/DocumentsUI/res/values-kk-rKZ/strings.xml
index 21a8260..2687900 100644
--- a/packages/DocumentsUI/res/values-kk-rKZ/strings.xml
+++ b/packages/DocumentsUI/res/values-kk-rKZ/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Құжат қайта аталмады"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Кейбір файлдар түрлендірілді"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> қолданбасына <xliff:g id="STORAGE"><i>^3</i></xliff:g> қоймасындағы <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> каталогына өтуге рұқсат беру керек пе?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> қолданбасына <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> каталогына қатынас беру керек пе?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> <xliff:g id="STORAGE"><i>^2</i></xliff:g> қоймасындағы деректерге, соның ішінде фотосуреттерге және бейнелерге кіру мүмкіндігін беру керек пе?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Қайта сұралмасын"</string>
<string name="allow" msgid="7225948811296386551">"Рұқсат беру"</string>
<string name="deny" msgid="2081879885755434506">"Бас тарту"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"\"<xliff:g id="NAME">%1$s</xliff:g>\" жою керек пе?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"\"<xliff:g id="NAME">%1$s</xliff:g>\" қалтасын және оның мазмұнын жою керек пе?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> файлды жою керек пе?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> файлды жою керек пе?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> қалтаны ішіндегісімен бірге жою керек пе?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> қалтаны ішіндегісімен бірге жою керек пе?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> элементті жою керек пе?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> элементті жою керек пе?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-km-rKH/strings.xml b/packages/DocumentsUI/res/values-km-rKH/strings.xml
index 5a049add..ea24043 100644
--- a/packages/DocumentsUI/res/values-km-rKH/strings.xml
+++ b/packages/DocumentsUI/res/values-km-rKH/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"បានបរាជ័យក្នុងការប្តូរឈ្មោះឯកសារ"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"ឯកសារមួយចំនួនត្រូវបានបម្លែង"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"ផ្តល់សិទ្ធិឲ្យ <xliff:g id="APPNAME"><b>^1</b></xliff:g> ចូលដំណើរការថត <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> នៅលើ <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"ផ្តល់សិទ្ធិឲ្យ <xliff:g id="APPNAME"><b>^1</b></xliff:g> ចូលដំណើរការថត <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> ឬ?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"ផ្តល់សិទ្ធិអនុញ្ញាតដល់ <xliff:g id="APPNAME"><b>^1</b></xliff:g> ដើម្បីចូលដំណើរការទិន្នន័យរបស់អ្នក រាប់បញ្ចូលទាំងរូបថត និងវីដេអូ នៅលើ <xliff:g id="STORAGE"><i>^2</i></xliff:g> ឬទេ?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"កុំសួរទៀត"</string>
<string name="allow" msgid="7225948811296386551">"អនុញ្ញាត"</string>
<string name="deny" msgid="2081879885755434506">"បដិសេធ"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"លុប \"<xliff:g id="NAME">%1$s</xliff:g>\" ឬ?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"លុបថតឯកសារ \"<xliff:g id="NAME">%1$s</xliff:g>\" និងមាតិការបស់វាឬ?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other">លុបឯកសារ <xliff:g id="COUNT_1">%1$d</xliff:g> ច្បាប់ឬ?</item>
+ <item quantity="one">លុបឯកសារ <xliff:g id="COUNT_0">%1$d</xliff:g> ច្បាប់ឬ?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other">លុបថតឯកសារ <xliff:g id="COUNT_1">%1$d</xliff:g> និងមាតិការបស់វាឬ?</item>
+ <item quantity="one">លុបថតឯកសារ <xliff:g id="COUNT_0">%1$d</xliff:g> និងមាតិការបស់វាឬ?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other">លុបធាតុ <xliff:g id="COUNT_1">%1$d</xliff:g> ឬ?</item>
+ <item quantity="one">លុបធាតុ <xliff:g id="COUNT_0">%1$d</xliff:g> ឬ?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-kn-rIN/strings.xml b/packages/DocumentsUI/res/values-kn-rIN/strings.xml
index 924b14a..c756009 100644
--- a/packages/DocumentsUI/res/values-kn-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-kn-rIN/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"ಡಾಕ್ಯುಮೆಂಟ್ ಮರುಹೆಸರಿಸಲು ವಿಫಲವಾಗಿದೆ"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"ಕೆಲವು ಫೈಲ್ಗಳನ್ನು ಪರಿವರ್ತಿಸಲಾಗಿದೆ"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="STORAGE"><i>^3</i></xliff:g> ರಲ್ಲಿ <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> ಡೈರೆಕ್ಟರಿಗೆ <xliff:g id="APPNAME"><b>^1</b></xliff:g> ಪ್ರವೇಶ ನೀಡುವುದೇ?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"<xliff:g id="APPNAME"><b>^1</b></xliff:g><xliff:g id="DIRECTORY"><i>^2</i></xliff:g> ಡೈರೆಕ್ಟರಿ ಪ್ರವೇಶಿಸಲು ಅನುಮತಿಸುವುದೇ?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="STORAGE"><i>^2</i></xliff:g> ಸಂಗ್ರಹಣೆಯಲ್ಲಿನ ಪೋಟೋಗಳು ಮತ್ತು ವೀಡಿಯೊಗಳು ಸೇರಿದಂತೆ ನಿಮ್ಮ ಡೇಟಾವನ್ನು ಪ್ರವೇಶಿಸಲು <xliff:g id="APPNAME"><b>^1</b></xliff:g> ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುವುದೇ?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"ಮತ್ತೆ ಕೇಳಬೇಡಿ"</string>
<string name="allow" msgid="7225948811296386551">"ಅನುಮತಿಸು"</string>
<string name="deny" msgid="2081879885755434506">"ನಿರಾಕರಿಸು"</string>
@@ -118,11 +120,18 @@
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ಆಯ್ಕೆಮಾಡಲಾಗಿದೆ</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ಆಯ್ಕೆಮಾಡಲಾಗಿದೆ</item>
</plurals>
- <!-- no translation found for delete_filename_confirmation_message (5312817725577537488) -->
- <skip />
- <!-- no translation found for delete_foldername_confirmation_message (5885501832257285329) -->
- <skip />
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <string name="delete_filename_confirmation_message" msgid="5312817725577537488">"\"<xliff:g id="NAME">%1$s</xliff:g>\" ಅಳಿಸುವುದೇ?"</string>
+ <string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"\"<xliff:g id="NAME">%1$s</xliff:g>\" ಫೋಲ್ಡರ್ ಮತ್ತು ಅದರ ವಿಷಯಗಳನ್ನು ಅಳಿಸುವುದೇ?"</string>
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="one"> <xliff:g id="COUNT_1">%1$d</xliff:g> ಫೈಲ್ಗಳನ್ನು ಅಳಿಸುವುದೇ?</item>
+ <item quantity="other"> <xliff:g id="COUNT_1">%1$d</xliff:g> ಫೈಲ್ಗಳನ್ನು ಅಳಿಸುವುದೇ?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="one"> <xliff:g id="COUNT_1">%1$d</xliff:g> ಫೋಲ್ಡರ್ಗಳು ಮತ್ತು ಅವುಗಳ ವಿಷಯಗಳನ್ನು ಅಳಿಸುವುದೇ?</item>
+ <item quantity="other"> <xliff:g id="COUNT_1">%1$d</xliff:g> ಫೋಲ್ಡರ್ಗಳು ಮತ್ತು ಅವುಗಳ ವಿಷಯಗಳನ್ನು ಅಳಿಸುವುದೇ?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="one"> <xliff:g id="COUNT_1">%1$d</xliff:g> ಐಟಂಗಳನ್ನು ಅಳಿಸುವುದೇ?</item>
+ <item quantity="other"> <xliff:g id="COUNT_1">%1$d</xliff:g> ಐಟಂಗಳನ್ನು ಅಳಿಸುವುದೇ?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-ko/strings.xml b/packages/DocumentsUI/res/values-ko/strings.xml
index df0cbf2..4d5dcf9 100644
--- a/packages/DocumentsUI/res/values-ko/strings.xml
+++ b/packages/DocumentsUI/res/values-ko/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"문서 이름을 변경하지 못했습니다."</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"일부 파일이 변환되었습니다."</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g>이(가) <xliff:g id="STORAGE"><i>^3</i></xliff:g>에서 <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> 디렉토리에 액세스하도록 허용하시겠습니까?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"<xliff:g id="APPNAME"><b>^1</b></xliff:g>이(가) <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> 디렉토리에 액세스하도록 허용하시겠습니까?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="APPNAME"><b>^1</b></xliff:g>에서 사진, 동영상 등 <xliff:g id="STORAGE"><i>^2</i></xliff:g>의 내 데이터에 액세스하도록 허용하시겠습니까?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"다시 묻지 않음"</string>
<string name="allow" msgid="7225948811296386551">"허용"</string>
<string name="deny" msgid="2081879885755434506">"거부"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"<xliff:g id="NAME">%1$s</xliff:g>을(를) 삭제하시겠습니까?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"\'<xliff:g id="NAME">%1$s</xliff:g>\' 폴더와 폴더에 포함된 콘텐츠를 삭제하시겠습니까?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other">파일 <xliff:g id="COUNT_1">%1$d</xliff:g>개를 삭제하시겠습니까?</item>
+ <item quantity="one">파일 <xliff:g id="COUNT_0">%1$d</xliff:g>개를 삭제하시겠습니까?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other">폴더 <xliff:g id="COUNT_1">%1$d</xliff:g>개와 폴더에 포함된 콘텐츠를 삭제하시겠습니까?</item>
+ <item quantity="one">폴더 <xliff:g id="COUNT_0">%1$d</xliff:g>개와 폴더에 포함된 콘텐츠를 삭제하시겠습니까?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other">항목 <xliff:g id="COUNT_1">%1$d</xliff:g>개를 삭제하시겠습니까?</item>
+ <item quantity="one">항목 <xliff:g id="COUNT_0">%1$d</xliff:g>개를 삭제하시겠습니까?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-ky-rKG/strings.xml b/packages/DocumentsUI/res/values-ky-rKG/strings.xml
index 84bc37f..1b39039 100644
--- a/packages/DocumentsUI/res/values-ky-rKG/strings.xml
+++ b/packages/DocumentsUI/res/values-ky-rKG/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Документтин аталышы өзгөртүлбөй калды"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Айрым файлдардын форматы өзгөртүлдү"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> колдонмосуна <xliff:g id="STORAGE"><i>^3</i></xliff:g> түзмөгүндөгү <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> папканы пайдалануу мүмкүнчүлүгү берилсинби?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> колдонмосуна <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> каталогун пайдалануу мүмкүнчүлүгү берилсинби?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> колдонмосуна <xliff:g id="STORAGE"><i>^2</i></xliff:g> түзмөгүндөгү дайындарыңыз, сүрөттөрүңүз жана видеолоруңузду пайдалануу мүмкүнчүлүгү берилсинби?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Экинчи суралбасын"</string>
<string name="allow" msgid="7225948811296386551">"Уруксат берүү"</string>
<string name="deny" msgid="2081879885755434506">"Жок"</string>
@@ -118,11 +120,18 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> тандалды</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> тандалды</item>
</plurals>
- <!-- no translation found for delete_filename_confirmation_message (5312817725577537488) -->
- <skip />
- <!-- no translation found for delete_foldername_confirmation_message (5885501832257285329) -->
- <skip />
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <string name="delete_filename_confirmation_message" msgid="5312817725577537488">"\"<xliff:g id="NAME">%1$s</xliff:g>\" жок кылынсынбы?"</string>
+ <string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"\"<xliff:g id="NAME">%1$s</xliff:g>\" куржуну мазмуну менен жок кылынсынбы?"</string>
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> файл жок кылынсынбы?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> файл жок кылынсынбы?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> куржун мазмуну менен жок кылынсынбы?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> куржун мазмуну менен жок кылынсынбы?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> нерсе жок кылынсынбы?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> нерсе жок кылынсынбы?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-lo-rLA/strings.xml b/packages/DocumentsUI/res/values-lo-rLA/strings.xml
index 4c891aa..a4f381d 100644
--- a/packages/DocumentsUI/res/values-lo-rLA/strings.xml
+++ b/packages/DocumentsUI/res/values-lo-rLA/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"ປ່ຽນຊື່ເອກະສານບໍ່ສຳເລັດ"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"ປ່ຽນແປງບາງໄຟລ໌ແລ້ວ"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"ອະນຸຍາດສິດເຂົ້າເຖິງໃຫ້ <xliff:g id="APPNAME"><b>^1</b></xliff:g> ເພື່ອເຂົ້າໄດເຣກທໍຣີ <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> ຢູ່ <xliff:g id="STORAGE"><i>^3</i></xliff:g> ບໍ?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"ອະນຸມັດ <xliff:g id="APPNAME"><b>^1</b></xliff:g> ໃຫ້ເຂົ້າຫາໄດເຣັກທໍຣີ <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> ບໍ?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"ອະນຸມັດໃຫ້ <xliff:g id="APPNAME"><b>^1</b></xliff:g> ເຂົ້າເຖິງຂໍ້ມູນຂອງທ່ານ ເຊິ່ງຮວມເຖິງຮູບພາບ ແລະ ວິດີໂອໃນ <xliff:g id="STORAGE"><i>^2</i></xliff:g> ໄດ້ບໍ?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"ບໍ່ຕ້ອງຖາມຄືນ"</string>
<string name="allow" msgid="7225948811296386551">"ອະນຸຍາດ"</string>
<string name="deny" msgid="2081879885755434506">"ປະຕິເສດ"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"ລຶບ \"<xliff:g id="NAME">%1$s</xliff:g>\" ອອກບໍ?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"ລຶບໂຟນເດີ \"<xliff:g id="NAME">%1$s</xliff:g>\" ແລະ ເນື້ອຫາທັງໝົດຂອງມັນອອກບໍ?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other">ລຶບ <xliff:g id="COUNT_1">%1$d</xliff:g> ໄຟລ໌ອອກບໍ?</item>
+ <item quantity="one">ລຶບ <xliff:g id="COUNT_0">%1$d</xliff:g> ໄຟລ໌ອອກບໍ?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other">ລຶບ <xliff:g id="COUNT_1">%1$d</xliff:g> ໂຟນເດີ ແລະ ເນື້ອຫາຂອງມັນອອກບໍ?</item>
+ <item quantity="one">ລຶບ <xliff:g id="COUNT_0">%1$d</xliff:g> ໂຟນເດີ ແລະ ເນື້ອຫາຂອງມັນອອກບໍ?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other">ລຶບ <xliff:g id="COUNT_1">%1$d</xliff:g> ລາຍການອອກບໍ?</item>
+ <item quantity="one">ລຶບ <xliff:g id="COUNT_0">%1$d</xliff:g> ລາຍການອອກບໍ?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-lt/strings.xml b/packages/DocumentsUI/res/values-lt/strings.xml
index 8aec2d4..2e0df93 100644
--- a/packages/DocumentsUI/res/values-lt/strings.xml
+++ b/packages/DocumentsUI/res/values-lt/strings.xml
@@ -125,6 +125,8 @@
<string name="rename_error" msgid="4203041674883412606">"Nepavyko pervardyti dokumento"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Kai kurie failai buvo konvertuoti"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Suteikti „<xliff:g id="APPNAME"><b>^1</b></xliff:g>“ prieigą prie katalogo „<xliff:g id="DIRECTORY"><i>^2</i></xliff:g>“ <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Suteikti „<xliff:g id="APPNAME"><b>^1</b></xliff:g>“ prieigą prie katalogo „<xliff:g id="DIRECTORY"><i>^2</i></xliff:g>“?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Suteikti programai „<xliff:g id="APPNAME"><b>^1</b></xliff:g>“ prieigą prie duomenų, įskaitant nuotraukas ir vaizdo įrašus, <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Daugiau neklausti"</string>
<string name="allow" msgid="7225948811296386551">"Leisti"</string>
<string name="deny" msgid="2081879885755434506">"Atmesti"</string>
@@ -136,7 +138,22 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Ištrinti „<xliff:g id="NAME">%1$s</xliff:g>“?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Ištrinti aplanką „<xliff:g id="NAME">%1$s</xliff:g>“ ir jo turinį?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="one">Ištrinti <xliff:g id="COUNT_1">%1$d</xliff:g> failą?</item>
+ <item quantity="few">Ištrinti <xliff:g id="COUNT_1">%1$d</xliff:g> failus?</item>
+ <item quantity="many">Ištrinti <xliff:g id="COUNT_1">%1$d</xliff:g> failo?</item>
+ <item quantity="other">Ištrinti <xliff:g id="COUNT_1">%1$d</xliff:g> failų?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="one">Ištrinti <xliff:g id="COUNT_1">%1$d</xliff:g> aplanką ir jų turinį?</item>
+ <item quantity="few">Ištrinti <xliff:g id="COUNT_1">%1$d</xliff:g> aplankus ir jų turinį?</item>
+ <item quantity="many">Ištrinti <xliff:g id="COUNT_1">%1$d</xliff:g> aplanko ir jų turinį?</item>
+ <item quantity="other">Ištrinti <xliff:g id="COUNT_1">%1$d</xliff:g> aplankų ir jų turinį?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="one">Ištrinti <xliff:g id="COUNT_1">%1$d</xliff:g> elementą?</item>
+ <item quantity="few">Ištrinti <xliff:g id="COUNT_1">%1$d</xliff:g> elementus?</item>
+ <item quantity="many">Ištrinti <xliff:g id="COUNT_1">%1$d</xliff:g> elemento?</item>
+ <item quantity="other">Ištrinti <xliff:g id="COUNT_1">%1$d</xliff:g> elementų?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-lv/strings.xml b/packages/DocumentsUI/res/values-lv/strings.xml
index be2f0b7..fb81aa0 100644
--- a/packages/DocumentsUI/res/values-lv/strings.xml
+++ b/packages/DocumentsUI/res/values-lv/strings.xml
@@ -118,6 +118,8 @@
<string name="rename_error" msgid="4203041674883412606">"Neizdevās pārdēvēt dokumentu"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Daži faili tika pārveidoti."</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Vai atļaut lietotnei <xliff:g id="APPNAME"><b>^1</b></xliff:g> piekļūt direktorijam <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> šajā krātuvē: <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Vai piešķirt lietotnei <xliff:g id="APPNAME"><b>^1</b></xliff:g> piekļuvi direktorijam <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Vai atļaut lietotnei <xliff:g id="APPNAME"><b>^1</b></xliff:g> piekļūt jūsu datiem, tostarp fotoattēliem un videoklipiem, šajā krātuvē: <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Turpmāk vairs nejautāt"</string>
<string name="allow" msgid="7225948811296386551">"Atļaut"</string>
<string name="deny" msgid="2081879885755434506">"Noraidīt"</string>
@@ -128,7 +130,19 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Vai izdzēst failu “<xliff:g id="NAME">%1$s</xliff:g>”?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Vai izdzēst mapi “<xliff:g id="NAME">%1$s</xliff:g>” un tās saturu?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="zero">Vai izdzēst <xliff:g id="COUNT_1">%1$d</xliff:g> failus?</item>
+ <item quantity="one">Vai izdzēst <xliff:g id="COUNT_1">%1$d</xliff:g> failu?</item>
+ <item quantity="other">Vai izdzēst <xliff:g id="COUNT_1">%1$d</xliff:g> failus?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="zero">Vai izdzēst <xliff:g id="COUNT_1">%1$d</xliff:g> mapes un to saturu?</item>
+ <item quantity="one">Vai izdzēst <xliff:g id="COUNT_1">%1$d</xliff:g> mapi un to saturu?</item>
+ <item quantity="other">Vai izdzēst <xliff:g id="COUNT_1">%1$d</xliff:g> mapes un to saturu?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="zero">Vai izdzēst <xliff:g id="COUNT_1">%1$d</xliff:g> vienumus?</item>
+ <item quantity="one">Vai izdzēst <xliff:g id="COUNT_1">%1$d</xliff:g> vienumu?</item>
+ <item quantity="other">Vai izdzēst <xliff:g id="COUNT_1">%1$d</xliff:g> vienumus?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-mk-rMK/strings.xml b/packages/DocumentsUI/res/values-mk-rMK/strings.xml
index e9c7f3f..ad428a0 100644
--- a/packages/DocumentsUI/res/values-mk-rMK/strings.xml
+++ b/packages/DocumentsUI/res/values-mk-rMK/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Не успеа да се преименува документот"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Некои датотеки беа конвертирани"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Овозможете пристап на <xliff:g id="APPNAME"><b>^1</b></xliff:g> до директориумот <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> на <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Овозможете пристап на <xliff:g id="APPNAME"><b>^1</b></xliff:g> до директориумот <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Да се овозможи пристап на <xliff:g id="APPNAME"><b>^1</b></xliff:g> до вашите податоци, вклучувајќи фотографии и видеа, на <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Не прашувај повторно"</string>
<string name="allow" msgid="7225948811296386551">"Дозволи"</string>
<string name="deny" msgid="2081879885755434506">"Одбиј"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Да се избрише „<xliff:g id="NAME">%1$s</xliff:g>“?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Да се избрише папката „<xliff:g id="NAME">%1$s</xliff:g>“ и нејзините содржини?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="one">Да се избрише <xliff:g id="COUNT_1">%1$d</xliff:g> датотека?</item>
+ <item quantity="other">Да се избришат <xliff:g id="COUNT_1">%1$d</xliff:g> датотеки?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="one">Да се избрише <xliff:g id="COUNT_1">%1$d</xliff:g> папка и нивните содржини?</item>
+ <item quantity="other">Да се избришат <xliff:g id="COUNT_1">%1$d</xliff:g> папки и нивните содржини?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="one">Да се избрише <xliff:g id="COUNT_1">%1$d</xliff:g> ставка?</item>
+ <item quantity="other">Да се избришат <xliff:g id="COUNT_1">%1$d</xliff:g> ставки?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-ml-rIN/strings.xml b/packages/DocumentsUI/res/values-ml-rIN/strings.xml
index e71e62a..5a16512 100644
--- a/packages/DocumentsUI/res/values-ml-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-ml-rIN/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"ഡോക്യുമെന്റിന്റെ പേരുമാറ്റുന്നത് പരാജയപ്പെട്ടു"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"ചില ഫയലുകൾ പരിവർത്തനം ചെയ്യപ്പെട്ടു"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="STORAGE"><i>^3</i></xliff:g> സ്റ്റോറേജിലെ <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> എന്ന ഡയറക്റ്ററിയിലേക്ക് <xliff:g id="APPNAME"><b>^1</b></xliff:g> ആപ്പിന് ആക്സസ് അനുവദിക്കണോ?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"<xliff:g id="DIRECTORY"><i>^2</i></xliff:g> എന്ന ഡയറക്ടറിയിലേക്ക് <xliff:g id="APPNAME"><b>^1</b></xliff:g> ആപ്പിന് ആക്സസ് അനുവദിക്കണോ?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="STORAGE"><i>^2</i></xliff:g> സ്റ്റോറേജിലെ ഫോട്ടോകളും വീഡിയോകളും ഉൾപ്പെടെ, നിങ്ങളുടെ ഡാറ്റയിലേക്ക് <xliff:g id="APPNAME"><b>^1</b></xliff:g> ആപ്പിന് ആക്സസ്സ് അനുവദിക്കണോ?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"വീണ്ടും ആവശ്യപ്പെടരുത്"</string>
<string name="allow" msgid="7225948811296386551">"അനുവദിക്കുക"</string>
<string name="deny" msgid="2081879885755434506">"നിരസിക്കുക"</string>
@@ -118,11 +120,18 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> തിരഞ്ഞെടുത്തു</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> തിരഞ്ഞെടുത്തു</item>
</plurals>
- <!-- no translation found for delete_filename_confirmation_message (5312817725577537488) -->
- <skip />
- <!-- no translation found for delete_foldername_confirmation_message (5885501832257285329) -->
- <skip />
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <string name="delete_filename_confirmation_message" msgid="5312817725577537488">"\"<xliff:g id="NAME">%1$s</xliff:g>\" ഇല്ലാതാക്കണോ?"</string>
+ <string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"\"<xliff:g id="NAME">%1$s</xliff:g>\" എന്ന ഫോൾഡറും അതിലെ ഉള്ളടങ്ങളും ഇല്ലാതാക്കണോ?"</string>
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ഫയലുകൾ ഇല്ലാതാക്കണോ?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ഫയൽ ഇല്ലാതാക്കണോ?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ഫോൾഡറുകളും അവയിലെ ഉള്ളടക്കങ്ങളും ഇല്ലാതാക്കണോ?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ഫോൾഡറും അതിലെ ഉള്ളടക്കങ്ങളും ഇല്ലാതാക്കണോ?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ഇനങ്ങൾ ഇല്ലാതാക്കണോ?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ഇനം ഇല്ലാതാക്കണോ?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-mn-rMN/strings.xml b/packages/DocumentsUI/res/values-mn-rMN/strings.xml
index 64cc2a8..cf2c2d4 100644
--- a/packages/DocumentsUI/res/values-mn-rMN/strings.xml
+++ b/packages/DocumentsUI/res/values-mn-rMN/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Баримт бичгийн нэрийн өөрчилж чадсангүй"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Зарим файлыг хөрвүүлсэн"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="STORAGE"><i>^3</i></xliff:g>-д байгаа <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> лавлагаанд хандахыг <xliff:g id="APPNAME"><b>^1</b></xliff:g>-д зөвшөөрөх үү?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"<xliff:g id="DIRECTORY"><i>^2</i></xliff:g> лавлагаанд хандах эрхийг <xliff:g id="APPNAME"><b>^1</b></xliff:g>-д олгох уу?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="STORAGE"><i>^2</i></xliff:g>-д байгаа зураг, видео гэх мэт таны өгөгдөлд <xliff:g id="APPNAME"><b>^1</b></xliff:g> хандахыг зөвшөөрөх үү?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Дахин бүү асуу"</string>
<string name="allow" msgid="7225948811296386551">"Зөвшөөрөх"</string>
<string name="deny" msgid="2081879885755434506">"Татгалзах"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"\"<xliff:g id="NAME">%1$s</xliff:g>\"-г устгах уу?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"\"<xliff:g id="NAME">%1$s</xliff:g>\" фолдер болон үүний агуулгыг устгах уу?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other"> <xliff:g id="COUNT_1">%1$d</xliff:g> файлыг устгах уу?</item>
+ <item quantity="one"> <xliff:g id="COUNT_0">%1$d</xliff:g> файлыг устгах уу?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other"> <xliff:g id="COUNT_1">%1$d</xliff:g> фолдер болон агуулгуудыг нь устгах уу?</item>
+ <item quantity="one"> <xliff:g id="COUNT_0">%1$d</xliff:g> фолдер болон агуулгыг нь устгах уу?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other"> <xliff:g id="COUNT_1">%1$d</xliff:g> зүйлийг устгах уу?</item>
+ <item quantity="one"> <xliff:g id="COUNT_0">%1$d</xliff:g> зүйлийг устгах уу?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-mr-rIN/strings.xml b/packages/DocumentsUI/res/values-mr-rIN/strings.xml
index 415eda8..09dd133 100644
--- a/packages/DocumentsUI/res/values-mr-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-mr-rIN/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"दस्तऐवज पुनर्नामित करण्यात अयशस्वी झाले"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"काही फायली रूपांतरित केल्या होत्या"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="STORAGE"><i>^3</i></xliff:g> वर <xliff:g id="APPNAME"><b>^1</b></xliff:g> ला <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> निर्देशिकेवर प्रवेशाची मंजूरी द्यायची?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> ला <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> निर्देशिकमध्ये प्रवेश मंजूर करायचा?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> ला <xliff:g id="STORAGE"><i>^2</i></xliff:g> वर फोटो आणि व्हिडिओंसह, आपल्या डेटामध्ये प्रवेश करण्याची मंजूरी द्यायची?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"पुन्हा विचारू नका"</string>
<string name="allow" msgid="7225948811296386551">"अनुमती द्या"</string>
<string name="deny" msgid="2081879885755434506">"नकार द्या"</string>
@@ -118,11 +120,18 @@
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> निवडला</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> निवडले</item>
</plurals>
- <!-- no translation found for delete_filename_confirmation_message (5312817725577537488) -->
- <skip />
- <!-- no translation found for delete_foldername_confirmation_message (5885501832257285329) -->
- <skip />
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <string name="delete_filename_confirmation_message" msgid="5312817725577537488">"\"<xliff:g id="NAME">%1$s</xliff:g>\" हटवायची?"</string>
+ <string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"\"<xliff:g id="NAME">%1$s</xliff:g>\" फोल्डर आणि त्यामधील सामग्री हटवायची?"</string>
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> फाईल हटवायची?</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> फायली हटवायच्या?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> फोल्डर आणि त्यामधील सामग्री हटवायची?</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> फोल्डर आणि त्यामधील सामग्री हटवायची?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> आयटम हटवायचा?</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> आयटम हटवायचे?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-ms-rMY/strings.xml b/packages/DocumentsUI/res/values-ms-rMY/strings.xml
index c5f4771..9eb3d49 100644
--- a/packages/DocumentsUI/res/values-ms-rMY/strings.xml
+++ b/packages/DocumentsUI/res/values-ms-rMY/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Gagal menamakan semula dokumen"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Sesetengah fail telah ditukarkan"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Beri <xliff:g id="APPNAME"><b>^1</b></xliff:g> akses kepada direktori <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> di <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Beri <xliff:g id="APPNAME"><b>^1</b></xliff:g> akses kepada direktori <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Beri <xliff:g id="APPNAME"><b>^1</b></xliff:g> akses kepada data anda, termasuk foto dan video pada <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Jangan tanya lagi"</string>
<string name="allow" msgid="7225948811296386551">"Benarkan"</string>
<string name="deny" msgid="2081879885755434506">"Nafi"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Padamkan \"<xliff:g id="NAME">%1$s</xliff:g>\"?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Padamkan folder \"<xliff:g id="NAME">%1$s</xliff:g>\" dan kandungannya?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other">Padamkan <xliff:g id="COUNT_1">%1$d</xliff:g> fail?</item>
+ <item quantity="one">Padamkan <xliff:g id="COUNT_0">%1$d</xliff:g> fail?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other">Padamkan <xliff:g id="COUNT_1">%1$d</xliff:g> folder dan kandungannya?</item>
+ <item quantity="one">Padamkan <xliff:g id="COUNT_0">%1$d</xliff:g> folder dan kandungannya?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other">Padamkan <xliff:g id="COUNT_1">%1$d</xliff:g> item?</item>
+ <item quantity="one">Padamkan <xliff:g id="COUNT_0">%1$d</xliff:g> item?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-my-rMM/strings.xml b/packages/DocumentsUI/res/values-my-rMM/strings.xml
index bdfd1a2..7c637c4 100644
--- a/packages/DocumentsUI/res/values-my-rMM/strings.xml
+++ b/packages/DocumentsUI/res/values-my-rMM/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"စာရွက်စာတမ်းကို အမည်ပြောင်းခြင်း မအောင်မြင်ပါ"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"အချို့ဖိုင်များကို ပြောင်းလဲထားသည်"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> ကို <xliff:g id="STORAGE"><i>^3</i></xliff:g> ရှိ <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> လမ်းညွှန်အား အသုံးပြုခွင့်ပေးမလား။"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> အား <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> စာရင်းကို အသုံးပြုခွင့်ပေးမလား။"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="STORAGE"><i>^2</i></xliff:g> ရှိဓာတ်ပုံများနှင့် ဗီဒီယိုများအပါအဝင် သင့်ဒေတာများကို <xliff:g id="APPNAME"><b>^1</b></xliff:g> အားအသုံးပြုခွင့်ပေးမလား။"</string>
<string name="never_ask_again" msgid="4295278542972859268">"နောက်ထပ်မမေးပါနှင့်"</string>
<string name="allow" msgid="7225948811296386551">"ခွင့်ပြုသည်"</string>
<string name="deny" msgid="2081879885755434506">"ငြင်းပယ်သည်"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"\"<xliff:g id="NAME">%1$s</xliff:g>\" ကိုဖျက်မလား။"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"\"<xliff:g id="NAME">%1$s</xliff:g>\" ဖိုင်တွဲနှင့် ၎င်းတွင်ပါဝင်သည့် အကြောင်းအရာများကို ဖျက်မလား။"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other">ဖိုင် <xliff:g id="COUNT_1">%1$d</xliff:g> ခုကိုဖျက်မလား။</item>
+ <item quantity="one">ဖိုင် <xliff:g id="COUNT_0">%1$d</xliff:g> ခုကိုဖျက်မလား။</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other">ဖိုင်တွဲ <xliff:g id="COUNT_1">%1$d</xliff:g> ခုနှင့် ၎င်း၏အကြောင်းအရာများကို ဖျက်မလား။</item>
+ <item quantity="one">ဖိုင်တွဲ <xliff:g id="COUNT_0">%1$d</xliff:g> ခုနှင့် ၎င်း၏အကြောင်းအရာများကို ဖျက်မလား။</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other">အကြောင်းအရာ <xliff:g id="COUNT_1">%1$d</xliff:g> ခုကိုဖျက်မလား။</item>
+ <item quantity="one">အကြောင်းအရာ <xliff:g id="COUNT_0">%1$d</xliff:g> ခုကိုဖျက်မလား။</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-nb/strings.xml b/packages/DocumentsUI/res/values-nb/strings.xml
index 5922d1c..3c344eb 100644
--- a/packages/DocumentsUI/res/values-nb/strings.xml
+++ b/packages/DocumentsUI/res/values-nb/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Kunne ikke gi dokumentet nytt navn"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Noen filer er konvertert"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Vil du gi <xliff:g id="APPNAME"><b>^1</b></xliff:g> tilgang til <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>-katalogen på <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Vil du gi <xliff:g id="APPNAME"><b>^1</b></xliff:g> tilgang til <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>-katalogen?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Vil du gi <xliff:g id="APPNAME"><b>^1</b></xliff:g> tilgang til dataene dine – inkludert bilder og videoer – på <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Ikke spør igjen"</string>
<string name="allow" msgid="7225948811296386551">"Tillat"</string>
<string name="deny" msgid="2081879885755434506">"Avslå"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Vil du slette «<xliff:g id="NAME">%1$s</xliff:g>»?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Vil du slette «<xliff:g id="NAME">%1$s</xliff:g>»-mappen og innholdet i den?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other">Vil du slette <xliff:g id="COUNT_1">%1$d</xliff:g> filer?</item>
+ <item quantity="one">Vil du slette <xliff:g id="COUNT_0">%1$d</xliff:g> fil?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other">Vil du slette <xliff:g id="COUNT_1">%1$d</xliff:g> mapper og innholdet i dem?</item>
+ <item quantity="one">Vil du slette <xliff:g id="COUNT_0">%1$d</xliff:g> mappe og innholdet i den?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other">Vil du slette <xliff:g id="COUNT_1">%1$d</xliff:g> elementer?</item>
+ <item quantity="one">Vil du slette <xliff:g id="COUNT_0">%1$d</xliff:g> element?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-ne-rNP/strings.xml b/packages/DocumentsUI/res/values-ne-rNP/strings.xml
index 4bc814b..9fef037 100644
--- a/packages/DocumentsUI/res/values-ne-rNP/strings.xml
+++ b/packages/DocumentsUI/res/values-ne-rNP/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"कागजात पुन: नामाकरण गर्न असफल भयो"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"केही फाइलहरू परिवर्तन गरिएका थिए"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> लाई <xliff:g id="STORAGE"><i>^3</i></xliff:g> मा भएको <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> निर्देशिकामा पहुँच गर्न अनुमति दिने हो?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> लाई <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> निर्देशिकामाथि पहुँच गर्न अनुमति प्रदान गर्ने हो?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> लाई <xliff:g id="STORAGE"><i>^2</i></xliff:g> मा भएका तस्बिर र भिडियोहरू लगायत तपाईँको डेटामा पहुँच गर्नका लागि अनुमति दिने हो?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"फेरि नसोध्नुहोस्"</string>
<string name="allow" msgid="7225948811296386551">"अनुमति दिनुहोस्"</string>
<string name="deny" msgid="2081879885755434506">"अस्वीकार गर्नुहोस्"</string>
@@ -118,11 +120,18 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> लाई चयन गरियो</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> लाई चयन गरियो</item>
</plurals>
- <!-- no translation found for delete_filename_confirmation_message (5312817725577537488) -->
- <skip />
- <!-- no translation found for delete_foldername_confirmation_message (5885501832257285329) -->
- <skip />
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <string name="delete_filename_confirmation_message" msgid="5312817725577537488">"\"<xliff:g id="NAME">%1$s</xliff:g>\" लाई मेट्ने हो?"</string>
+ <string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"फोल्डर \"<xliff:g id="NAME">%1$s</xliff:g>\" र यसका सामग्रीहरूलाई मेट्ने हो?"</string>
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> फाइलहरूलाई मेट्ने हो?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> फाइललाई मेट्ने हो?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> फोल्डरहरू र तिनीहरूका सामग्रीहरूलाई मेट्ने हो?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> फोल्डर र यसका सामग्रीहरूलाई मेट्ने हो?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> वस्तुहरूलाई मेट्ने हो?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> वस्तुलाई मेट्ने हो?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-nl/strings.xml b/packages/DocumentsUI/res/values-nl/strings.xml
index f1b3ed7..c5b9d76 100644
--- a/packages/DocumentsUI/res/values-nl/strings.xml
+++ b/packages/DocumentsUI/res/values-nl/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Kan naam van document niet wijzigen"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Sommige bestanden zijn geconverteerd"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> toegang verlenen tot de map <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> op <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> toegang verlenen tot de map <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> toegang verlenen tot je gegevens, waaronder foto\'s en video\'s, op <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Niet meer vragen"</string>
<string name="allow" msgid="7225948811296386551">"Toestaan"</string>
<string name="deny" msgid="2081879885755434506">"Weigeren"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"<xliff:g id="NAME">%1$s</xliff:g> verwijderen?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Map <xliff:g id="NAME">%1$s</xliff:g> en de bijbehorende inhoud verwijderen?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> bestanden verwijderen?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> bestand verwijderen?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> mappen en de bijbehorende inhoud verwijderen?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> map en de bijbehorende inhoud verwijderen?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> items verwijderen?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> item verwijderen?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-pa-rIN/strings.xml b/packages/DocumentsUI/res/values-pa-rIN/strings.xml
index f9f9412..7d5e14a 100644
--- a/packages/DocumentsUI/res/values-pa-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-pa-rIN/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"ਦਸਤਾਵੇਜ਼ ਦਾ ਮੁੜ-ਨਾਮਕਰਨ ਕਰਨਾ ਅਸਫਲ ਰਿਹਾ"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"ਕੁਝ ਫ਼ਾਈਲਾਂ ਤਬਦੀਲ ਕੀਤੀਆਂ ਗਈਆਂ ਸਨ"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"ਕੀ <xliff:g id="APPNAME"><b>^1</b></xliff:g> ਨੂੰ <xliff:g id="STORAGE"><i>^3</i></xliff:g> \'ਤੇ <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> ਡਾਇਰੈਕਟਰੀ \'ਤੇ ਪਹੁੰਚ ਦੇਣੀ ਹੈ?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"ਕੀ <xliff:g id="APPNAME"><b>^1</b></xliff:g> ਨੂੰ <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> ਡਾਇਰੈਕਟਰੀ \'ਤੇ ਪਹੁੰਚ ਦੇਣੀ ਹੈ?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"ਕੀ <xliff:g id="APPNAME"><b>^1</b></xliff:g> ਨੂੰ <xliff:g id="STORAGE"><i>^2</i></xliff:g> \'ਤੇ ਫੋਟੋਆਂ ਅਤੇ ਵੀਡੀਓ ਸਮੇਤ, ਤੁਹਾਡੇ ਡੈਟੇ \'ਤੇ ਪਹੁੰਚ ਦੇਣੀ ਹੈ?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"ਦੁਬਾਰਾ ਨਾ ਪੁੱਛੋ"</string>
<string name="allow" msgid="7225948811296386551">"ਆਗਿਆ ਦਿਓ"</string>
<string name="deny" msgid="2081879885755434506">"ਅਸਵੀਕਾਰ ਕਰੋ"</string>
@@ -118,11 +120,18 @@
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ਚੁਣੀ ਗਈ</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ਚੁਣੀਆਂ ਗਈਆਂ</item>
</plurals>
- <!-- no translation found for delete_filename_confirmation_message (5312817725577537488) -->
- <skip />
- <!-- no translation found for delete_foldername_confirmation_message (5885501832257285329) -->
- <skip />
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <string name="delete_filename_confirmation_message" msgid="5312817725577537488">"ਕੀ \"<xliff:g id="NAME">%1$s</xliff:g>\" ਨੂੰ ਮਿਟਾਉਣਾ ਹੈ?"</string>
+ <string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"ਫੋਲਡਰ \"<xliff:g id="NAME">%1$s</xliff:g>\" ਅਤੇ ਉਸ ਦੀਆਂ ਸਮੱਗਰੀਆਂ ਨੂੰ ਮਿਟਾਉਣਾ ਹੈ?"</string>
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="one">ਕੀ <xliff:g id="COUNT_1">%1$d</xliff:g> ਫ਼ਾਈਲਾਂ ਨੂੰ ਮਿਟਾਉਣਾ ਹੈ?</item>
+ <item quantity="other">ਕੀ <xliff:g id="COUNT_1">%1$d</xliff:g> ਫ਼ਾਈਲਾਂ ਨੂੰ ਮਿਟਾਉਣਾ ਹੈ?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="one">ਕੀ <xliff:g id="COUNT_1">%1$d</xliff:g> ਫੋਲਡਰਾਂ ਅਤੇ ਉਹਨਾਂ ਦੀਆਂ ਸਮੱਗਰੀਆਂ ਨੂੰ ਮਿਟਾਉਣਾ ਹੈ?</item>
+ <item quantity="other">ਕੀ <xliff:g id="COUNT_1">%1$d</xliff:g> ਫੋਲਡਰਾਂ ਅਤੇ ਉਹਨਾਂ ਦੀਆਂ ਸਮੱਗਰੀਆਂ ਨੂੰ ਮਿਟਾਉਣਾ ਹੈ?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="one">ਕੀ <xliff:g id="COUNT_1">%1$d</xliff:g> ਆਈਟਮਾਂ ਨੂੰ ਮਿਟਾਉਣਾ ਹੈ?</item>
+ <item quantity="other">ਕੀ <xliff:g id="COUNT_1">%1$d</xliff:g> ਆਈਟਮਾਂ ਨੂੰ ਮਿਟਾਉਣਾ ਹੈ?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-pl/strings.xml b/packages/DocumentsUI/res/values-pl/strings.xml
index 7a35bc2..ca007d7 100644
--- a/packages/DocumentsUI/res/values-pl/strings.xml
+++ b/packages/DocumentsUI/res/values-pl/strings.xml
@@ -125,6 +125,8 @@
<string name="rename_error" msgid="4203041674883412606">"Nie udało się zmienić nazwy dokumentu"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Niektóre pliki zostały przekonwertowane"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Zezwolić aplikacji <xliff:g id="APPNAME"><b>^1</b></xliff:g> na dostęp do katalogu <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> w pamięci masowej <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Przyznać aplikacji <xliff:g id="APPNAME"><b>^1</b></xliff:g> dostęp do katalogu <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Zezwolić aplikacji <xliff:g id="APPNAME"><b>^1</b></xliff:g> na dostęp do Twoich danych, w tym zdjęć i filmów, zapisanych w pamięci <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Nie pytaj ponownie"</string>
<string name="allow" msgid="7225948811296386551">"Zezwól"</string>
<string name="deny" msgid="2081879885755434506">"Odmów"</string>
@@ -136,7 +138,22 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Usunąć „<xliff:g id="NAME">%1$s</xliff:g>”?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Usunąć folder „<xliff:g id="NAME">%1$s</xliff:g>” i jego zawartość?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="few">Usunąć <xliff:g id="COUNT_1">%1$d</xliff:g> pliki?</item>
+ <item quantity="many">Usunąć <xliff:g id="COUNT_1">%1$d</xliff:g> plików?</item>
+ <item quantity="other">Usunąć <xliff:g id="COUNT_1">%1$d</xliff:g> pliku?</item>
+ <item quantity="one">Usunąć <xliff:g id="COUNT_0">%1$d</xliff:g> plik?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="few">Usunąć <xliff:g id="COUNT_1">%1$d</xliff:g> foldery wraz z zawartością?</item>
+ <item quantity="many">Usunąć <xliff:g id="COUNT_1">%1$d</xliff:g> folderów wraz z zawartością?</item>
+ <item quantity="other">Usunąć <xliff:g id="COUNT_1">%1$d</xliff:g> folderu wraz z zawartością?</item>
+ <item quantity="one">Usunąć <xliff:g id="COUNT_0">%1$d</xliff:g> folder wraz z zawartością?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="few">Usunąć <xliff:g id="COUNT_1">%1$d</xliff:g> elementy?</item>
+ <item quantity="many">Usunąć <xliff:g id="COUNT_1">%1$d</xliff:g> elementów?</item>
+ <item quantity="other">Usunąć <xliff:g id="COUNT_1">%1$d</xliff:g> elementu?</item>
+ <item quantity="one">Usunąć <xliff:g id="COUNT_0">%1$d</xliff:g> element?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-pt-rBR/strings.xml b/packages/DocumentsUI/res/values-pt-rBR/strings.xml
index b765cd0..21359c7 100644
--- a/packages/DocumentsUI/res/values-pt-rBR/strings.xml
+++ b/packages/DocumentsUI/res/values-pt-rBR/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Falha ao renomear documento"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Alguns arquivos foram convertidos"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Conceder ao <xliff:g id="APPNAME"><b>^1</b></xliff:g> acesso ao diretório <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> no <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Conceder acesso a <xliff:g id="APPNAME"><b>^1</b></xliff:g> ao diretório <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Conceder a <xliff:g id="APPNAME"><b>^1</b></xliff:g> acesso aos seus dados, incluindo fotos e vídeos, no/na <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Não perguntar novamente"</string>
<string name="allow" msgid="7225948811296386551">"Permitir"</string>
<string name="deny" msgid="2081879885755434506">"Negar"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Excluir \" <xliff:g id="NAME">%1$s</xliff:g>\"?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Excluir pasta \"<xliff:g id="NAME">%1$s</xliff:g>\" e o respectivo conteúdo?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="one">Excluir <xliff:g id="COUNT_1">%1$d</xliff:g> arquivos?</item>
+ <item quantity="other">Excluir <xliff:g id="COUNT_1">%1$d</xliff:g> arquivos?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="one">Excluir <xliff:g id="COUNT_1">%1$d</xliff:g> pastas e o respectivo conteúdo?</item>
+ <item quantity="other">Excluir <xliff:g id="COUNT_1">%1$d</xliff:g> pastas e o respectivo conteúdo?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="one">Excluir <xliff:g id="COUNT_1">%1$d</xliff:g> itens?</item>
+ <item quantity="other">Excluir <xliff:g id="COUNT_1">%1$d</xliff:g> itens?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-pt-rPT/strings.xml b/packages/DocumentsUI/res/values-pt-rPT/strings.xml
index 1edd1c0..aea1249 100644
--- a/packages/DocumentsUI/res/values-pt-rPT/strings.xml
+++ b/packages/DocumentsUI/res/values-pt-rPT/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Falha ao mudar o nome do documento"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Alguns ficheiros foram convertidos"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Pretende conceder a <xliff:g id="APPNAME"><b>^1</b></xliff:g> acesso ao diretório <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> no(a) <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Pretende conceder a <xliff:g id="APPNAME"><b>^1</b></xliff:g> acesso ao diretório <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Pretende conceder a <xliff:g id="APPNAME"><b>^1</b></xliff:g> acesso aos seus dados, incluindo fotos e vídeos, no(a) <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Não perguntar novamente"</string>
<string name="allow" msgid="7225948811296386551">"Permitir"</string>
<string name="deny" msgid="2081879885755434506">"Recusar"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Pretende eliminar \"<xliff:g id="NAME">%1$s</xliff:g>\"?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Pretende eliminar a pasta \"<xliff:g id="NAME">%1$s</xliff:g>\" e os respetivos conteúdos?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other">Pretende eliminar <xliff:g id="COUNT_1">%1$d</xliff:g> ficheiros?</item>
+ <item quantity="one">Pretende eliminar <xliff:g id="COUNT_0">%1$d</xliff:g> ficheiro?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other">Pretende eliminar <xliff:g id="COUNT_1">%1$d</xliff:g> pastas e os respetivos conteúdos?</item>
+ <item quantity="one">Pretende eliminar <xliff:g id="COUNT_0">%1$d</xliff:g> pasta e os respetivos conteúdos?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other">Pretende eliminar <xliff:g id="COUNT_1">%1$d</xliff:g> itens?</item>
+ <item quantity="one">Pretende eliminar <xliff:g id="COUNT_0">%1$d</xliff:g> item?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-pt/strings.xml b/packages/DocumentsUI/res/values-pt/strings.xml
index b765cd0..21359c7 100644
--- a/packages/DocumentsUI/res/values-pt/strings.xml
+++ b/packages/DocumentsUI/res/values-pt/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Falha ao renomear documento"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Alguns arquivos foram convertidos"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Conceder ao <xliff:g id="APPNAME"><b>^1</b></xliff:g> acesso ao diretório <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> no <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Conceder acesso a <xliff:g id="APPNAME"><b>^1</b></xliff:g> ao diretório <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Conceder a <xliff:g id="APPNAME"><b>^1</b></xliff:g> acesso aos seus dados, incluindo fotos e vídeos, no/na <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Não perguntar novamente"</string>
<string name="allow" msgid="7225948811296386551">"Permitir"</string>
<string name="deny" msgid="2081879885755434506">"Negar"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Excluir \" <xliff:g id="NAME">%1$s</xliff:g>\"?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Excluir pasta \"<xliff:g id="NAME">%1$s</xliff:g>\" e o respectivo conteúdo?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="one">Excluir <xliff:g id="COUNT_1">%1$d</xliff:g> arquivos?</item>
+ <item quantity="other">Excluir <xliff:g id="COUNT_1">%1$d</xliff:g> arquivos?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="one">Excluir <xliff:g id="COUNT_1">%1$d</xliff:g> pastas e o respectivo conteúdo?</item>
+ <item quantity="other">Excluir <xliff:g id="COUNT_1">%1$d</xliff:g> pastas e o respectivo conteúdo?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="one">Excluir <xliff:g id="COUNT_1">%1$d</xliff:g> itens?</item>
+ <item quantity="other">Excluir <xliff:g id="COUNT_1">%1$d</xliff:g> itens?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-ro/strings.xml b/packages/DocumentsUI/res/values-ro/strings.xml
index 10f6aee..4b833b1 100644
--- a/packages/DocumentsUI/res/values-ro/strings.xml
+++ b/packages/DocumentsUI/res/values-ro/strings.xml
@@ -118,6 +118,8 @@
<string name="rename_error" msgid="4203041674883412606">"Documentul nu a putut fi redenumit"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Unele fișiere au fost convertite"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Permiteți aplicației <xliff:g id="APPNAME"><b>^1</b></xliff:g> accesul la directorul <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> de pe <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Permiteți aplicației <xliff:g id="APPNAME"><b>^1</b></xliff:g> să acceseze directorul <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Permiteți aplicației <xliff:g id="APPNAME"><b>^1</b></xliff:g> să vă acceseze datele, inclusiv fotografiile și videoclipurile, de pe <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Nu mai întreba"</string>
<string name="allow" msgid="7225948811296386551">"Permiteți"</string>
<string name="deny" msgid="2081879885755434506">"Refuzați"</string>
@@ -128,7 +130,19 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Ștergeți „<xliff:g id="NAME">%1$s</xliff:g>”?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Ștergeți dosarul „<xliff:g id="NAME">%1$s</xliff:g>” și conținutul acestuia?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="few">Ștergeți <xliff:g id="COUNT_1">%1$d</xliff:g> fișiere?</item>
+ <item quantity="other">Ștergeți <xliff:g id="COUNT_1">%1$d</xliff:g> de fișiere?</item>
+ <item quantity="one">Ștergeți <xliff:g id="COUNT_0">%1$d</xliff:g> fișier?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="few">Ștergeți <xliff:g id="COUNT_1">%1$d</xliff:g> dosare și conținutul acestora?</item>
+ <item quantity="other">Ștergeți <xliff:g id="COUNT_1">%1$d</xliff:g> de dosare și conținutul acestora?</item>
+ <item quantity="one">Ștergeți <xliff:g id="COUNT_0">%1$d</xliff:g> dosar și conținutul acestuia?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="few">Ștergeți <xliff:g id="COUNT_1">%1$d</xliff:g> elemente?</item>
+ <item quantity="other">Ștergeți <xliff:g id="COUNT_1">%1$d</xliff:g> de elemente?</item>
+ <item quantity="one">Ștergeți <xliff:g id="COUNT_0">%1$d</xliff:g> element?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-ru/strings.xml b/packages/DocumentsUI/res/values-ru/strings.xml
index d9f40097..6ba635d 100644
--- a/packages/DocumentsUI/res/values-ru/strings.xml
+++ b/packages/DocumentsUI/res/values-ru/strings.xml
@@ -125,6 +125,8 @@
<string name="rename_error" msgid="4203041674883412606">"Не удалось переименовать документ"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Формат некоторых файлов изменен"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Открыть приложению \"<xliff:g id="APPNAME"><b>^1</b></xliff:g>\" доступ к папке \"<xliff:g id="DIRECTORY"><i>^2</i></xliff:g>\" на устройстве \"<xliff:g id="STORAGE"><i>^3</i></xliff:g>\"?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Открыть приложению \"<xliff:g id="APPNAME"><b>^1</b></xliff:g>\" доступ к папке \"<xliff:g id="DIRECTORY"><i>^2</i></xliff:g>\"?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Открыть приложению \"<xliff:g id="APPNAME"><b>^1</b></xliff:g>\" доступ к вашим данным, включая фото и видео, на носителе: <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Больше не спрашивать"</string>
<string name="allow" msgid="7225948811296386551">"Разрешить"</string>
<string name="deny" msgid="2081879885755434506">"Отклонить"</string>
@@ -136,7 +138,22 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Удалить файл \"<xliff:g id="NAME">%1$s</xliff:g>\"?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Удалить папку \"<xliff:g id="NAME">%1$s</xliff:g>\" со всем содержимым?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="one">Удалить <xliff:g id="COUNT_1">%1$d</xliff:g> файл?</item>
+ <item quantity="few">Удалить <xliff:g id="COUNT_1">%1$d</xliff:g> файла?</item>
+ <item quantity="many">Удалить <xliff:g id="COUNT_1">%1$d</xliff:g> файлов?</item>
+ <item quantity="other">Удалить <xliff:g id="COUNT_1">%1$d</xliff:g> файла?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="one">Удалить <xliff:g id="COUNT_1">%1$d</xliff:g> папку со всем содержимым?</item>
+ <item quantity="few">Удалить <xliff:g id="COUNT_1">%1$d</xliff:g> папки со всем содержимым?</item>
+ <item quantity="many">Удалить <xliff:g id="COUNT_1">%1$d</xliff:g> папок со всем содержимым?</item>
+ <item quantity="other">Удалить <xliff:g id="COUNT_1">%1$d</xliff:g> папки со всем содержимым?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="one">Удалить <xliff:g id="COUNT_1">%1$d</xliff:g> объект?</item>
+ <item quantity="few">Удалить <xliff:g id="COUNT_1">%1$d</xliff:g> объекта?</item>
+ <item quantity="many">Удалить <xliff:g id="COUNT_1">%1$d</xliff:g> объектов?</item>
+ <item quantity="other">Удалить <xliff:g id="COUNT_1">%1$d</xliff:g> объекта?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-si-rLK/strings.xml b/packages/DocumentsUI/res/values-si-rLK/strings.xml
index 41ee2260..41c9d4a 100644
--- a/packages/DocumentsUI/res/values-si-rLK/strings.xml
+++ b/packages/DocumentsUI/res/values-si-rLK/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"ලේඛනය යළි නම් කිරීම අසාර්ථක විය"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"සමහර ගොනු පරිවර්තනය කරන ලදී"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> හට <xliff:g id="STORAGE"><i>^3</i></xliff:g> මත <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> නාමාවලිය වෙත ප්රවේශය දෙන්නද?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> ප්රවේශය <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> නාමාවලිය වෙත ලබා දෙන්නද?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="STORAGE"><i>^2</i></xliff:g> හි, ඡායාරූප සහ වීඩියෝ ඇතුළුව, ඔබේ දත්තවලට <xliff:g id="APPNAME"><b>^1</b></xliff:g> හට ප්රවේශය ලබා දෙන්නද?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"නැවත අසන්න එපා"</string>
<string name="allow" msgid="7225948811296386551">"අවසර දෙන්න"</string>
<string name="deny" msgid="2081879885755434506">"ප්රතික්ෂේප කරන්න"</string>
@@ -118,11 +120,18 @@
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g>ක් තෝරන ලදී</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g>ක් තෝරන ලදී</item>
</plurals>
- <!-- no translation found for delete_filename_confirmation_message (5312817725577537488) -->
- <skip />
- <!-- no translation found for delete_foldername_confirmation_message (5885501832257285329) -->
- <skip />
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <string name="delete_filename_confirmation_message" msgid="5312817725577537488">"\"<xliff:g id="NAME">%1$s</xliff:g>\" මකන්නද?"</string>
+ <string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"\"<xliff:g id="NAME">%1$s</xliff:g>\" ෆෝල්ඩරය හා එහි අන්තර්ගත මකන්නද?"</string>
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="one">ගොනු <xliff:g id="COUNT_1">%1$d</xliff:g> ක් මකන්නද?</item>
+ <item quantity="other">ගොනු <xliff:g id="COUNT_1">%1$d</xliff:g> ක් මකන්නද?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="one">ෆෝල්ඩර <xliff:g id="COUNT_1">%1$d</xliff:g> ක් හා එහි අන්තර්ගත මකන්නද?</item>
+ <item quantity="other">ෆෝල්ඩර <xliff:g id="COUNT_1">%1$d</xliff:g> ක් හා එහි අන්තර්ගත මකන්නද?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="one">අයිතම <xliff:g id="COUNT_1">%1$d</xliff:g> ක් මකන්නද?</item>
+ <item quantity="other">අයිතම <xliff:g id="COUNT_1">%1$d</xliff:g> ක් මකන්නද?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-sk/strings.xml b/packages/DocumentsUI/res/values-sk/strings.xml
index 518093c..1ff01b0 100644
--- a/packages/DocumentsUI/res/values-sk/strings.xml
+++ b/packages/DocumentsUI/res/values-sk/strings.xml
@@ -125,6 +125,8 @@
<string name="rename_error" msgid="4203041674883412606">"Premenovanie dokumentu zlyhalo"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Niektoré súbory boli konvertované"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Udeliť aplikácii <xliff:g id="APPNAME"><b>^1</b></xliff:g> prístup k adresáru <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> v úložisku <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Udeliť aplikácii <xliff:g id="APPNAME"><b>^1</b></xliff:g> prístup k adresáru <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Chcete aplikácii <xliff:g id="APPNAME"><b>^1</b></xliff:g> udeliť prístup k dátam (vrátane fotiek a videí) v úložisku <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Nabudúce sa nepýtať"</string>
<string name="allow" msgid="7225948811296386551">"Povoliť"</string>
<string name="deny" msgid="2081879885755434506">"Zamietnuť"</string>
@@ -136,7 +138,22 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Odstrániť <xliff:g id="NAME">%1$s</xliff:g>?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Odstrániť priečinok <xliff:g id="NAME">%1$s</xliff:g> a jeho obsah?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="few">Odstrániť <xliff:g id="COUNT_1">%1$d</xliff:g> súbory?</item>
+ <item quantity="many">Odstrániť <xliff:g id="COUNT_1">%1$d</xliff:g> súboru?</item>
+ <item quantity="other">Odstrániť <xliff:g id="COUNT_1">%1$d</xliff:g> súborov?</item>
+ <item quantity="one">Odstrániť <xliff:g id="COUNT_0">%1$d</xliff:g> súbor?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="few">Odstrániť <xliff:g id="COUNT_1">%1$d</xliff:g> priečinky a ich obsah?</item>
+ <item quantity="many">Odstrániť <xliff:g id="COUNT_1">%1$d</xliff:g> priečinka a jeho obsah?</item>
+ <item quantity="other">Odstrániť <xliff:g id="COUNT_1">%1$d</xliff:g> priečinkov a ich obsah?</item>
+ <item quantity="one">Odstrániť <xliff:g id="COUNT_0">%1$d</xliff:g> priečinok a jeho obsah?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="few">Odstrániť <xliff:g id="COUNT_1">%1$d</xliff:g> položky?</item>
+ <item quantity="many">Odstrániť <xliff:g id="COUNT_1">%1$d</xliff:g> položky?</item>
+ <item quantity="other">Odstrániť <xliff:g id="COUNT_1">%1$d</xliff:g> položiek?</item>
+ <item quantity="one">Odstrániť <xliff:g id="COUNT_0">%1$d</xliff:g> položku?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-sl/strings.xml b/packages/DocumentsUI/res/values-sl/strings.xml
index 39cfed7..d8b983c 100644
--- a/packages/DocumentsUI/res/values-sl/strings.xml
+++ b/packages/DocumentsUI/res/values-sl/strings.xml
@@ -125,6 +125,8 @@
<string name="rename_error" msgid="4203041674883412606">"Dokumenta ni bilo mogoče preimenovati"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Nekatere datoteke so bile pretvorjene"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Želite aplikaciji <xliff:g id="APPNAME"><b>^1</b></xliff:g> dovoliti dostop do imenika <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> v shrambi <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Želite aplikaciji <xliff:g id="APPNAME"><b>^1</b></xliff:g> dovoliti dostop do imenika <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Odobrite aplikaciji <xliff:g id="APPNAME"><b>^1</b></xliff:g> dostop do podatkov, vključno s fotografijami in videoposnetki, v shrambi <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Ne sprašuj več"</string>
<string name="allow" msgid="7225948811296386551">"Dovoli"</string>
<string name="deny" msgid="2081879885755434506">"Zavrni"</string>
@@ -136,7 +138,22 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Ali želite izbrisati »<xliff:g id="NAME">%1$s</xliff:g>«?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Ali želite izbrisati mapo »<xliff:g id="NAME">%1$s</xliff:g>« in njeno vsebino?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="one">Ali želite izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> datoteko?</item>
+ <item quantity="two">Ali želite izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> datoteki?</item>
+ <item quantity="few">Ali želite izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> datoteke?</item>
+ <item quantity="other">Ali želite izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> datotek?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="one">Ali želite izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> mapo in njihovo vsebino?</item>
+ <item quantity="two">Ali želite izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> mapi in njihovo vsebino?</item>
+ <item quantity="few">Ali želite izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> mape in njihovo vsebino?</item>
+ <item quantity="other">Ali želite izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> map in njihovo vsebino?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="one">Ali želite izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> element?</item>
+ <item quantity="two">Ali želite izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> elementa?</item>
+ <item quantity="few">Ali želite izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> elemente?</item>
+ <item quantity="other">Ali želite izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> elementov?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-sq-rAL/strings.xml b/packages/DocumentsUI/res/values-sq-rAL/strings.xml
index f5ebbbc..933a537 100644
--- a/packages/DocumentsUI/res/values-sq-rAL/strings.xml
+++ b/packages/DocumentsUI/res/values-sq-rAL/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Riemërtimi i dokumentit dështoi"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Disa skedarë u konvertuan"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Jepi aplikacionit <xliff:g id="APPNAME"><b>^1</b></xliff:g> qasje te direktoria <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> në <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"T\'i jepet aplikacionit <xliff:g id="APPNAME"><b>^1</b></xliff:g> qasje te direktoria <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"T\'i jepet aplikacionit <xliff:g id="APPNAME"><b>^1</b></xliff:g> qasje te të dhënat, duke përfshirë fotografitë dhe videot, në <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Mos pyet përsëri"</string>
<string name="allow" msgid="7225948811296386551">"Lejo"</string>
<string name="deny" msgid="2081879885755434506">"Moho"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Të fshihet \"<xliff:g id="NAME">%1$s</xliff:g>\"?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Të fshihet dosja \"<xliff:g id="NAME">%1$s</xliff:g>\" dhe përmbajtja e saj?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other">Të fshihen <xliff:g id="COUNT_1">%1$d</xliff:g> skedarë?</item>
+ <item quantity="one">Të fshihet <xliff:g id="COUNT_0">%1$d</xliff:g> skedar?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other">Të fshihen <xliff:g id="COUNT_1">%1$d</xliff:g> dosje dhe përmbajtjet e saj?</item>
+ <item quantity="one">Të fshihet <xliff:g id="COUNT_0">%1$d</xliff:g> dosje dhe përmbajtjet e saj?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other">Të fshihen <xliff:g id="COUNT_1">%1$d</xliff:g> artikuj?</item>
+ <item quantity="one">Të fshihet <xliff:g id="COUNT_0">%1$d</xliff:g> artikull?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-sr/strings.xml b/packages/DocumentsUI/res/values-sr/strings.xml
index 6a6488e..0505f42 100644
--- a/packages/DocumentsUI/res/values-sr/strings.xml
+++ b/packages/DocumentsUI/res/values-sr/strings.xml
@@ -118,6 +118,8 @@
<string name="rename_error" msgid="4203041674883412606">"Преименовање документа није успело"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Неке датотеке су конвертоване"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Желите ли да апликацији <xliff:g id="APPNAME"><b>^1</b></xliff:g> одобрите приступ директоријуму <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> на меморијском простору <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Желите да дозволите да <xliff:g id="APPNAME"><b>^1</b></xliff:g> приступа директоријуму <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Желите да ли да дозволите да апликација <xliff:g id="APPNAME"><b>^1</b></xliff:g> приступа подацима, укључујући слике и видео снимке, на локацији <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Не питај поново"</string>
<string name="allow" msgid="7225948811296386551">"Дозволи"</string>
<string name="deny" msgid="2081879885755434506">"Одбиј"</string>
@@ -128,7 +130,19 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Желите ли да избришете „<xliff:g id="NAME">%1$s</xliff:g>“?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Желите ли да избришете директоријум „<xliff:g id="NAME">%1$s</xliff:g>“ и његов садржај?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="one">Желите ли да избришете <xliff:g id="COUNT_1">%1$d</xliff:g> датотеку?</item>
+ <item quantity="few">Желите ли да избришете <xliff:g id="COUNT_1">%1$d</xliff:g> датотеке?</item>
+ <item quantity="other">Желите ли да избришете <xliff:g id="COUNT_1">%1$d</xliff:g> датотека?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="one">Желите ли да избришете <xliff:g id="COUNT_1">%1$d</xliff:g> директоријум и њихов садржај?</item>
+ <item quantity="few">Желите ли да избришете <xliff:g id="COUNT_1">%1$d</xliff:g> директоријума и њихов садржај?</item>
+ <item quantity="other">Желите ли да избришете <xliff:g id="COUNT_1">%1$d</xliff:g> директоријума и њихов садржај?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="one">Желите ли да избришете <xliff:g id="COUNT_1">%1$d</xliff:g> ставку?</item>
+ <item quantity="few">Желите ли да избришете <xliff:g id="COUNT_1">%1$d</xliff:g> ставке?</item>
+ <item quantity="other">Желите ли да избришете <xliff:g id="COUNT_1">%1$d</xliff:g> ставки?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-sv/strings.xml b/packages/DocumentsUI/res/values-sv/strings.xml
index 8b55b22..99f334b 100644
--- a/packages/DocumentsUI/res/values-sv/strings.xml
+++ b/packages/DocumentsUI/res/values-sv/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Det gick inte att byta namn på dokumentet"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Vissa filer konverterades"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Vill du ge <xliff:g id="APPNAME"><b>^1</b></xliff:g> åtkomst till katalogen <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> på <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Vill du ge <xliff:g id="APPNAME"><b>^1</b></xliff:g> åtkomst till katalogen <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Vill du ge <xliff:g id="APPNAME"><b>^1</b></xliff:g> åtkomst till din data (inklusive foton och videor) på <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Fråga inte igen"</string>
<string name="allow" msgid="7225948811296386551">"Tillåt"</string>
<string name="deny" msgid="2081879885755434506">"Neka"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Vill du radera <xliff:g id="NAME">%1$s</xliff:g>?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Vill du radera mappen <xliff:g id="NAME">%1$s</xliff:g> och dess innehåll?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other">Vill du radera <xliff:g id="COUNT_1">%1$d</xliff:g> filer?</item>
+ <item quantity="one">Vill du radera <xliff:g id="COUNT_0">%1$d</xliff:g> fil?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other">Vill du radera <xliff:g id="COUNT_1">%1$d</xliff:g> mappar och deras innehåll?</item>
+ <item quantity="one">Vill du radera <xliff:g id="COUNT_0">%1$d</xliff:g> mapp och dess innehåll?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other">Vill du radera <xliff:g id="COUNT_1">%1$d</xliff:g> objekt?</item>
+ <item quantity="one">Vill du radera <xliff:g id="COUNT_0">%1$d</xliff:g> objekt?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-sw/strings.xml b/packages/DocumentsUI/res/values-sw/strings.xml
index 482c7ec..566e6a4 100644
--- a/packages/DocumentsUI/res/values-sw/strings.xml
+++ b/packages/DocumentsUI/res/values-sw/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Imeshindwa kubadilisha jina la hati"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Baadhi ya faili zimebadilishwa muundo"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Ungependa kuruhusu <xliff:g id="APPNAME"><b>^1</b></xliff:g> ifikie saraka ya <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> kwenye <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Ungependa kuruhusu <xliff:g id="APPNAME"><b>^1</b></xliff:g> ifikie saraka ya <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Ungependa kuruhusu <xliff:g id="APPNAME"><b>^1</b></xliff:g> ifikie data yako, ikiwa ni pamoja na picha na video kwenye <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Usiniulize tena"</string>
<string name="allow" msgid="7225948811296386551">"Ruhusu"</string>
<string name="deny" msgid="2081879885755434506">"Kataza"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Ungependa kufuta \"<xliff:g id="NAME">%1$s</xliff:g>\"?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Ungependa kufuta folda ya \"<xliff:g id="NAME">%1$s</xliff:g>\" na maudhui yake?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other">Ungependa kufuta faili <xliff:g id="COUNT_1">%1$d</xliff:g>?</item>
+ <item quantity="one">Ungependa kufuta faili <xliff:g id="COUNT_0">%1$d</xliff:g>?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other">Ungependa kufuta folda <xliff:g id="COUNT_1">%1$d</xliff:g> na maudhui yaliyomo?</item>
+ <item quantity="one">Ungependa kufuta folda <xliff:g id="COUNT_0">%1$d</xliff:g> na maudhui yaliyomo?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other">Ungependa kufuta vipengee <xliff:g id="COUNT_1">%1$d</xliff:g>?</item>
+ <item quantity="one">Ungependa kufuta kipengee <xliff:g id="COUNT_0">%1$d</xliff:g>?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-sw720dp/colors.xml b/packages/DocumentsUI/res/values-sw720dp/colors.xml
new file mode 100644
index 0000000..3ecafe2
--- /dev/null
+++ b/packages/DocumentsUI/res/values-sw720dp/colors.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <color name="menu_search_background">#ff676f74</color>
+</resources>
diff --git a/packages/DocumentsUI/res/values-sw720dp/config.xml b/packages/DocumentsUI/res/values-sw720dp/config.xml
new file mode 100644
index 0000000..4898e74
--- /dev/null
+++ b/packages/DocumentsUI/res/values-sw720dp/config.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <!-- Indicates if search view is taking the whole toolbar space -->
+ <bool name="full_bar_search_view">false</bool>
+</resources>
diff --git a/packages/DocumentsUI/res/values-ta-rIN/strings.xml b/packages/DocumentsUI/res/values-ta-rIN/strings.xml
index b32c324..9a8b4ec 100644
--- a/packages/DocumentsUI/res/values-ta-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-ta-rIN/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"ஆவணத்திற்கு மறுபெயரிடுவதில் தோல்வி"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"சில கோப்புகள் மாற்றப்பட்டன"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="STORAGE"><i>^3</i></xliff:g> இல் உள்ள <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> கோப்பகத்தை அணுக <xliff:g id="APPNAME"><b>^1</b></xliff:g>ஐ அனுமதிக்கவா?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"<xliff:g id="DIRECTORY"><i>^2</i></xliff:g> கோப்பகத்தை அணுக, <xliff:g id="APPNAME"><b>^1</b></xliff:g>ஐ அனுமதிக்கவா?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="STORAGE"><i>^2</i></xliff:g> இல் உள்ள படங்கள், வீடியோக்கள் உட்பட எல்லா தரவையும் அணுக, <xliff:g id="APPNAME"><b>^1</b></xliff:g>ஐ அனுமதிக்கவா?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"மீண்டும் கேட்காதே"</string>
<string name="allow" msgid="7225948811296386551">"அனுமதி"</string>
<string name="deny" msgid="2081879885755434506">"நிராகரி"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"\"<xliff:g id="NAME">%1$s</xliff:g>\"ஐ நீக்கவா?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"\"<xliff:g id="NAME">%1$s</xliff:g>\" கோப்புறையையும் அதன் உள்ளடக்கத்தையும் நீக்கவா?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> கோப்புகளை நீக்கவா?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> கோப்பை நீக்கவா?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> கோப்புறைகளையும் அவற்றின் உள்ளடக்கத்தையும் நீக்கவா?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> கோப்புறையையும் அதன் உள்ளடக்கத்தையும் நீக்கவா?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> உருப்படிகளை நீக்கவா?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> உருப்படியை நீக்கவா?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-te-rIN/strings.xml b/packages/DocumentsUI/res/values-te-rIN/strings.xml
index e77fd66..224d0db 100644
--- a/packages/DocumentsUI/res/values-te-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-te-rIN/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"పత్రం పేరు మార్చడంలో విఫలమైంది"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"కొన్ని పైల్లు మార్చబడ్డాయి"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g>కి <xliff:g id="STORAGE"><i>^3</i></xliff:g>లో <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> డైరెక్టరీ ప్రాప్యతను మంజూరు చేయాలా?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"<xliff:g id="APPNAME"><b>^1</b></xliff:g>కి <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> డైరెక్టరీ ప్రాప్యతను మంజూరు చేయాలా?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="STORAGE"><i>^2</i></xliff:g>లో ఫోటోలు మరియు వీడియోలతో సహా మీ డేటా ప్రాప్యతను <xliff:g id="APPNAME"><b>^1</b></xliff:g>కి మంజూరు చేయాలా?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"మళ్లీ అడగవద్దు"</string>
<string name="allow" msgid="7225948811296386551">"అనుమతించండి"</string>
<string name="deny" msgid="2081879885755434506">"తిరస్కరించండి"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"\"<xliff:g id="NAME">%1$s</xliff:g>\"ని తొలగించాలా?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"\"<xliff:g id="NAME">%1$s</xliff:g>\" ఫోల్డర్ని మరియు అందులోని కంటెంట్లను తొలగించాలా?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ఫైల్లను తొలగించాలా?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ఫైల్ను తొలగించాలా?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ఫోల్డర్లు మరియు వీటిలోని కంటెంట్లను తొలగించాలా?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ఫోల్డర్ మరియు దీనిలోని కంటెంట్లను తొలగించాలా?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> అంశాలను తొలగించాలా?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> అంశాన్ని తొలగించాలా?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-th/strings.xml b/packages/DocumentsUI/res/values-th/strings.xml
index 4d94795..af07584 100644
--- a/packages/DocumentsUI/res/values-th/strings.xml
+++ b/packages/DocumentsUI/res/values-th/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"ไม่สามารถเปลี่ยนชื่อเอกสาร"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"แปลงบางไฟล์แล้ว"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"ให้สิทธิ์ <xliff:g id="APPNAME"><b>^1</b></xliff:g> ในการเข้าถึงไดเรกทอรี <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> ใน <xliff:g id="STORAGE"><i>^3</i></xliff:g> ไหม"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"ให้สิทธิ์ <xliff:g id="APPNAME"><b>^1</b></xliff:g> เข้าถึงไดเรกทอรี <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> ไหม"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"ให้สิทธิ์ <xliff:g id="APPNAME"><b>^1</b></xliff:g> เข้าถึงข้อมูลของคุณ รวมถึงรูปภาพและวิดีโอใน <xliff:g id="STORAGE"><i>^2</i></xliff:g> ไหม"</string>
<string name="never_ask_again" msgid="4295278542972859268">"ไม่ต้องถามอีก"</string>
<string name="allow" msgid="7225948811296386551">"อนุญาต"</string>
<string name="deny" msgid="2081879885755434506">"ปฏิเสธ"</string>
@@ -118,11 +120,18 @@
<item quantity="other">เลือกไว้ <xliff:g id="COUNT_1">%1$d</xliff:g> รายการ</item>
<item quantity="one">เลือกไว้ <xliff:g id="COUNT_0">%1$d</xliff:g> รายการ</item>
</plurals>
- <!-- no translation found for delete_filename_confirmation_message (5312817725577537488) -->
- <skip />
- <!-- no translation found for delete_foldername_confirmation_message (5885501832257285329) -->
- <skip />
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <string name="delete_filename_confirmation_message" msgid="5312817725577537488">"ลบ \"<xliff:g id="NAME">%1$s</xliff:g>\" ไหม"</string>
+ <string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"ลบโฟลเดอร์ \"<xliff:g id="NAME">%1$s</xliff:g>\" และเนื้อหาข้างในไหม"</string>
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other">ลบ <xliff:g id="COUNT_1">%1$d</xliff:g> ไฟล์ใช่ไหม</item>
+ <item quantity="one">ลบ <xliff:g id="COUNT_0">%1$d</xliff:g> ไฟล์ใช่ไหม</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other">ลบ <xliff:g id="COUNT_1">%1$d</xliff:g> โฟลเดอร์และเนื้อหาข้างในใช่ไหม</item>
+ <item quantity="one">ลบ <xliff:g id="COUNT_0">%1$d</xliff:g> โฟลเดอร์และเนื้อหาข้างในใช่ไหม</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other">ลบ <xliff:g id="COUNT_1">%1$d</xliff:g> รายการใช่ไหม</item>
+ <item quantity="one">ลบ <xliff:g id="COUNT_0">%1$d</xliff:g> รายการใช่ไหม</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-tl/strings.xml b/packages/DocumentsUI/res/values-tl/strings.xml
index f395a5b..b2acf05 100644
--- a/packages/DocumentsUI/res/values-tl/strings.xml
+++ b/packages/DocumentsUI/res/values-tl/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Hindi napalitan ang pangalan ng dokumento"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Na-convert ang ilang file"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Bigyan ang <xliff:g id="APPNAME"><b>^1</b></xliff:g> ng access sa directory ng <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> sa <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Bibigyan ang <xliff:g id="APPNAME"><b>^1</b></xliff:g> ng access sa direktoryong <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Bigyan ang <xliff:g id="APPNAME"><b>^1</b></xliff:g> ng access sa iyong data, kabilang ang mga larawan at video, sa <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Huwag nang tatanunging muli"</string>
<string name="allow" msgid="7225948811296386551">"Payagan"</string>
<string name="deny" msgid="2081879885755434506">"Tanggihan"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Gusto mo bang i-delete ang \"<xliff:g id="NAME">%1$s</xliff:g>?\""</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Gusto mo bang i-delete ang folder na \"<xliff:g id="NAME">%1$s</xliff:g>\" at ang mga content nito?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="one">Gusto mo bang i-delete ang <xliff:g id="COUNT_1">%1$d</xliff:g> file?</item>
+ <item quantity="other">Gusto mo bang i-delete ang <xliff:g id="COUNT_1">%1$d</xliff:g> (na) file?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="one">Gusto mo bang i-delete ang <xliff:g id="COUNT_1">%1$d</xliff:g> folder at mga content ng mga ito?</item>
+ <item quantity="other">Gusto mo bang i-delete ang <xliff:g id="COUNT_1">%1$d</xliff:g> na folder at mga content ng mga ito?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="one">Gusto mo bang i-delete ang <xliff:g id="COUNT_1">%1$d</xliff:g> item?</item>
+ <item quantity="other">Gusto mo bang i-delete ang <xliff:g id="COUNT_1">%1$d</xliff:g> na item?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-tr/strings.xml b/packages/DocumentsUI/res/values-tr/strings.xml
index bc4db0d..c5653c9 100644
--- a/packages/DocumentsUI/res/values-tr/strings.xml
+++ b/packages/DocumentsUI/res/values-tr/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Dokümanın adı değiştirilemedi"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Bazı dosyalar dönüştürüldü"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> uygulamasına <xliff:g id="STORAGE"><i>^3</i></xliff:g> depolama alanındaki <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> dizinine erişim izni verilsin mi?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"<xliff:g id="DIRECTORY"><i>^2</i></xliff:g> dizinine erişmek için <xliff:g id="APPNAME"><b>^1</b></xliff:g> uygulamasına izin verilsin mi?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> uygulamasının, fotoğraflar ve videolar dahil olmak üzere <xliff:g id="STORAGE"><i>^2</i></xliff:g> üzerindeki verilerinize erişmesine izin verilsin mi?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Tekrar sorma"</string>
<string name="allow" msgid="7225948811296386551">"İzin Ver"</string>
<string name="deny" msgid="2081879885755434506">"Reddet"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"\"<xliff:g id="NAME">%1$s</xliff:g>\" silinsin mi?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"\"<xliff:g id="NAME">%1$s</xliff:g>\" adlı klasör ve içindekiler silinsin mi?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> dosya silinsin mi?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> dosya silinsin mi?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> klasör ve içindekiler silinsin mi?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> klasör ve içindekiler silinsin mi?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> öğe silinsin mi?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> öğe silinsin mi?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-uk/strings.xml b/packages/DocumentsUI/res/values-uk/strings.xml
index 79b6fbf..21532b6 100644
--- a/packages/DocumentsUI/res/values-uk/strings.xml
+++ b/packages/DocumentsUI/res/values-uk/strings.xml
@@ -125,6 +125,8 @@
<string name="rename_error" msgid="4203041674883412606">"Не вдалося перейменувати документ"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Деякі файли конвертовано"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Надати додатку <xliff:g id="APPNAME"><b>^1</b></xliff:g> доступ до каталогу <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> на пристрої пам’яті <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Надати додатку <xliff:g id="APPNAME"><b>^1</b></xliff:g> доступ до каталогу \"<xliff:g id="DIRECTORY"><i>^2</i></xliff:g>\"?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Надати додатку <xliff:g id="APPNAME"><b>^1</b></xliff:g> доступ до ваших даних, зокрема до фотографій і відео, які містить <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Не запитувати знову"</string>
<string name="allow" msgid="7225948811296386551">"Дозвол."</string>
<string name="deny" msgid="2081879885755434506">"Забор."</string>
@@ -136,7 +138,22 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Видалити файл <xliff:g id="NAME">%1$s</xliff:g>?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Видалити папку \"<xliff:g id="NAME">%1$s</xliff:g>\" та її вміст?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="one">Видалити <xliff:g id="COUNT_1">%1$d</xliff:g> файл?</item>
+ <item quantity="few">Видалити <xliff:g id="COUNT_1">%1$d</xliff:g> файли?</item>
+ <item quantity="many">Видалити <xliff:g id="COUNT_1">%1$d</xliff:g> файлів?</item>
+ <item quantity="other">Видалити <xliff:g id="COUNT_1">%1$d</xliff:g> файлу?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="one">Видалити <xliff:g id="COUNT_1">%1$d</xliff:g> папку та їх вміст?</item>
+ <item quantity="few">Видалити <xliff:g id="COUNT_1">%1$d</xliff:g> папки та їх вміст?</item>
+ <item quantity="many">Видалити <xliff:g id="COUNT_1">%1$d</xliff:g> папок та їх вміст?</item>
+ <item quantity="other">Видалити <xliff:g id="COUNT_1">%1$d</xliff:g> папки та їх вміст?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="one">Видалити <xliff:g id="COUNT_1">%1$d</xliff:g> елемент?</item>
+ <item quantity="few">Видалити <xliff:g id="COUNT_1">%1$d</xliff:g> елементи?</item>
+ <item quantity="many">Видалити <xliff:g id="COUNT_1">%1$d</xliff:g> елементів?</item>
+ <item quantity="other">Видалити <xliff:g id="COUNT_1">%1$d</xliff:g> елемента?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-ur-rPK/strings.xml b/packages/DocumentsUI/res/values-ur-rPK/strings.xml
index 4498f87..eb0cfc8 100644
--- a/packages/DocumentsUI/res/values-ur-rPK/strings.xml
+++ b/packages/DocumentsUI/res/values-ur-rPK/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"دستاویز کا نام تبدیل کرنے میں ناکام"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"کچھ فائلوں کو تبدیل کیا گیا تھا"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> کو <xliff:g id="STORAGE"><i>^3</i></xliff:g> پر <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> ڈائرکٹری تک رسائی عطا کریں؟"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> کو <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> ڈائرکٹری تک رسائی دیں؟"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> کو اپنے ڈیٹا بشمول <xliff:g id="STORAGE"><i>^2</i></xliff:g> پر موجود تصاویر اور ویڈیوز تک رسائی عطا کریں؟"</string>
<string name="never_ask_again" msgid="4295278542972859268">"دوبارہ نہ پوچھیں"</string>
<string name="allow" msgid="7225948811296386551">"اجازت دیں"</string>
<string name="deny" msgid="2081879885755434506">"مسترد کریں"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"\"<xliff:g id="NAME">%1$s</xliff:g>\" حذف کریں؟"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"\"<xliff:g id="NAME">%1$s</xliff:g>\" فولڈر اور اس کی مشمولات حذف کریں؟"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> فائلیں حذف کریں؟</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> فائل حذف کریں؟</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> فولڈرز اور ان کے مشمولات حذف کریں؟</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> فولڈر اور اس کے مشمولات حذف کریں؟</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> آئٹمز حذف کریں؟</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> آئٹم حذف کریں؟</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-uz-rUZ/strings.xml b/packages/DocumentsUI/res/values-uz-rUZ/strings.xml
index b4f435b..2654308 100644
--- a/packages/DocumentsUI/res/values-uz-rUZ/strings.xml
+++ b/packages/DocumentsUI/res/values-uz-rUZ/strings.xml
@@ -110,7 +110,9 @@
<string name="menu_rename" msgid="7678802479104285353">"Qayta nomlash"</string>
<string name="rename_error" msgid="4203041674883412606">"Hujjatni qayta nomlab bo‘lmadi"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Bir nechta fayllar o‘girildi"</string>
- <string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> ilovasining <xliff:g id="STORAGE"><i>^3</i></xliff:g> xotirasidagi “<xliff:g id="DIRECTORY"><i>^2</i></xliff:g>” jildiga kirishiga ruxsat berilsinmi?"</string>
+ <string name="open_external_dialog_request" msgid="5789329484285817629">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> ilovasiga <xliff:g id="STORAGE"><i>^3</i></xliff:g> xotirasidagi “<xliff:g id="DIRECTORY"><i>^2</i></xliff:g>” jildidan foydalanishiga ruxsat berilsinmi?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> ilovasiga “<xliff:g id="DIRECTORY"><i>^2</i></xliff:g>” jildidan foydalanishiga ruxsat berilsinmi?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"<xliff:g id="APPNAME"><b>^1</b></xliff:g> ilovasiga <xliff:g id="STORAGE"><i>^2</i></xliff:g> xotirasidagi ma’lumotlardan, jumladan, rasmlar va videolardan foydalanishiga ruxsat berilsinmi?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Boshqa so‘ralmasin"</string>
<string name="allow" msgid="7225948811296386551">"Ruxsat berish"</string>
<string name="deny" msgid="2081879885755434506">"Rad qilish"</string>
@@ -118,11 +120,18 @@
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ta belgilandi</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ta belgilandi</item>
</plurals>
- <!-- no translation found for delete_filename_confirmation_message (5312817725577537488) -->
- <skip />
- <!-- no translation found for delete_foldername_confirmation_message (5885501832257285329) -->
- <skip />
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <string name="delete_filename_confirmation_message" msgid="5312817725577537488">"“<xliff:g id="NAME">%1$s</xliff:g>” fayli o‘chirib tashlansinmi?"</string>
+ <string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"“<xliff:g id="NAME">%1$s</xliff:g>” jildi ichidagi kontentlari bilan o‘chirib tashlansinmi?"</string>
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ta fayl o‘chirilsinmi?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ta fayl o‘chirib tashlansinmi?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ta jild ichidagi kontentlari bilan o‘chirib tashlansinmi?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ta jild ichidagi kontentlari bilan o‘chirib tashlansinmi?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ta element o‘chirib tashlansinmi?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ta element o‘chirib tashlansinmi?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-vi/strings.xml b/packages/DocumentsUI/res/values-vi/strings.xml
index b64026a..9af7db0 100644
--- a/packages/DocumentsUI/res/values-vi/strings.xml
+++ b/packages/DocumentsUI/res/values-vi/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Không đổi được tên tài liệu"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Đã chuyển đổi một số tệp"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Cấp cho <xliff:g id="APPNAME"><b>^1</b></xliff:g> quyền truy cập vào thư mục <xliff:g id="DIRECTORY"><i>^2</i></xliff:g> trong <xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Cấp cho <xliff:g id="APPNAME"><b>^1</b></xliff:g> quyền truy cập thư mục <xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Cấp cho <xliff:g id="APPNAME"><b>^1</b></xliff:g> quyền truy cập vào dữ liệu của bạn, kể cả ảnh và video trên <xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Không hỏi lại"</string>
<string name="allow" msgid="7225948811296386551">"Cho phép"</string>
<string name="deny" msgid="2081879885755434506">"Từ chối"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Xóa \"<xliff:g id="NAME">%1$s</xliff:g>\"?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Xóa thư mục \"<xliff:g id="NAME">%1$s</xliff:g>\" và nội dung của thư mục?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other">Xóa <xliff:g id="COUNT_1">%1$d</xliff:g> tệp?</item>
+ <item quantity="one">Xóa <xliff:g id="COUNT_0">%1$d</xliff:g> tệp?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other">Xóa <xliff:g id="COUNT_1">%1$d</xliff:g> thư mục và nội dung trong đó?</item>
+ <item quantity="one">Xóa <xliff:g id="COUNT_0">%1$d</xliff:g> thư mục và nội dung trong đó?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other">Xóa <xliff:g id="COUNT_1">%1$d</xliff:g> mục?</item>
+ <item quantity="one">Xóa <xliff:g id="COUNT_0">%1$d</xliff:g> mục?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-zh-rCN/strings.xml b/packages/DocumentsUI/res/values-zh-rCN/strings.xml
index 90d1619..e3db1a0 100644
--- a/packages/DocumentsUI/res/values-zh-rCN/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rCN/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"无法重命名文档"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"部分文件已转换成其他格式"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"要授权<xliff:g id="APPNAME"><b>^1</b></xliff:g>访问 <xliff:g id="STORAGE"><i>^3</i></xliff:g>上的“<xliff:g id="DIRECTORY"><i>^2</i></xliff:g>”目录吗?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"要授权<xliff:g id="APPNAME"><b>^1</b></xliff:g>访问<xliff:g id="DIRECTORY"><i>^2</i></xliff:g>目录吗?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"要授权<xliff:g id="APPNAME"><b>^1</b></xliff:g>访问您 <xliff:g id="STORAGE"><i>^2</i></xliff:g>上的数据(包括照片和视频)吗?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"不再询问"</string>
<string name="allow" msgid="7225948811296386551">"允许"</string>
<string name="deny" msgid="2081879885755434506">"拒绝"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"要删除“<xliff:g id="NAME">%1$s</xliff:g>”吗?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"要删除文件夹“<xliff:g id="NAME">%1$s</xliff:g>”及其中的内容吗?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other">删除 <xliff:g id="COUNT_1">%1$d</xliff:g> 个文件?</item>
+ <item quantity="one">删除 <xliff:g id="COUNT_0">%1$d</xliff:g> 个文件?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other">删除 <xliff:g id="COUNT_1">%1$d</xliff:g> 个文件夹及其中的内容?</item>
+ <item quantity="one">删除 <xliff:g id="COUNT_0">%1$d</xliff:g> 个文件夹及其中的内容?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other">删除 <xliff:g id="COUNT_1">%1$d</xliff:g> 项?</item>
+ <item quantity="one">删除 <xliff:g id="COUNT_0">%1$d</xliff:g> 项?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-zh-rHK/strings.xml b/packages/DocumentsUI/res/values-zh-rHK/strings.xml
index aeda260..f13a4bd1 100644
--- a/packages/DocumentsUI/res/values-zh-rHK/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rHK/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"無法重新命名文件"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"部分檔案已轉換成其他格式"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"要為「<xliff:g id="APPNAME"><b>^1</b></xliff:g>」開放 <xliff:g id="STORAGE"><i>^3</i></xliff:g>上的「<xliff:g id="DIRECTORY"><i>^2</i></xliff:g>」目錄存取權嗎?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"要為「<xliff:g id="APPNAME"><b>^1</b></xliff:g>」開放「<xliff:g id="DIRECTORY"><i>^2</i></xliff:g>」目錄的存取權嗎?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"要向「<xliff:g id="APPNAME"><b>^1</b></xliff:g>」開放 <xliff:g id="STORAGE"><i>^2</i></xliff:g>上的相片和影片等資料的存取權嗎?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"不要再詢問"</string>
<string name="allow" msgid="7225948811296386551">"允許"</string>
<string name="deny" msgid="2081879885755434506">"拒絕"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"要刪除「<xliff:g id="NAME">%1$s</xliff:g>」嗎?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"要刪除「<xliff:g id="NAME">%1$s</xliff:g>」資料夾及其內容嗎?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other">要刪除 <xliff:g id="COUNT_1">%1$d</xliff:g> 個檔案嗎?</item>
+ <item quantity="one">要刪除 <xliff:g id="COUNT_0">%1$d</xliff:g> 個檔案嗎?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other">要刪除 <xliff:g id="COUNT_1">%1$d</xliff:g> 個資料夾及其內容嗎?</item>
+ <item quantity="one">要刪除 <xliff:g id="COUNT_0">%1$d</xliff:g> 個資料夾及其內容嗎?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other">要刪除 <xliff:g id="COUNT_1">%1$d</xliff:g> 個項目嗎?</item>
+ <item quantity="one">要刪除 <xliff:g id="COUNT_0">%1$d</xliff:g> 個項目嗎?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-zh-rTW/strings.xml b/packages/DocumentsUI/res/values-zh-rTW/strings.xml
index dc76411..f8f8282 100644
--- a/packages/DocumentsUI/res/values-zh-rTW/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rTW/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"無法重新命名文件"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"部分檔案已轉換成其他格式"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"要允許<xliff:g id="APPNAME"><b>^1</b></xliff:g>存取 <xliff:g id="STORAGE"><i>^3</i></xliff:g>上的「<xliff:g id="DIRECTORY"><i>^2</i></xliff:g>」目錄嗎?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"要允許<xliff:g id="APPNAME"><b>^1</b></xliff:g>存取「<xliff:g id="DIRECTORY"><i>^2</i></xliff:g>」目錄嗎?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"要允許「<xliff:g id="APPNAME"><b>^1</b></xliff:g>」存取 <xliff:g id="STORAGE"><i>^2</i></xliff:g>上的資料 (包括相片和影片) 嗎?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"不要再詢問"</string>
<string name="allow" msgid="7225948811296386551">"允許"</string>
<string name="deny" msgid="2081879885755434506">"拒絕"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"要刪除「<xliff:g id="NAME">%1$s</xliff:g>」嗎?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"要刪除「<xliff:g id="NAME">%1$s</xliff:g>」資料夾和當中的內容嗎?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="other">要刪除 <xliff:g id="COUNT_1">%1$d</xliff:g> 個檔案嗎?</item>
+ <item quantity="one">要刪除 <xliff:g id="COUNT_0">%1$d</xliff:g> 個檔案嗎?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="other">要刪除 <xliff:g id="COUNT_1">%1$d</xliff:g> 個資料夾和當中的內容嗎?</item>
+ <item quantity="one">要刪除 <xliff:g id="COUNT_0">%1$d</xliff:g> 個資料夾和當中的內容嗎?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="other">要刪除 <xliff:g id="COUNT_1">%1$d</xliff:g> 個項目嗎?</item>
+ <item quantity="one">要刪除 <xliff:g id="COUNT_0">%1$d</xliff:g> 個項目嗎?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-zu/strings.xml b/packages/DocumentsUI/res/values-zu/strings.xml
index c53031b..a0b9f7f 100644
--- a/packages/DocumentsUI/res/values-zu/strings.xml
+++ b/packages/DocumentsUI/res/values-zu/strings.xml
@@ -111,6 +111,8 @@
<string name="rename_error" msgid="4203041674883412606">"Yehlulekile ukuqamba kabusha idokhumenti"</string>
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Amanye amafayela aguqulelwe"</string>
<string name="open_external_dialog_request" msgid="5789329484285817629">"Nika i-<xliff:g id="APPNAME"><b>^1</b></xliff:g> ukufinyelela ekuqondiseni kwe-<xliff:g id="DIRECTORY"><i>^2</i></xliff:g> ku-<xliff:g id="STORAGE"><i>^3</i></xliff:g>?"</string>
+ <string name="open_external_dialog_request_primary_volume" msgid="6635562535713428688">"Nika ukufinyelela kwe-<xliff:g id="APPNAME"><b>^1</b></xliff:g> kwinkomba ye-<xliff:g id="DIRECTORY"><i>^2</i></xliff:g>?"</string>
+ <string name="open_external_dialog_root_request" msgid="8899108702926347720">"Nikeza i-<xliff:g id="APPNAME"><b>^1</b></xliff:g> ukufinyelela kudatha yakho, okufaka izithombe namavidiyo, ku-<xliff:g id="STORAGE"><i>^2</i></xliff:g>?"</string>
<string name="never_ask_again" msgid="4295278542972859268">"Ungaphindi ubuze"</string>
<string name="allow" msgid="7225948811296386551">"Vumela"</string>
<string name="deny" msgid="2081879885755434506">"Yala"</string>
@@ -120,7 +122,16 @@
</plurals>
<string name="delete_filename_confirmation_message" msgid="5312817725577537488">"Susa i-\"<xliff:g id="NAME">%1$s</xliff:g>\"?"</string>
<string name="delete_foldername_confirmation_message" msgid="5885501832257285329">"Susa ifolda engu-\"<xliff:g id="NAME">%1$s</xliff:g>\" nokuqukethwe kwalo?"</string>
- <!-- no translation found for delete_files_confirmation_message (8417505791395471802) -->
- <!-- no translation found for delete_folders_confirmation_message (9185648028213507769) -->
- <!-- no translation found for delete_items_confirmation_message (5376214433530243459) -->
+ <plurals name="delete_files_confirmation_message" formatted="false" msgid="8417505791395471802">
+ <item quantity="one">Susa amafayela angu-<xliff:g id="COUNT_1">%1$d</xliff:g>?</item>
+ <item quantity="other">Susa amafayela angu-<xliff:g id="COUNT_1">%1$d</xliff:g>?</item>
+ </plurals>
+ <plurals name="delete_folders_confirmation_message" formatted="false" msgid="9185648028213507769">
+ <item quantity="one">Susa amafolda angu-<xliff:g id="COUNT_1">%1$d</xliff:g> nokuqukethwe kwawo?</item>
+ <item quantity="other">Susa amafolda angu-<xliff:g id="COUNT_1">%1$d</xliff:g> nokuqukethwe kwawo?</item>
+ </plurals>
+ <plurals name="delete_items_confirmation_message" formatted="false" msgid="5376214433530243459">
+ <item quantity="one">Susa izinto ezingu-<xliff:g id="COUNT_1">%1$d</xliff:g>?</item>
+ <item quantity="other">Susa izinto ezingu-<xliff:g id="COUNT_1">%1$d</xliff:g>?</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values/colors.xml b/packages/DocumentsUI/res/values/colors.xml
index 51e04b6..cf0643d 100644
--- a/packages/DocumentsUI/res/values/colors.xml
+++ b/packages/DocumentsUI/res/values/colors.xml
@@ -24,13 +24,14 @@
<color name="window_background">#fff1f1f1</color>
<color name="drawer_background">#fff1f1f1</color>
<color name="directory_background">#fff7f7f7</color>
- <color name="menu_search_background">#ff676f74</color>
+ <color name="menu_search_background">@android:color/transparent</color>
<color name="primary_dark">@*android:color/primary_dark_material_dark</color>
<color name="primary">@*android:color/material_blue_grey_900</color>
<color name="accent">@*android:color/accent_material_light</color>
<color name="accent_dark">@*android:color/accent_material_dark</color>
<color name="action_mode">@color/material_grey_400</color>
+ <color name="status_bar_color">@*android:color/material_blue_grey_950</color>
<color name="band_select_background">#88ffffff</color>
<color name="band_select_border">#44000000</color>
diff --git a/packages/DocumentsUI/res/values/config.xml b/packages/DocumentsUI/res/values/config.xml
index 408603e..f0cab08 100644
--- a/packages/DocumentsUI/res/values/config.xml
+++ b/packages/DocumentsUI/res/values/config.xml
@@ -20,10 +20,15 @@
<!-- Intentionally unset. Vendors should set this in an overlay. -->
<string name="trusted_quick_viewer_package" translatable="false"></string>
+
+ <!-- overridden for RTL langs -->
<bool name="list_divider_inset_left">true</bool>
- <!-- Indicates if the home directory should be hidden in the roots list, that is presented
- in the drawer/left side panel ) -->
- <bool name="home_root_hidden">true</bool>
- <!-- Indicates if the advanced roots like internal storage should be hidden in the roots list) -->
- <bool name="advanced_roots_hidden">true</bool>
+
+ <!-- Flags setup as productivity oriented in which case Downloads app will be presented
+ as Files app. Including showing of the Documents and "advanced" roots. -->
+ <bool name="productivity_device">false</bool>
+
+ <!-- Indicates if search view is taking the whole toolbar space -->
+ <bool name="full_bar_search_view">true</bool>
+
</resources>
diff --git a/packages/DocumentsUI/res/values/dimens.xml b/packages/DocumentsUI/res/values/dimens.xml
index 5af7da3..e682994 100644
--- a/packages/DocumentsUI/res/values/dimens.xml
+++ b/packages/DocumentsUI/res/values/dimens.xml
@@ -38,4 +38,8 @@
<dimen name="drag_shadow_size">120dp</dimen>
<dimen name="grid_item_elevation">2dp</dimen>
<dimen name="max_drawer_width">280dp</dimen>
+
+ <dimen name="drag_shadow_width">160dp</dimen>
+ <dimen name="drag_shadow_height">48dp</dimen>
+
</resources>
diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml
index b26ee97..eb99a0d 100644
--- a/packages/DocumentsUI/res/values/strings.xml
+++ b/packages/DocumentsUI/res/values/strings.xml
@@ -18,9 +18,6 @@
<!-- Title of the documents application [CHAR LIMIT=32] -->
<string name="app_label">Documents</string>
- <!-- Title of the standalone files activity. [CHAR LIMIT=32] -->
- <string name="files_label">Files</string>
-
<!-- Title of the standalone downloads activity. [CHAR LIMIT=32] -->
<string name="downloads_label">Downloads</string>
@@ -204,6 +201,9 @@
<string name="open_external_dialog_request">Grant <xliff:g id="appName" example="System Settings"><b>^1</b></xliff:g>
access to <xliff:g id="directory" example="Pictures"><i>^2</i></xliff:g> directory on
<xliff:g id="storage" example="SD Card"><i>^3</i></xliff:g>?</string>
+ <!-- Text in an alert dialog asking user to grant app access to a given directory in the internal storage -->
+ <string name="open_external_dialog_request_primary_volume">Grant <xliff:g id="appName" example="System Settings"><b>^1</b></xliff:g>
+ access to <xliff:g id="directory" example="Pictures"><i>^2</i></xliff:g> directory?</string>
<!-- Text in an alert dialog asking user to grant app access to all data in an external storage volume -->
<string name="open_external_dialog_root_request">Grant <xliff:g id="appName" example="System Settings"><b>^1</b></xliff:g>
access to your data, including photos and videos, on <xliff:g id="storage" example="SD Card"><i>^2</i></xliff:g>?</string>
@@ -220,6 +220,12 @@
<item quantity="other"><xliff:g id="count" example="3">%1$d</xliff:g> selected</item>
</plurals>
+ <!-- Label text showing user how many items are being dragged. Can be one or more elements. -->
+ <plurals name="elements_dragged">
+ <item quantity="one"><xliff:g id="count" example="1">%1$d</xliff:g> item</item>
+ <item quantity="other"><xliff:g id="count" example="3">%1$d</xliff:g> items</item>
+ </plurals>
+
<!-- Dialog text shown to users when asking if they want to delete a file (a confirmation) -->
<string name="delete_filename_confirmation_message">Delete \"<xliff:g id="name" example="cat.jpg">%1$s</xliff:g>\"?</string>
<!-- Dialog text shown to users when asking if they want to delete a folder (a confirmation) -->
diff --git a/packages/DocumentsUI/res/values/styles.xml b/packages/DocumentsUI/res/values/styles.xml
index a548d89..9f09ebc 100644
--- a/packages/DocumentsUI/res/values/styles.xml
+++ b/packages/DocumentsUI/res/values/styles.xml
@@ -30,6 +30,7 @@
<item name="android:colorAccent">@color/accent</item>
<item name="colorActionMode">@color/action_mode</item>
<item name="android:queryBackground">@color/menu_search_background</item>
+ <item name="android:statusBarColor">@color/status_bar_color</item>
<item name="android:listDivider">@*android:drawable/list_divider_material</item>
@@ -43,7 +44,7 @@
<style name="TrimmedHorizontalProgressBar" parent="android:Widget.Material.ProgressBar.Horizontal">
<item name="android:indeterminateDrawable">@drawable/progress_indeterminate_horizontal_material_trimmed</item>
<item name="android:minHeight">3dp</item>
- <item name="android:maxHeight">3dp</item>
+ <item name="android:maxHeight">3dp</item>
</style>
</resources>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index 2911027..4ee37a5 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -18,6 +18,10 @@
import static com.android.documentsui.Shared.DEBUG;
import static com.android.documentsui.Shared.EXTRA_BENCHMARK;
+import static com.android.documentsui.State.ACTION_CREATE;
+import static com.android.documentsui.State.ACTION_GET_CONTENT;
+import static com.android.documentsui.State.ACTION_OPEN;
+import static com.android.documentsui.State.ACTION_OPEN_TREE;
import static com.android.documentsui.State.MODE_GRID;
import android.app.Activity;
@@ -32,18 +36,17 @@
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
-import android.os.MessageQueue;
import android.os.MessageQueue.IdleHandler;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Root;
import android.support.annotation.CallSuper;
import android.support.annotation.LayoutRes;
import android.support.annotation.Nullable;
-import android.support.annotation.VisibleForTesting;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
+import android.view.WindowManager;
import android.widget.Spinner;
import com.android.documentsui.SearchViewManager.SearchManagerListener;
@@ -58,6 +61,7 @@
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Date;
import java.util.List;
import java.util.concurrent.Executor;
@@ -79,6 +83,7 @@
private int mLayoutId;
private boolean mNavDrawerHasFocus;
+ private long mStartTime;
public abstract void onDocumentPicked(DocumentInfo doc, Model model);
public abstract void onDocumentsPicked(List<DocumentInfo> docs);
@@ -96,16 +101,14 @@
@CallSuper
@Override
public void onCreate(Bundle icicle) {
+ // Record the time when onCreate is invoked for metric.
+ mStartTime = new Date().getTime();
+
super.onCreate(icicle);
final Intent intent = getIntent();
- // If startup benchmark is requested by a whitelisted testing package, then close the
- // activity once idle, and notify the testing activity.
- if (intent.getBooleanExtra(EXTRA_BENCHMARK, false) &&
- BENCHMARK_TESTING_PACKAGE.equals(getCallingPackage())) {
- closeOnIdleForTesting();
- }
+ addListenerForLaunchCompletion();
setContentView(mLayoutId);
@@ -145,7 +148,8 @@
getMenuInflater().inflate(R.menu.activity, menu);
mNavigator.update();
- mSearchManager.install((DocumentsToolbar) findViewById(R.id.toolbar));
+ boolean fullBarSearch = getResources().getBoolean(R.bool.full_bar_search_view);
+ mSearchManager.install((DocumentsToolbar) findViewById(R.id.toolbar), fullBarSearch);
return showMenu;
}
@@ -163,6 +167,7 @@
final MenuItem sortSize = menu.findItem(R.id.menu_sort_size);
final MenuItem grid = menu.findItem(R.id.menu_grid);
final MenuItem list = menu.findItem(R.id.menu_list);
+ final MenuItem advanced = menu.findItem(R.id.menu_advanced);
final MenuItem fileSize = menu.findItem(R.id.menu_file_size);
// Search uses backend ranking; no sorting, recents doesn't support sort.
@@ -174,6 +179,9 @@
grid.setVisible(mState.derivedMode != State.MODE_GRID);
list.setVisible(mState.derivedMode != State.MODE_LIST);
+ advanced.setVisible(mState.showAdvancedOption);
+ advanced.setTitle(mState.showAdvancedOption && mState.showAdvanced
+ ? R.string.menu_advanced_hide : R.string.menu_advanced_show);
fileSize.setTitle(LocalPreferences.getDisplayFileSize(this)
? R.string.menu_file_size_hide : R.string.menu_file_size_show);
@@ -193,25 +201,29 @@
return state;
}
- State state = createSharedState();
- includeState(state);
- if (DEBUG) Log.d(mTag, "Created new state object: " + state);
- return state;
- }
-
- private State createSharedState() {
State state = new State();
final Intent intent = getIntent();
state.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false);
-
state.forceSize = intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_FILESIZE, false);
state.showSize = state.forceSize || LocalPreferences.getDisplayFileSize(this);
-
state.initAcceptMimes(intent);
state.excludedAuthorities = getExcludedAuthorities();
+ includeState(state);
+
+ // Advanced roots are shown by deafult without menu option if forced by config or intent.
+ state.showAdvanced = Shared.shouldShowDeviceRoot(this, intent);
+ // Menu option is shown for whitelisted intents if advanced roots are not shown by default.
+ state.showAdvancedOption = !state.showAdvanced &&
+ (state.action == ACTION_OPEN ||
+ state.action == ACTION_CREATE ||
+ state.action == ACTION_OPEN_TREE ||
+ state.action == ACTION_GET_CONTENT);
+
+ if (DEBUG) Log.d(mTag, "Created new state object: " + state);
+
return state;
}
@@ -245,6 +257,7 @@
@Override
public boolean onOptionsItemSelected(MenuItem item) {
+ Metrics.logMenuAction(this, item.getItemId());
switch (item.getItemId()) {
case android.R.id.home:
@@ -285,6 +298,10 @@
}
return true;
+ case R.id.menu_advanced:
+ setDisplayAdvancedDevices(!mState.showAdvanced);
+ return true;
+
case R.id.menu_file_size:
setDisplayFileSize(!LocalPreferences.getDisplayFileSize(this));
return true;
@@ -377,8 +394,12 @@
public void onSearchChanged(@Nullable String query) {
// We should not get here if root is not searchable
assert(canSearchRoot());
-
reloadSearch(query);
+ }
+
+ @Override
+ public void onSearchFinished() {
+ // Restores menu icons state
invalidateOptionsMenu();
}
@@ -442,10 +463,16 @@
* Method can be overridden if the change of the behavior of the the child activity is needed.
*/
public Uri getDefaultRoot() {
- return Shared.isHomeRootHidden(this)
- ? DocumentsContract.buildRootUri("com.android.providers.downloads.documents",
- "downloads")
- : DocumentsContract.buildHomeUri();
+ return Shared.shouldShowDocumentsRoot(this, getIntent())
+ ? DocumentsContract.buildHomeUri()
+ : DocumentsContract.buildRootUri(
+ "com.android.providers.downloads.documents", "downloads");
+ }
+
+ void setDisplayAdvancedDevices(boolean display) {
+ mState.showAdvanced = display;
+ RootsFragment.get(getFragmentManager()).onDisplayStateChanged();
+ invalidateOptionsMenu();
}
void setDisplayFileSize(boolean display) {
@@ -594,22 +621,22 @@
return true;
}
} else if (keyCode == KeyEvent.KEYCODE_TAB) {
+ Metrics.logKeyboardAction(this, Metrics.ACTION_KEYBOARD_SWITCH_FOCUS);
// Tab toggles focus on the navigation drawer.
toggleNavDrawerFocus();
return true;
} else if (keyCode == KeyEvent.KEYCODE_DEL) {
+ Metrics.logKeyboardAction(this, Metrics.ACTION_KEYBOARD_BACK);
popDir();
return true;
}
return super.onKeyDown(keyCode, event);
}
- @VisibleForTesting
public void addEventListener(EventListener listener) {
mEventListeners.add(listener);
}
- @VisibleForTesting
public void removeEventListener(EventListener listener) {
mEventListeners.remove(listener);
}
@@ -673,6 +700,44 @@
return false;
}
+ /**
+ * Closes the activity when it's idle.
+ */
+ private void addListenerForLaunchCompletion() {
+ addEventListener(new EventListener() {
+ @Override
+ public void onDirectoryNavigated(Uri uri) {
+ }
+
+ @Override
+ public void onDirectoryLoaded(Uri uri) {
+ removeEventListener(this);
+ getMainLooper().getQueue().addIdleHandler(new IdleHandler() {
+ @Override
+ public boolean queueIdle() {
+ // If startup benchmark is requested by a whitelisted testing package, then
+ // close the activity once idle, and notify the testing activity.
+ if (getIntent().getBooleanExtra(EXTRA_BENCHMARK, false) &&
+ BENCHMARK_TESTING_PACKAGE.equals(getCallingPackage())) {
+ setResult(RESULT_OK);
+ finish();
+ }
+
+ Metrics.logStartupMs(
+ BaseActivity.this, (int) (new Date().getTime() - mStartTime));
+
+ // Remove the idle handler.
+ return false;
+ }
+ });
+ new Handler().post(new Runnable() {
+ @Override public void run() {
+ }
+ });
+ }
+ });
+ }
+
private static final class PickRootTask extends PairedTask<BaseActivity, Void, DocumentInfo> {
private RootInfo mRoot;
@@ -694,33 +759,6 @@
}
}
- /**
- * Closes the activity when it's idle. Used only for tests.
- */
- private void closeOnIdleForTesting() {
- addEventListener(new EventListener() {
- @Override
- public void onDirectoryNavigated(Uri uri) {
- }
-
- @Override
- public void onDirectoryLoaded(Uri uri) {
- getMainLooper().getQueue().addIdleHandler(new IdleHandler() {
- @Override
- public boolean queueIdle() {
- setResult(RESULT_OK);
- finish();
- return false;
- }
- });
- new Handler().post(new Runnable() {
- @Override public void run() {
- }
- });
- }
- });
- }
-
private static final class HandleRootsChangedTask
extends PairedTask<BaseActivity, RootInfo, RootInfo> {
DocumentInfo mDownloadsDocument;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
index ebc9082..5b5a96e 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
@@ -153,10 +153,12 @@
if (result != null) {
// Navigate into newly created child
mActivity.onDirectoryCreated(result);
+ Metrics.logCreateDirOperation(getContext());
} else {
- Snackbars.makeSnackbar(mActivity, R.string.create_error, Snackbar.LENGTH_SHORT).show();
+ Snackbars.makeSnackbar(mActivity, R.string.create_error, Snackbar.LENGTH_SHORT)
+ .show();
+ Metrics.logCreateDirError(getContext());
}
-
mActivity.setPending(false);
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Display.java b/packages/DocumentsUI/src/com/android/documentsui/Display.java
index bae2d58..8b13222 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Display.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Display.java
@@ -26,7 +26,7 @@
*/
public final class Display {
/*
- * Returns the screen width in pixels.
+ * Returns the screen width in raw pixels.
*/
public static float screenWidth(Activity activity) {
Point size = new Point();
@@ -42,7 +42,7 @@
}
/*
- * Returns action bar height in pixels.
+ * Returns action bar height in raw pixels.
*/
public static float actionBarHeight(Context context) {
int actionBarHeight = 0;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index 5788420..40b54d3 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -203,8 +203,8 @@
mState.action == ACTION_PICK_COPY_DESTINATION) {
title = getResources().getString(R.string.title_save);
} else {
- // If all else fails, just call it "Downloads".
- title = getResources().getString(R.string.downloads_label);
+ // If all else fails, just call it "Documents".
+ title = getResources().getString(R.string.app_label);
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsApplication.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsApplication.java
index 9005442..5ea6cfa 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsApplication.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsApplication.java
@@ -69,7 +69,7 @@
final int memoryClassBytes = am.getMemoryClass() * 1024 * 1024;
mRoots = new RootsCache(this);
- mRoots.updateAsync();
+ mRoots.updateAsync(false);
mThumbnails = new ThumbnailCache(memoryClassBytes / 4);
@@ -105,7 +105,7 @@
final String packageName = data.getSchemeSpecificPart();
mRoots.updatePackageAsync(packageName);
} else {
- mRoots.updateAsync();
+ mRoots.updateAsync(true);
}
}
};
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DrawerController.java b/packages/DocumentsUI/src/com/android/documentsui/DrawerController.java
index 020f2c0..14e6b69 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DrawerController.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DrawerController.java
@@ -18,6 +18,7 @@
import static com.android.documentsui.Shared.DEBUG;
+import android.annotation.IntDef;
import android.app.Activity;
import android.content.Context;
import android.support.v4.app.ActionBarDrawerToggle;
@@ -27,16 +28,44 @@
import android.view.View;
import android.widget.Toolbar;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* A facade over the various pieces comprising "roots fragment in a Drawer".
*
* @see DrawerController#create(DrawerLayout)
*/
abstract class DrawerController implements DrawerListener {
-
public static final String TAG = "DrawerController";
+ // Drawer opening triggered by tapping the navigation icon
+ public static final int OPENED_HAMBURGER = 0;
+ // Drawer opening triggered by swiping right from the edge of the screen
+ public static final int OPENED_SWIPE = 1;
+ // Mostly programmatically forced drawer opening
+ public static final int OPENED_OTHER = 2;
+
+ @IntDef(flag = true, value = {
+ OPENED_HAMBURGER,
+ OPENED_SWIPE,
+ OPENED_OTHER
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Trigger {}
+
+ /**
+ * Toggles the drawer and sets the OPENED_OTHER as the action that causes opening the drawer.
+ * @param open
+ */
abstract void setOpen(boolean open);
+
+ /**
+ * Toggles the drawer.
+ * @param open
+ * @param trigger Indicates what action caused opening the drawer. It is ignored for closing.
+ */
+ abstract void setOpen(boolean open, @Trigger int trigger);
abstract boolean isPresent();
abstract boolean isOpen();
abstract void setTitle(String title);
@@ -92,11 +121,11 @@
* Runtime controller that manages a real drawer.
*/
private static final class RuntimeDrawerController extends DrawerController {
-
private final ActionBarDrawerToggle mToggle;
private DrawerLayout mLayout;
private View mDrawer;
private Toolbar mToolbar;
+ private @Trigger int mTrigger = OPENED_OTHER;
public RuntimeDrawerController(
DrawerLayout layout, View drawer, ActionBarDrawerToggle toggle,
@@ -113,8 +142,14 @@
@Override
void setOpen(boolean open) {
+ setOpen(open, OPENED_OTHER);
+ }
+
+ @Override
+ void setOpen(boolean open, @Trigger int trigger) {
if (open) {
mLayout.openDrawer(mDrawer);
+ mTrigger = trigger;
} else {
mLayout.closeDrawer(mDrawer);
}
@@ -148,16 +183,21 @@
@Override
public void onDrawerOpened(View drawerView) {
mToggle.onDrawerOpened(drawerView);
+ Metrics.logDrawerOpened(mToolbar.getContext(), mTrigger);
}
@Override
public void onDrawerClosed(View drawerView) {
mToggle.onDrawerClosed(drawerView);
+ mTrigger = OPENED_OTHER;
}
@Override
public void onDrawerStateChanged(int newState) {
mToggle.onDrawerStateChanged(newState);
+ if (newState == DrawerLayout.STATE_DRAGGING) {
+ mTrigger = OPENED_SWIPE;
+ }
}
}
@@ -169,6 +209,8 @@
@Override
void setOpen(boolean open) {}
+ @Override
+ void setOpen(boolean open, @Trigger int trigger) {}
@Override
boolean isOpen() {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
index 2af6c46..527eb78 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -180,7 +180,10 @@
@Override
public String getDrawerTitle() {
- return getResources().getString(R.string.downloads_label);
+ Intent intent = getIntent();
+ return (intent != null && intent.hasExtra(Intent.EXTRA_TITLE))
+ ? intent.getStringExtra(Intent.EXTRA_TITLE)
+ : getTitle().toString();
}
@Override
@@ -201,6 +204,8 @@
newWindow.setVisible(true);
Menus.disableHiddenItems(menu, pasteFromCb);
+ // It hides icon if searching in progress
+ mSearchManager.updateMenu();
return true;
}
@@ -210,19 +215,22 @@
case R.id.menu_create_dir:
assert(canCreateDirectory());
showCreateDirectoryDialog();
- return true;
+ break;
case R.id.menu_new_window:
createNewWindow();
- return true;
+ break;
case R.id.menu_paste_from_clipboard:
DirectoryFragment dir = getDirectoryFragment();
if (dir != null) {
dir.pasteFromClipboard();
}
- return true;
+ break;
+ default:
+ return super.onOptionsItemSelected(item);
}
- return super.onOptionsItemSelected(item);
+ Metrics.logMenuAction(this, item.getItemId());
+ return true;
}
private void createNewWindow() {
@@ -233,7 +241,7 @@
// With new multi-window mode we have to pick how we are launched.
// By default we'd be launched in-place above the existing app.
// By setting launch-to-side ActivityManager will open us to side.
- if (inMultiWindow()) {
+ if (isInMultiWindowMode()) {
intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
}
@@ -344,18 +352,21 @@
case KeyEvent.KEYCODE_A:
dir = getDirectoryFragment();
if (dir != null) {
+ Metrics.logKeyboardAction(this, Metrics.ACTION_KEYBOARD_SELECT_ALL);
dir.selectAllFiles();
}
return true;
case KeyEvent.KEYCODE_C:
dir = getDirectoryFragment();
if (dir != null) {
+ Metrics.logKeyboardAction(this, Metrics.ACTION_KEYBOARD_COPY);
dir.copySelectedToClipboard();
}
return true;
case KeyEvent.KEYCODE_V:
dir = getDirectoryFragment();
if (dir != null) {
+ Metrics.logKeyboardAction(this, Metrics.ACTION_KEYBOARD_PASTE);
dir.pasteFromClipboard();
}
return true;
@@ -381,7 +392,7 @@
}
// Open the Close drawer if it is closed and we're at the top of a root.
- if (size == 1) {
+ if (size <= 1) {
mDrawer.setOpen(true);
// Remember so we don't just close it again if back is pressed again.
mDrawerLastFiddled = System.currentTimeMillis();
diff --git a/packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java b/packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java
index 7930c28..5cb6ca3 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java
@@ -87,9 +87,24 @@
startActivity(intent);
}
- static final Intent createLaunchIntent(Context context) {
- Intent intent = new Intent(context, FilesActivity.class);
+ static final Intent createLaunchIntent(Activity activity) {
+ Intent intent = new Intent(activity, FilesActivity.class);
intent.setData(buildLaunchUri());
+
+ // Relay any config overrides bits present in the original intent.
+ Intent original = activity.getIntent();
+ if (original != null) {
+ if (original.hasExtra(Shared.EXTRA_PRODUCTIVITY_MODE)) {
+ intent.putExtra(
+ Shared.EXTRA_PRODUCTIVITY_MODE,
+ original.getBooleanExtra(Shared.EXTRA_PRODUCTIVITY_MODE, false));
+ }
+ if (original.hasExtra(Intent.EXTRA_TITLE)) {
+ intent.putExtra(
+ Intent.EXTRA_TITLE,
+ original.getStringExtra(Intent.EXTRA_TITLE));
+ }
+ }
return intent;
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Metrics.java b/packages/DocumentsUI/src/com/android/documentsui/Metrics.java
index 929d1e0..05cc7e6 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Metrics.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Metrics.java
@@ -29,7 +29,9 @@
import android.net.Uri;
import android.provider.DocumentsContract;
import android.util.Log;
+import android.view.KeyEvent;
+import com.android.documentsui.State.ActionType;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.RootInfo;
import com.android.documentsui.services.FileOperationService;
@@ -64,6 +66,12 @@
private static final String COUNT_FILEOP_SYSTEM = "docsui_fileop_system";
private static final String COUNT_FILEOP_EXTERNAL = "docsui_fileop_external";
private static final String COUNT_FILEOP_CANCELED = "docsui_fileop_canceled";
+ private static final String COUNT_STARTUP_MS = "docsui_startup_ms";
+ private static final String COUNT_DRAWER_OPENED = "docsui_drawer_opened";
+ private static final String COUNT_DRAG_N_DROP = "docsui_drag_n_drop";
+ private static final String COUNT_SEARCH = "docsui_search";
+ private static final String COUNT_MENU_ACTION = "docsui_menu_action";
+ private static final String COUNT_KEYBOARD_ACTION = "docsui_keyboard_action";
// Indices for bucketing roots in the roots histogram. "Other" is the catch-all index for any
// root that is not explicitly recognized by the Metrics code (see {@link
@@ -144,10 +152,14 @@
private static final int FILEOP_MOVE_SYSTEM_PROVIDER = 6; // Move to a system provider.
private static final int FILEOP_MOVE_EXTERNAL_PROVIDER = 7; // Move to a 3rd-party provider.
private static final int FILEOP_DELETE = 8;
+ private static final int FILEOP_RENAME = 9;
+ private static final int FILEOP_CREATE_DIR = 10;
private static final int FILEOP_OTHER_ERROR = 100;
private static final int FILEOP_DELETE_ERROR = 101;
private static final int FILEOP_MOVE_ERROR = 102;
private static final int FILEOP_COPY_ERROR = 103;
+ private static final int FILEOP_RENAME_ERROR = 104;
+ private static final int FILEOP_CREATE_DIR_ERROR = 105;
@IntDef(flag = true, value = {
FILEOP_OTHER,
@@ -158,10 +170,14 @@
FILEOP_MOVE_SYSTEM_PROVIDER,
FILEOP_MOVE_EXTERNAL_PROVIDER,
FILEOP_DELETE,
+ FILEOP_RENAME,
+ FILEOP_CREATE_DIR,
FILEOP_OTHER_ERROR,
FILEOP_COPY_ERROR,
FILEOP_MOVE_ERROR,
- FILEOP_DELETE_ERROR
+ FILEOP_DELETE_ERROR,
+ FILEOP_RENAME_ERROR,
+ FILEOP_CREATE_DIR_ERROR
})
@Retention(RetentionPolicy.SOURCE)
public @interface FileOp {}
@@ -185,8 +201,71 @@
@Retention(RetentionPolicy.SOURCE)
public @interface MetricsOpType {}
- // Codes representing different launch actions. These are used for bucketing stats in the
- // COUNT_LAUNCH_ACTION histogram.
+ // Codes representing different provider types. Used for sorting file operations when logging.
+ private static final int PROVIDER_INTRA = 0;
+ private static final int PROVIDER_SYSTEM = 1;
+ private static final int PROVIDER_EXTERNAL = 2;
+
+ @IntDef(flag = false, value = {
+ PROVIDER_INTRA,
+ PROVIDER_SYSTEM,
+ PROVIDER_EXTERNAL
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Provider {}
+
+
+ // Codes representing different menu actions. These are used for bucketing stats in the
+ // COUNT_MENU_ACTION histogram.
+ // Both regular toolbar menu and action mode menu operations are included.
+ // Do not change or rearrange these values, that will break historical data. Only add to the
+ // list.
+ // Do not use negative numbers or zero; clearcut only handles positive integers.
+ private static final int ACTION_MENU_OTHER = 1;
+ private static final int ACTION_MENU_GRID = 2;
+ private static final int ACTION_MENU_LIST = 3;
+ private static final int ACTION_MENU_SORT = 4;
+ private static final int ACTION_MENU_SORT_NAME = 5;
+ private static final int ACTION_MENU_SORT_DATE = 6;
+ private static final int ACTION_MENU_SORT_SIZE = 7;
+ private static final int ACTION_MENU_SEARCH = 8;
+ private static final int ACTION_MENU_SHOW_SIZE = 9;
+ private static final int ACTION_MENU_SETTINGS = 10;
+ private static final int ACTION_MENU_COPY_TO = 11;
+ private static final int ACTION_MENU_MOVE_TO = 12;
+ private static final int ACTION_MENU_DELETE = 13;
+ private static final int ACTION_MENU_RENAME = 14;
+ private static final int ACTION_MENU_CREATE_DIR = 15;
+ private static final int ACTION_MENU_SELECT_ALL = 16;
+ private static final int ACTION_MENU_SHARE = 17;
+ private static final int ACTION_MENU_OPEN = 18;
+ private static final int ACTION_MENU_ADVANCED = 19;
+
+ @IntDef(flag = false, value = {
+ ACTION_MENU_OTHER,
+ ACTION_MENU_GRID,
+ ACTION_MENU_LIST,
+ ACTION_MENU_SORT,
+ ACTION_MENU_SORT_NAME,
+ ACTION_MENU_SORT_DATE,
+ ACTION_MENU_SORT_SIZE,
+ ACTION_MENU_SHOW_SIZE,
+ ACTION_MENU_SETTINGS,
+ ACTION_MENU_COPY_TO,
+ ACTION_MENU_MOVE_TO,
+ ACTION_MENU_DELETE,
+ ACTION_MENU_RENAME,
+ ACTION_MENU_CREATE_DIR,
+ ACTION_MENU_SELECT_ALL,
+ ACTION_MENU_SHARE,
+ ACTION_MENU_OPEN,
+ ACTION_MENU_ADVANCED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MenuAction {}
+
+ // Codes representing different menu actions. These are used for bucketing stats in the
+ // COUNT_MENU_ACTION histogram.
// Do not change or rearrange these values, that will break historical data. Only add to the
// list.
// Do not use negative numbers or zero; clearcut only handles positive integers.
@@ -212,18 +291,45 @@
@Retention(RetentionPolicy.SOURCE)
public @interface MetricsAction {}
- // Codes representing different provider types. Used for sorting file operations when logging.
- private static final int PROVIDER_INTRA = 0;
- private static final int PROVIDER_SYSTEM = 1;
- private static final int PROVIDER_EXTERNAL = 2;
+ // Codes representing different keyboard shortcut triggered actions. These are used for
+ // bucketing stats in the COUNT_KEYBOARD_ACTION histogram.
+ // Do not change or rearrange these values, that will break historical data. Only add to the
+ // list.
+ // Do not use negative numbers or zero; clearcut only handles positive integers.
+ public static final int ACTION_KEYBOARD_OTHER = 1;
+ public static final int ACTION_KEYBOARD_PASTE = 2;
+ public static final int ACTION_KEYBOARD_COPY = 3;
+ public static final int ACTION_KEYBOARD_DELETE = 4;
+ public static final int ACTION_KEYBOARD_SELECT_ALL = 5;
+ public static final int ACTION_KEYBOARD_BACK = 6;
+ public static final int ACTION_KEYBOARD_SWITCH_FOCUS = 7;
- @IntDef(flag = true, value = {
- PROVIDER_INTRA,
- PROVIDER_SYSTEM,
- PROVIDER_EXTERNAL
+ @IntDef(flag = false, value = {
+ ACTION_KEYBOARD_OTHER,
+ ACTION_KEYBOARD_PASTE,
+ ACTION_KEYBOARD_COPY,
+ ACTION_KEYBOARD_DELETE,
+ ACTION_KEYBOARD_SELECT_ALL,
+ ACTION_KEYBOARD_BACK,
+ ACTION_KEYBOARD_SWITCH_FOCUS
})
@Retention(RetentionPolicy.SOURCE)
- public @interface Provider {}
+ public @interface KeyboardAction {}
+
+ // Codes representing different actions to open the drawer. They are used for bucketing stats in
+ // the COUNT_DRAWER_OPENED histogram.
+ // Do not change or rearrange these values, that will break historical data. Only add to the
+ // list.
+ // Do not use negative numbers or zero; clearcut only handles positive integers.
+ private static final int DRAWER_OPENED_HAMBURGER = 1;
+ private static final int DRAWER_OPENED_SWIPE = 2;
+
+ @IntDef(flag = true, value = {
+ DRAWER_OPENED_HAMBURGER,
+ DRAWER_OPENED_SWIPE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DrawerTrigger {}
/**
* Logs when DocumentsUI is started, and how. Call this when DocumentsUI first starts up.
@@ -285,6 +391,20 @@
}
/**
+ * Logs a drawer opened event. Call this when the user opens drawer by swipe or by clicking the
+ * hamburger icon.
+ * @param context
+ * @param trigger type of action that opened the drawer
+ */
+ public static void logDrawerOpened(Context context, @DrawerController.Trigger int trigger) {
+ if (trigger == DrawerController.OPENED_HAMBURGER) {
+ logHistogram(context, COUNT_DRAWER_OPENED, DRAWER_OPENED_HAMBURGER);
+ } else if (trigger == DrawerController.OPENED_SWIPE) {
+ logHistogram(context, COUNT_DRAWER_OPENED, DRAWER_OPENED_SWIPE);
+ }
+ }
+
+ /**
* Logs file operation stats. Call this when a file operation has completed. The given
* DocumentInfo is only used to distinguish broad categories of actions (e.g. copying from one
* provider to another vs copying within a given provider). No PII is logged.
@@ -315,6 +435,28 @@
}
/**
+ * Logs create directory operation. It is a part of file operation stats. We do not
+ * differentiate between internal and external locations, all create directory operations are
+ * logged under COUNT_FILEOP_SYSTEM. Call this when a create directory operation has completed.
+ *
+ * @param context
+ */
+ public static void logCreateDirOperation(Context context) {
+ logHistogram(context, COUNT_FILEOP_SYSTEM, FILEOP_CREATE_DIR);
+ }
+
+ /**
+ * Logs rename file operation. It is a part of file operation stats. We do not differentiate
+ * between internal and external locations, all rename operations are logged under
+ * COUNT_FILEOP_SYSTEM. Call this when a rename file operation has completed.
+ *
+ * @param context
+ */
+ public static void logRenameFileOperation(Context context) {
+ logHistogram(context, COUNT_FILEOP_SYSTEM, FILEOP_RENAME);
+ }
+
+ /**
* Logs some kind of file operation error. Call this when a file operation (e.g. copy, delete)
* fails.
*
@@ -347,7 +489,29 @@
}
/**
- * Log the cancellation of a file operation. Call this when a Job is canceled.
+ * Logs create directory operation error. We do not differentiate between internal and external
+ * locations, all create directory errors are logged under COUNT_FILEOP_SYSTEM. Call this when a
+ * create directory operation fails.
+ *
+ * @param context
+ */
+ public static void logCreateDirError(Context context) {
+ logHistogram(context, COUNT_FILEOP_SYSTEM, FILEOP_CREATE_DIR);
+ }
+
+ /**
+ * Logs rename file operation error. We do not differentiate between internal and external
+ * locations, all rename errors are logged under COUNT_FILEOP_SYSTEM. Call this
+ * when a rename file operation fails.
+ *
+ * @param context
+ */
+ public static void logRenameFileError(Context context) {
+ logHistogram(context, COUNT_FILEOP_SYSTEM, FILEOP_RENAME_ERROR);
+ }
+
+ /**
+ * Logs the cancellation of a file operation. Call this when a Job is canceled.
* @param context
* @param operationType
*/
@@ -355,6 +519,44 @@
logHistogram(context, COUNT_FILEOP_CANCELED, toMetricsOpType(operationType));
}
+ /**
+ * Logs keyboard shortcut actions. Since keyboard shortcuts have their corresponding menu items,
+ * they are identified by menu item resource id for convenience.
+ * @param context
+ * @param keyCode
+ */
+ public static void logKeyboardAction(Context context, @KeyboardAction int action) {
+ logHistogram(context, COUNT_KEYBOARD_ACTION, action);
+ }
+
+ /**
+ * Logs startup time in milliseconds.
+ * @param context
+ * @param startupMs Startup time in milliseconds.
+ */
+ public static void logStartupMs(Context context, int startupMs) {
+ logHistogram(context, COUNT_STARTUP_MS, startupMs);
+ }
+
+ /**
+ * Logs a drag and drop action. Call this when the user drops the content triggering copy.
+ * operation.
+ *
+ * @param context
+ */
+ public static void logDragNDrop(Context context) {
+ logCount(context, COUNT_DRAG_N_DROP);
+ }
+
+ /**
+ * Logs a search. Call this when the search operation is finished.
+ *
+ * @param context
+ */
+ public static void logSearch(Context context) {
+ logCount(context, COUNT_SEARCH);
+ }
+
private static void logInterProviderFileOps(
Context context,
String histogram,
@@ -475,6 +677,74 @@
}
/**
+ * Logs menu action that was selected by user.
+ * @param context
+ * @param id Resource id of the menu item.
+ */
+ public static void logMenuAction(Context context, int id) {
+ @MenuAction int menuAction = ACTION_MENU_OTHER;
+ switch (id) {
+ case R.id.menu_grid:
+ menuAction = ACTION_MENU_GRID;
+ break;
+ case R.id.menu_list:
+ menuAction = ACTION_MENU_LIST;
+ break;
+ case R.id.menu_sort:
+ menuAction = ACTION_MENU_SORT;
+ break;
+ case R.id.menu_sort_name:
+ menuAction = ACTION_MENU_SORT_NAME;
+ break;
+ case R.id.menu_sort_date:
+ menuAction = ACTION_MENU_SORT_DATE;
+ break;
+ case R.id.menu_sort_size:
+ menuAction = ACTION_MENU_SORT_SIZE;
+ break;
+ case R.id.menu_search:
+ menuAction = ACTION_MENU_SEARCH;
+ break;
+ case R.id.menu_file_size:
+ menuAction = ACTION_MENU_SHOW_SIZE;
+ break;
+ case R.id.menu_settings:
+ menuAction = ACTION_MENU_SETTINGS;
+ break;
+ case R.id.menu_copy_to:
+ menuAction = ACTION_MENU_COPY_TO;
+ break;
+ case R.id.menu_move_to:
+ menuAction = ACTION_MENU_MOVE_TO;
+ break;
+ case R.id.menu_delete:
+ menuAction = ACTION_MENU_DELETE;
+ break;
+ case R.id.menu_rename:
+ menuAction = ACTION_MENU_RENAME;
+ break;
+ case R.id.menu_create_dir:
+ menuAction = ACTION_MENU_CREATE_DIR;
+ break;
+ case R.id.menu_select_all:
+ menuAction = ACTION_MENU_SELECT_ALL;
+ break;
+ case R.id.menu_share:
+ menuAction = ACTION_MENU_SHARE;
+ break;
+ case R.id.menu_open:
+ menuAction = ACTION_MENU_OPEN;
+ break;
+ case R.id.menu_advanced:
+ menuAction = ACTION_MENU_ADVANCED;
+ break;
+ default:
+ break;
+ }
+ logHistogram(context, COUNT_MENU_ACTION, menuAction);
+ }
+
+ /**
* Internal method for making a MetricsLogger.count call. Increments the given counter by 1.
*
* @param context
@@ -492,7 +762,7 @@
* @param name The name of the histogram.
* @param bucket The bucket to increment.
*/
- private static void logHistogram(Context context, String name, int bucket) {
+ private static void logHistogram(Context context, String name, @ActionType int bucket) {
if (DEBUG) Log.d(TAG, name + ": " + bucket);
MetricsLogger.histogram(context, name, bucket);
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java b/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java
index 30c1020..f6fe47b 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java
@@ -91,7 +91,7 @@
private void onNavigationIconClicked() {
if (mDrawer.isPresent()) {
- mDrawer.setOpen(true);
+ mDrawer.setOpen(true, DrawerController.OPENED_HAMBURGER);
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/OpenExternalDirectoryActivity.java b/packages/DocumentsUI/src/com/android/documentsui/OpenExternalDirectoryActivity.java
index 2fe2756..854be0b 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/OpenExternalDirectoryActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/OpenExternalDirectoryActivity.java
@@ -86,6 +86,7 @@
private static final String EXTRA_VOLUME_LABEL = "com.android.documentsui.VOLUME_LABEL";
private static final String EXTRA_VOLUME_UUID = "com.android.documentsui.VOLUME_UUID";
private static final String EXTRA_IS_ROOT = "com.android.documentsui.IS_ROOT";
+ private static final String EXTRA_IS_PRIMARY = "com.android.documentsui.IS_PRIMARY";
// Special directory name representing the full volume
static final String DIRECTORY_ROOT = "ROOT_DIRECTORY";
@@ -157,6 +158,13 @@
Log.d(TAG, "showFragment() for volume " + storageVolume.dump() + ", directory "
+ directoryName + ", and user " + userId);
final boolean isRoot = directoryName.equals(DIRECTORY_ROOT);
+ final boolean isPrimary = storageVolume.isPrimary();
+
+ if (isRoot && isPrimary) {
+ if (DEBUG) Log.d(TAG, "root access requested on primary volume");
+ return false;
+ }
+
final File volumeRoot = storageVolume.getPathFile();
File file;
try {
@@ -235,6 +243,7 @@
args.putString(EXTRA_VOLUME_UUID, volumeUuid);
args.putString(EXTRA_APP_LABEL, appLabel);
args.putBoolean(EXTRA_IS_ROOT, isRoot);
+ args.putBoolean(EXTRA_IS_PRIMARY, isPrimary);
final FragmentManager fm = activity.getFragmentManager();
final FragmentTransaction ft = fm.beginTransaction();
@@ -352,6 +361,7 @@
private String mVolumeLabel;
private String mAppLabel;
private boolean mIsRoot;
+ private boolean mIsPrimary;
private CheckBox mDontAskAgain;
private OpenExternalDirectoryActivity mActivity;
private AlertDialog mDialog;
@@ -367,6 +377,7 @@
mVolumeLabel = args.getString(EXTRA_VOLUME_LABEL);
mAppLabel = args.getString(EXTRA_APP_LABEL);
mIsRoot = args.getBoolean(EXTRA_IS_ROOT);
+ mIsPrimary= args.getBoolean(EXTRA_IS_PRIMARY);
}
mActivity = (OpenExternalDirectoryActivity) getActivity();
}
@@ -435,7 +446,9 @@
message = TextUtils.expandTemplate(getText(
R.string.open_external_dialog_root_request), mAppLabel, mVolumeLabel);
} else {
- message = TextUtils.expandTemplate(getText(R.string.open_external_dialog_request),
+ message = TextUtils.expandTemplate(
+ getText(mIsPrimary ? R.string.open_external_dialog_request_primary_volume
+ : R.string.open_external_dialog_request),
mAppLabel, directory, mVolumeLabel);
}
final TextView messageField = (TextView) view.findViewById(R.id.message);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
index 6efe9c8..594e02f 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
@@ -65,8 +65,6 @@
private static final String TAG = "RootsCache";
- private static final boolean ENABLE_SYSTEM_CACHE = true;
-
private final Context mContext;
private final ContentObserver mObserver;
private OnCacheUpdateListener mCacheUpdateListener;
@@ -124,7 +122,7 @@
/**
* Gather roots from all known storage providers.
*/
- public void updateAsync() {
+ public void updateAsync(boolean forceRefreshAll) {
// NOTE: This method is called when the UI language changes.
// For that reason we update our RecentsRoot to reflect
@@ -141,14 +139,15 @@
| Root.FLAG_SUPPORTS_CREATE));
assert(mRecentsRoot.availableBytes == -1);
- new UpdateTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ new UpdateTask(forceRefreshAll, null)
+ .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
/**
* Gather roots from storage providers belonging to given package name.
*/
public void updatePackageAsync(String packageName) {
- new UpdateTask(packageName).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ new UpdateTask(false, packageName).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
/**
@@ -200,7 +199,7 @@
synchronized (mLock) {
for (String authority : mStoppedAuthorities) {
if (DEBUG) Log.d(TAG, "Loading stopped authority " + authority);
- mRoots.putAll(authority, loadRootsForAuthority(resolver, authority));
+ mRoots.putAll(authority, loadRootsForAuthority(resolver, authority, true));
}
mStoppedAuthorities.clear();
}
@@ -219,44 +218,35 @@
if (DEBUG) {
Log.d(TAG, "Loading stopped authority " + authority);
}
- mRoots.putAll(authority, loadRootsForAuthority(resolver, authority));
+ mRoots.putAll(authority, loadRootsForAuthority(resolver, authority, true));
mStoppedAuthorities.remove(authority);
}
}
private class UpdateTask extends AsyncTask<Void, Void, Void> {
- private final String mFilterPackage;
+ private final boolean mForceRefreshAll;
+ private final String mForceRefreshPackage;
private final Multimap<String, RootInfo> mTaskRoots = ArrayListMultimap.create();
private final HashSet<String> mTaskStoppedAuthorities = new HashSet<>();
/**
- * Update all roots.
+ * Create task to update roots cache.
+ *
+ * @param forceRefreshAll when true, all previously cached values for
+ * all packages should be ignored.
+ * @param forceRefreshPackage when non-null, all previously cached
+ * values for this specific package should be ignored.
*/
- public UpdateTask() {
- this(null);
- }
-
- /**
- * Only update roots belonging to given package name. Other roots will
- * be copied from cached {@link #mRoots} values.
- */
- public UpdateTask(String filterPackage) {
- mFilterPackage = filterPackage;
+ public UpdateTask(boolean forceRefreshAll, String forceRefreshPackage) {
+ mForceRefreshAll = forceRefreshAll;
+ mForceRefreshPackage = forceRefreshPackage;
}
@Override
protected Void doInBackground(Void... params) {
final long start = SystemClock.elapsedRealtime();
- if (mFilterPackage != null) {
- // We must have previously cached values to fill in non-matching
- // packages, so wait around for successful first load.
- if (!waitForFirstLoad()) {
- return null;
- }
- }
-
mTaskRoots.put(mRecentsRoot.authority, mRecentsRoot);
final ContentResolver resolver = mContext.getContentResolver();
@@ -302,29 +292,18 @@
return;
}
- // Try using cached roots if filtering
- boolean cacheHit = false;
- if (mFilterPackage != null && !mFilterPackage.equals(info.packageName)) {
- synchronized (mLock) {
- if (mTaskRoots.putAll(info.authority, mRoots.get(info.authority))) {
- if (DEBUG) Log.d(TAG, "Used cached roots for " + info.authority);
- cacheHit = true;
- }
- }
- }
-
- // Cache miss, or loading everything
- if (!cacheHit) {
- mTaskRoots.putAll(info.authority,
- loadRootsForAuthority(mContext.getContentResolver(), info.authority));
- }
+ final boolean forceRefresh = mForceRefreshAll
+ || Objects.equals(info.packageName, mForceRefreshPackage);
+ mTaskRoots.putAll(info.authority, loadRootsForAuthority(mContext.getContentResolver(),
+ info.authority, forceRefresh));
}
}
/**
* Bring up requested provider and query for all active roots.
*/
- private Collection<RootInfo> loadRootsForAuthority(ContentResolver resolver, String authority) {
+ private Collection<RootInfo> loadRootsForAuthority(ContentResolver resolver, String authority,
+ boolean forceRefresh) {
if (DEBUG) Log.d(TAG, "Loading roots for " + authority);
synchronized (mObservedAuthorities) {
@@ -336,7 +315,7 @@
}
final Uri rootsUri = DocumentsContract.buildRootsUri(authority);
- if (ENABLE_SYSTEM_CACHE) {
+ if (!forceRefresh) {
// Look for roots data that we might have cached for ourselves in the
// long-lived system process.
final Bundle systemCache = resolver.getCache(rootsUri);
@@ -363,14 +342,12 @@
ContentProviderClient.releaseQuietly(client);
}
- if (ENABLE_SYSTEM_CACHE) {
- // Cache these freshly parsed roots over in the long-lived system
- // process, in case our process goes away. The system takes care of
- // invalidating the cache if the package or Uri changes.
- final Bundle systemCache = new Bundle();
- systemCache.putParcelableArrayList(TAG, roots);
- resolver.putCache(rootsUri, systemCache);
- }
+ // Cache these freshly parsed roots over in the long-lived system
+ // process, in case our process goes away. The system takes care of
+ // invalidating the cache if the package or Uri changes.
+ final Bundle systemCache = new Bundle();
+ systemCache.putParcelableArrayList(TAG, roots);
+ resolver.putCache(rootsUri, systemCache);
return roots;
}
@@ -384,8 +361,8 @@
synchronized (mLock) {
RootInfo root = getRootLocked(authority, rootId);
if (root == null) {
- mRoots.putAll(
- authority, loadRootsForAuthority(mContext.getContentResolver(), authority));
+ mRoots.putAll(authority,
+ loadRootsForAuthority(mContext.getContentResolver(), authority, false));
root = getRootLocked(authority, rootId);
}
return root;
@@ -493,6 +470,11 @@
continue;
}
+ if (!state.showAdvanced && root.isAdvanced()) {
+ if (DEBUG) Log.d(TAG, "Excluding root because: unwanted advanced device.");
+ continue;
+ }
+
if (state.localOnly && !root.isLocalOnly()) {
if (DEBUG) Log.d(TAG, "Excluding root because: unwanted non-local device.");
continue;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
index 35da8cc..8bbcc30 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
@@ -18,6 +18,7 @@
import static com.android.documentsui.Shared.DEBUG;
+import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
@@ -117,7 +118,7 @@
Intent handlerAppIntent = getArguments().getParcelable(EXTRA_INCLUDE_APPS);
- mAdapter = new RootsAdapter(context, result, handlerAppIntent);
+ mAdapter = new RootsAdapter(context, result, handlerAppIntent, state);
mList.setAdapter(mAdapter);
onCurrentRootChanged();
@@ -308,8 +309,8 @@
* @param handlerAppIntent When not null, apps capable of handling the original
* intent will be included in list of roots (in special section at bottom).
*/
- public RootsAdapter(
- Context context, Collection<RootInfo> roots, @Nullable Intent handlerAppIntent) {
+ public RootsAdapter(Context context, Collection<RootInfo> roots,
+ @Nullable Intent handlerAppIntent, State state) {
super(context, 0);
final List<RootItem> libraries = new ArrayList<>();
@@ -318,9 +319,8 @@
for (final RootInfo root : roots) {
final RootItem item = new RootItem(root);
- if (root.isHome() && Shared.isHomeRootHidden(context)) {
- continue;
- } else if (root.isAdvanced() && Shared.areAdvancedRootsHidden(context)) {
+ if (root.isHome() &&
+ !Shared.shouldShowDocumentsRoot(context, ((Activity) context).getIntent())) {
continue;
} else if (root.isLibrary()) {
if (DEBUG) Log.d(TAG, "Adding " + root + " as library.");
diff --git a/packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java b/packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java
index 4d0ba4b..945ed34 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java
@@ -23,7 +23,9 @@
import android.provider.DocumentsContract.Root;
import android.text.TextUtils;
import android.util.Log;
+import android.view.Menu;
import android.view.MenuItem;
+import android.view.MenuItem.OnActionExpandListener;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
@@ -36,10 +38,12 @@
* Manages searching UI behavior.
*/
final class SearchViewManager implements
- SearchView.OnCloseListener, OnQueryTextListener, OnClickListener, OnFocusChangeListener {
+ SearchView.OnCloseListener, OnQueryTextListener, OnClickListener, OnFocusChangeListener,
+ OnActionExpandListener {
public interface SearchManagerListener {
void onSearchChanged(@Nullable String query);
+ void onSearchFinished();
}
public static final String TAG = "SearchManger";
@@ -48,10 +52,11 @@
private boolean mSearchExpanded;
private String mCurrentSearch;
private boolean mIgnoreNextClose;
+ private boolean mFullBar;
private DocumentsToolbar mActionBar;
- private MenuItem mMenu;
- private SearchView mView;
+ private MenuItem mMenuItem;
+ private SearchView mSearchView;
public SearchViewManager(SearchManagerListener listener, @Nullable Bundle savedState) {
mListener = listener;
@@ -62,45 +67,61 @@
mListener = listener;
}
- public void install(DocumentsToolbar actionBar) {
- // assert(mActionBar == null);
-
+ public void install(DocumentsToolbar actionBar, boolean isFullBarSearch) {
mActionBar = actionBar;
- mMenu = actionBar.getSearchMenu();
- mView = (SearchView) mMenu.getActionView();
+ mMenuItem = actionBar.getSearchMenu();
+ mSearchView = (SearchView) mMenuItem.getActionView();
- mView.setOnQueryTextListener(this);
- mView.setOnCloseListener(this);
- mView.setOnSearchClickListener(this);
- mView.setOnQueryTextFocusChangeListener(this);
+ mSearchView.setOnQueryTextListener(this);
+ mSearchView.setOnCloseListener(this);
+ mSearchView.setOnSearchClickListener(this);
+ mSearchView.setOnQueryTextFocusChangeListener(this);
+
+ mFullBar = isFullBarSearch;
+ if (mFullBar) {
+ mMenuItem.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW
+ | MenuItem.SHOW_AS_ACTION_ALWAYS);
+ mMenuItem.setOnActionExpandListener(this);
+ }
restoreSearch();
}
/**
+ * Used to hide menu icons, when the search is being restored. Needed because search restoration
+ * is done before onPrepareOptionsMenu(Menu menu) that is overriding the icons visibility.
+ */
+ public void updateMenu() {
+ if (isSearching() && mFullBar) {
+ Menu menu = mActionBar.getMenu();
+ menu.setGroupVisible(R.id.group_hide_when_searching, false);
+ }
+ }
+
+ /**
* @param root Info about the current directory.
*/
void update(RootInfo root) {
- if (mMenu == null) {
+ if (mMenuItem == null) {
if (DEBUG) Log.d(TAG, "update called before Search MenuItem installed.");
return;
}
if (mCurrentSearch != null) {
- mMenu.expandActionView();
+ mMenuItem.expandActionView();
- mView.setIconified(false);
- mView.clearFocus();
- mView.setQuery(mCurrentSearch, false);
+ mSearchView.setIconified(false);
+ mSearchView.clearFocus();
+ mSearchView.setQuery(mCurrentSearch, false);
} else {
- mView.clearFocus();
- if (!mView.isIconified()) {
+ mSearchView.clearFocus();
+ if (!mSearchView.isIconified()) {
mIgnoreNextClose = true;
- mView.setIconified(true);
+ mSearchView.setIconified(true);
}
- if (mMenu.isActionViewExpanded()) {
- mMenu.collapseActionView();
+ if (mMenuItem.isActionViewExpanded()) {
+ mMenuItem.collapseActionView();
}
}
@@ -109,7 +130,7 @@
}
void showMenu(boolean visible) {
- if (mMenu == null) {
+ if (mMenuItem == null) {
if (DEBUG) Log.d(TAG, "showMenu called before Search MenuItem installed.");
return;
}
@@ -118,7 +139,7 @@
mCurrentSearch = null;
}
- mMenu.setVisible(visible);
+ mMenuItem.setVisible(visible);
}
/**
@@ -129,46 +150,49 @@
boolean cancelSearch() {
if (isExpanded() || isSearching()) {
// If the query string is not empty search view won't get iconified
- mView.setQuery("", false);
- // Causes calling onClose(). onClose() is triggering directory content update.
- mView.setIconified(true);
+ mSearchView.setQuery("", false);
+
+ if (mFullBar) {
+ onClose();
+ } else {
+ // Causes calling onClose(). onClose() is triggering directory content update.
+ mSearchView.setIconified(true);
+ }
return true;
}
return false;
}
+ /**
+ * Sets search view into the searching state. Used to restore state after device orientation
+ * change.
+ */
private void restoreSearch() {
if (isSearching()) {
+ if(mFullBar) {
+ mMenuItem.expandActionView();
+ } else {
+ mSearchView.setIconified(false);
+ }
onSearchExpanded();
- mView.setIconified(false);
- mView.setQuery(mCurrentSearch, false);
- mView.clearFocus();
+ mSearchView.setQuery(mCurrentSearch, false);
+ mSearchView.clearFocus();
}
}
private void onSearchExpanded() {
mSearchExpanded = true;
- }
-
- boolean isSearching() {
- return mCurrentSearch != null;
- }
-
- boolean isExpanded() {
- return mSearchExpanded;
+ if(mFullBar) {
+ Menu menu = mActionBar.getMenu();
+ menu.setGroupVisible(R.id.group_hide_when_searching, false);
+ } else {
+ // If search in full-bar mode it will be logged in FilesActivity#onOptionsItemSelected
+ Metrics.logMenuAction(mActionBar.getContext(), R.id.menu_search);
+ }
}
/**
- * Called when owning activity is saving state to be used to restore state during creation.
- * @param state Bundle to save state too
- */
- public void onSaveInstanceState(Bundle state) {
- state.putString(Shared.EXTRA_QUERY, mCurrentSearch);
- }
-
- /**
- * Clears the search. Clears the SearchView background color. Triggers refreshing of the
- * directory content.
+ * Clears the search. Triggers refreshing of the directory content.
* @return True if the default behavior of clearing/dismissing SearchView should be overridden.
* False otherwise.
*/
@@ -187,13 +211,26 @@
mListener.onSearchChanged(mCurrentSearch);
}
}
+
+ if(mFullBar) {
+ mMenuItem.collapseActionView();
+ }
+ mListener.onSearchFinished();
+
return false;
}
/**
- * Sets mSearchExpanded. Called when search icon is clicked to start search. Used to detect when
- * the view expanded instead of onMenuItemActionExpand, because SearchView has showAsAction set
- * to always and onMenuItemAction* methods are not called.
+ * Called when owning activity is saving state to be used to restore state during creation.
+ * @param state Bundle to save state too
+ */
+ public void onSaveInstanceState(Bundle state) {
+ state.putString(Shared.EXTRA_QUERY, mCurrentSearch);
+ }
+
+ /**
+ * Sets mSearchExpanded. Called when search icon is clicked to start search for both search view
+ * modes.
*/
@Override
public void onClick(View v) {
@@ -203,19 +240,22 @@
@Override
public boolean onQueryTextSubmit(String query) {
mCurrentSearch = query;
- mView.clearFocus();
+ mSearchView.clearFocus();
if (mListener != null) {
mListener.onSearchChanged(mCurrentSearch);
}
return true;
}
+ /**
+ * Used to detect and handle back button pressed event when search is expanded.
+ */
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus) {
if (mCurrentSearch == null) {
- mView.setIconified(true);
- } else if (TextUtils.isEmpty(mView.getQuery())) {
+ mSearchView.setIconified(true);
+ } else if (TextUtils.isEmpty(mSearchView.getQuery())) {
cancelSearch();
}
}
@@ -226,8 +266,34 @@
return false;
}
+ @Override
+ public boolean onMenuItemActionCollapse(MenuItem item) {
+ Menu menu = mActionBar.getMenu();
+ menu.setGroupVisible(R.id.group_hide_when_searching, true);
+
+ // Handles case when search view is collapsed by using the arrow on the left of the bar
+ if (isExpanded() || isSearching()) {
+ cancelSearch();
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onMenuItemActionExpand(MenuItem item) {
+ return true;
+ }
+
String getCurrentSearch() {
return mCurrentSearch;
}
+ boolean isSearching() {
+ return mCurrentSearch != null;
+ }
+
+ boolean isExpanded() {
+ return mSearchExpanded;
+ }
+
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Shared.java b/packages/DocumentsUI/src/com/android/documentsui/Shared.java
index d21afee..1ba836a 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Shared.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Shared.java
@@ -16,13 +16,15 @@
package com.android.documentsui;
+import android.app.AlertDialog;
import android.content.Context;
+import android.content.Intent;
import android.content.res.Configuration;
+import android.provider.DocumentsContract;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.text.format.Time;
import android.view.WindowManager;
-import android.app.AlertDialog;
import java.text.Collator;
import java.util.ArrayList;
@@ -40,10 +42,21 @@
"com.android.documentsui.PICK_COPY_DESTINATION";
/**
+ * Extra flag allowing app to be opened in productivity mode (less downloadsy).
+ * Useful developers and the likes. When set to true overrides the default
+ * config value of productivity_device.
+ */
+ public static final String EXTRA_PRODUCTIVITY_MODE = "com.android.documentsui.PRODUCTIVITY";
+
+ /**
* Extra boolean flag for {@link ACTION_PICK_COPY_DESTINATION}, which
* specifies if the destination directory needs to create new directory or not.
*/
public static final String EXTRA_DIRECTORY_COPY = "com.android.documentsui.DIRECTORY_COPY";
+
+ /**
+ * Extra flag used to store the current stack so user opens in right spot.
+ */
public static final String EXTRA_STACK = "com.android.documentsui.STACK";
/**
@@ -170,17 +183,33 @@
}
/*
- * Indicates if the home directory should be hidden in the roots list.
+ * Returns true if app is running in "productivity mode".
*/
- public static boolean isHomeRootHidden(Context context) {
- return context.getResources().getBoolean(R.bool.home_root_hidden);
+ public static boolean productivityMode(Context context) {
+ return context.getResources().getBoolean(R.bool.productivity_device);
}
/*
- * Indicates if the advanced roots should be hidden.
+ * Returns true if app is running in "productivity mode".
*/
- public static boolean areAdvancedRootsHidden(Context context) {
- return context.getResources().getBoolean(R.bool.advanced_roots_hidden);
+ private static boolean isProductivityMode(Context context, Intent intent) {
+ return intent.getBooleanExtra(
+ Shared.EXTRA_PRODUCTIVITY_MODE,
+ context.getResources().getBoolean(R.bool.productivity_device));
}
+ /*
+ * Returns true if "Documents" root should be shown.
+ */
+ public static boolean shouldShowDocumentsRoot(Context context, Intent intent) {
+ return isProductivityMode(context, intent);
+ }
+
+ /*
+ * Returns true if device root should be shown.
+ */
+ public static boolean shouldShowDeviceRoot(Context context, Intent intent) {
+ return isProductivityMode(context, intent)
+ || intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, false);
+ }
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/State.java b/packages/DocumentsUI/src/com/android/documentsui/State.java
index 16b7660..f239eb4 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/State.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/State.java
@@ -43,10 +43,19 @@
private static final String TAG = "State";
+ @IntDef(flag = true, value = {
+ ACTION_BROWSE,
+ ACTION_PICK_COPY_DESTINATION,
+ ACTION_OPEN,
+ ACTION_CREATE,
+ ACTION_GET_CONTENT,
+ ACTION_OPEN_TREE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ActionType {}
// File manager and related private picking activity.
public static final int ACTION_BROWSE = 1;
public static final int ACTION_PICK_COPY_DESTINATION = 2;
-
// All public picking activities
public static final int ACTION_OPEN = 3;
public static final int ACTION_CREATE = 4;
@@ -69,7 +78,7 @@
public static final int SORT_ORDER_LAST_MODIFIED = 2;
public static final int SORT_ORDER_SIZE = 3;
- public int action;
+ public @ActionType int action;
public String[] acceptMimes;
/** Derived from local preferences */
@@ -84,6 +93,8 @@
public boolean forceSize;
public boolean showSize;
public boolean localOnly;
+ public boolean showAdvancedOption;
+ public boolean showAdvanced;
public boolean restored;
/*
* Indicates handler was an external app, like photos.
@@ -112,9 +123,6 @@
/** Instance state for every shown directory */
public HashMap<String, SparseArray<Parcelable>> dirState = new HashMap<>();
- /** UI selection */
- public Selection selectedDocuments = new Selection();
-
/** Currently copying file */
public List<DocumentInfo> selectedDocumentsForCopy = new ArrayList<>();
@@ -185,11 +193,12 @@
out.writeInt(forceSize ? 1 : 0);
out.writeInt(showSize ? 1 : 0);
out.writeInt(localOnly ? 1 : 0);
+ out.writeInt(showAdvancedOption ? 1 : 0);
+ out.writeInt(showAdvanced ? 1 : 0);
out.writeInt(restored ? 1 : 0);
out.writeInt(external ? 1 : 0);
DurableUtils.writeToParcel(out, stack);
out.writeMap(dirState);
- out.writeParcelable(selectedDocuments, 0);
out.writeList(selectedDocumentsForCopy);
out.writeList(excludedAuthorities);
out.writeInt(openableOnly ? 1 : 0);
@@ -214,11 +223,12 @@
state.forceSize = in.readInt() != 0;
state.showSize = in.readInt() != 0;
state.localOnly = in.readInt() != 0;
+ state.showAdvancedOption = in.readInt() != 0;
+ state.showAdvanced = in.readInt() != 0;
state.restored = in.readInt() != 0;
state.external = in.readInt() != 0;
DurableUtils.readFromParcel(in, state.stack);
in.readMap(state.dirState, loader);
- state.selectedDocuments = in.readParcelable(loader);
in.readList(state.selectedDocumentsForCopy, loader);
in.readList(state.excludedAuthorities, loader);
state.openableOnly = in.readInt() != 0;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 062f2d1..1c85a8a 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -40,10 +40,12 @@
import android.database.Cursor;
import android.graphics.Canvas;
import android.graphics.Point;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
+import android.os.Parcel;
import android.os.Parcelable;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
@@ -72,8 +74,8 @@
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
-import android.widget.Toolbar;
import android.widget.TextView;
+import android.widget.Toolbar;
import com.android.documentsui.BaseActivity;
import com.android.documentsui.DirectoryLoader;
@@ -85,6 +87,7 @@
import com.android.documentsui.Events.MotionInputEvent;
import com.android.documentsui.Menus;
import com.android.documentsui.MessageBar;
+import com.android.documentsui.Metrics;
import com.android.documentsui.R;
import com.android.documentsui.RecentsLoader;
import com.android.documentsui.RootsCache;
@@ -105,6 +108,7 @@
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Iterator;
import java.util.List;
import java.util.Objects;
@@ -223,7 +227,8 @@
mStateKey = buildStateKey(mRoot, mDocument);
mQuery = args.getString(Shared.EXTRA_QUERY);
mType = args.getInt(Shared.EXTRA_TYPE);
- mSelection = args.getParcelable(Shared.EXTRA_SELECTION);
+ final Selection selection = args.getParcelable(Shared.EXTRA_SELECTION);
+ mSelection = selection != null ? selection : new Selection();
mSearchMode = args.getBoolean(Shared.EXTRA_SEARCH_MODE);
mIconHelper = new IconHelper(context, MODE_GRID);
@@ -287,14 +292,25 @@
outState.putParcelable(Shared.EXTRA_ROOT, mRoot);
outState.putParcelable(Shared.EXTRA_DOC, mDocument);
outState.putString(Shared.EXTRA_QUERY, mQuery);
- outState.putParcelable(Shared.EXTRA_SELECTION, mSelection);
- outState.putBoolean(Shared.EXTRA_SEARCH_MODE, mSearchMode);
+ // Workaround. To avoid crash, write only up to 512 KB of selection.
+ // If more files are selected, then the selection will be lost.
+ final Parcel parcel = Parcel.obtain();
+ try {
+ mSelection.writeToParcel(parcel, 0);
+ if (parcel.dataSize() <= 512 * 1024) {
+ outState.putParcelable(Shared.EXTRA_SELECTION, mSelection);
+ }
+ } finally {
+ parcel.recycle();
+ }
+
+ outState.putBoolean(Shared.EXTRA_SEARCH_MODE, mSearchMode);
}
@Override
public void onActivityResult(@RequestCode int requestCode, int resultCode, Intent data) {
- switch(requestCode) {
+ switch (requestCode) {
case REQUEST_COPY_DESTINATION:
handleCopyResult(resultCode, data);
break;
@@ -487,24 +503,19 @@
@Override
public void onSelectionChanged() {
mSelectionManager.getSelection(mSelected);
- TypedValue color = new TypedValue();
if (mSelected.size() > 0) {
if (DEBUG) Log.d(TAG, "Maybe starting action mode.");
if (mActionMode == null) {
if (DEBUG) Log.d(TAG, "Yeah. Starting action mode.");
mActionMode = getActivity().startActionMode(this);
}
- getActivity().getTheme().resolveAttribute(R.attr.colorActionMode, color, true);
updateActionMenu();
} else {
if (DEBUG) Log.d(TAG, "Finishing action mode.");
if (mActionMode != null) {
mActionMode.finish();
}
- getActivity().getTheme().resolveAttribute(
- android.R.attr.colorPrimaryDark, color, true);
}
- getActivity().getWindow().setStatusBarColor(color.data);
if (mActionMode != null) {
final String title = Shared.getQuantityString(getActivity(),
@@ -594,6 +605,7 @@
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ Metrics.logMenuAction(getContext(), item.getItemId());
Selection selection = mSelectionManager.getSelection(new Selection());
@@ -1049,7 +1061,7 @@
}
// Can't copy folders to downloads, because we don't show folders there.
- if (!root.isDownloads()) {
+ if (root.isDownloads()) {
for (DocumentInfo docs : files) {
if (docs.isDirectory()) {
return false;
@@ -1061,8 +1073,19 @@
}
public void selectAllFiles() {
+ // Exclude disabled files
+ List<String> enabled = new ArrayList<String>();
+ for (String id : mAdapter.getModelIds()) {
+ Cursor cursor = getModel().getItem(id);
+ String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
+ int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
+ if (isDocumentEnabled(docMimeType, docFlags)) {
+ enabled.add(id);
+ }
+ }
+
// Only select things currently visible in the adapter.
- boolean changed = mSelectionManager.setItemsSelected(mAdapter.getModelIds(), true);
+ boolean changed = mSelectionManager.setItemsSelected(enabled, true);
if (changed) {
updateDisplayState();
}
@@ -1084,7 +1107,7 @@
}
// Make all items draggable.
- view.setOnLongClickListener(mDragHelper);
+ view.setOnLongClickListener(onLongClickListener);
}
private View.OnDragListener mOnDragListener = new View.OnDragListener() {
@@ -1124,6 +1147,7 @@
if (Objects.equals(src, dst)) {
return false;
}
+ Metrics.logDragNDrop(getContext());
copyFromClipData(event.getClipData(), dst);
return true;
}
@@ -1214,41 +1238,68 @@
DocumentInfo.fromDirectoryCursor(cursor));
}
- private Drawable getDragShadowIcon(List<DocumentInfo> docs) {
- if (docs.size() == 1) {
- final DocumentInfo doc = docs.get(0);
- return mIconHelper.getDocumentIcon(getActivity(), doc.authority, doc.documentId,
- doc.mimeType, doc.icon);
+ private static class DragShadowBuilder extends View.DragShadowBuilder {
+
+ private final Context mContext;
+ private final IconHelper mIconHelper;
+ private final LayoutInflater mInflater;
+ private final View mShadowView;
+ private final TextView mTitle;
+ private final ImageView mIcon;
+ private final int mWidth;
+ private final int mHeight;
+
+ public DragShadowBuilder(Context context, IconHelper iconHelper, List<DocumentInfo> docs) {
+ mContext = context;
+ mIconHelper = iconHelper;
+ mInflater = LayoutInflater.from(context);
+
+ mWidth = mContext.getResources().getDimensionPixelSize(R.dimen.drag_shadow_width);
+ mHeight= mContext.getResources().getDimensionPixelSize(R.dimen.drag_shadow_height);
+
+ mShadowView = mInflater.inflate(R.layout.drag_shadow_layout, null);
+ mTitle = (TextView) mShadowView.findViewById(android.R.id.title);
+ mIcon = (ImageView) mShadowView.findViewById(android.R.id.icon);
+
+ mTitle.setText(getTitle(docs));
+ mIcon.setImageDrawable(getIcon(docs));
}
- return getActivity().getDrawable(R.drawable.ic_doc_generic);
- }
- private class DrawableShadowBuilder extends View.DragShadowBuilder {
+ private Drawable getIcon(List<DocumentInfo> docs) {
+ if (docs.size() == 1) {
+ final DocumentInfo doc = docs.get(0);
+ return mIconHelper.getDocumentIcon(mContext, doc.authority, doc.documentId,
+ doc.mimeType, doc.icon);
+ }
+ return mContext.getDrawable(R.drawable.ic_doc_generic);
+ }
- private final Drawable mShadow;
-
- private final int mShadowDimension;
-
- public DrawableShadowBuilder(Drawable shadow) {
- mShadow = shadow;
- mShadowDimension = getResources().getDimensionPixelSize(
- R.dimen.drag_shadow_size);
- mShadow.setBounds(0, 0, mShadowDimension, mShadowDimension);
+ private String getTitle(List<DocumentInfo> docs) {
+ if (docs.size() == 1) {
+ final DocumentInfo doc = docs.get(0);
+ return doc.displayName;
+ }
+ return Shared.getQuantityString(mContext, R.plurals.elements_dragged, docs.size());
}
@Override
public void onProvideShadowMetrics(
Point shadowSize, Point shadowTouchPoint) {
- shadowSize.set(mShadowDimension, mShadowDimension);
- shadowTouchPoint.set(mShadowDimension / 2, mShadowDimension / 2);
+ shadowSize.set(mWidth, mHeight);
+ shadowTouchPoint.set(mWidth, mHeight);
}
@Override
public void onDrawShadow(Canvas canvas) {
- mShadow.draw(canvas);
+ Rect r = canvas.getClipBounds();
+ // Calling measure is necessary in order for all child views to get correctly laid out.
+ mShadowView.measure(
+ View.MeasureSpec.makeMeasureSpec(r.right- r.left, View.MeasureSpec.EXACTLY),
+ View.MeasureSpec.makeMeasureSpec(r.top- r.bottom, View.MeasureSpec.EXACTLY));
+ mShadowView.layout(r.left, r.top, r.right, r.bottom);
+ mShadowView.draw(canvas);
}
}
-
/**
* Abstract task providing support for loading documents *off*
* the main thread. And if it isn't obvious, creating a list
@@ -1336,6 +1387,7 @@
// This has to be handled here instead of in a keyboard shortcut, because
// keyboard shortcuts all have to be modified with the 'Ctrl' key.
if (mSelectionManager.hasSelection()) {
+ Metrics.logKeyboardAction(getContext(), Metrics.ACTION_KEYBOARD_DELETE);
deleteDocuments(mSelectionManager.getSelection());
}
// Always handle the key, even if there was nothing to delete. This is a
@@ -1387,9 +1439,10 @@
}
}
- private DragStartHelper mDragHelper = new DragStartHelper(null) {
+ private DragStartHelper.OnDragStartListener mOnDragStartListener =
+ new DragStartHelper.OnDragStartListener() {
@Override
- protected boolean onDragStart(View v) {
+ public boolean onDragStart(View v, DragStartHelper helper) {
if (isSelected(getModelId(v))) {
List<DocumentInfo> docs = getDraggableDocuments(v);
if (docs.isEmpty()) {
@@ -1397,7 +1450,7 @@
}
v.startDragAndDrop(
mClipper.getClipDataForDocuments(docs),
- new DrawableShadowBuilder(getDragShadowIcon(docs)),
+ new DragShadowBuilder(getActivity(), mIconHelper, docs),
getDisplayState().stack.peek(),
View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ |
View.DRAG_FLAG_GLOBAL_URI_WRITE
@@ -1409,6 +1462,15 @@
}
};
+ private DragStartHelper mDragHelper = new DragStartHelper(null, mOnDragStartListener);
+
+ private View.OnLongClickListener onLongClickListener = new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ return mDragHelper.onLongClick(v);
+ }
+ };
+
// Previously we listened to events with one class, only to bounce them forward
// to GestureDetector. We're still doing that here, but with a single class
// that reduces overall complexity in our glue code.
@@ -1439,7 +1501,7 @@
// Detect drag events. When a drag is detected, intercept the rest of the gesture.
View itemView = rv.findChildViewUnder(e.getX(), e.getY());
- if (itemView != null && mDragHelper.handleTouch(itemView, e)) {
+ if (itemView != null && mDragHelper.onTouch(itemView, e)) {
return true;
}
// Forward unhandled events to the GestureDetector.
@@ -1451,7 +1513,7 @@
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
View itemView = rv.findChildViewUnder(e.getX(), e.getY());
- mDragHelper.handleTouch(itemView, e);
+ mDragHelper.onTouch(itemView, e);
// Note: even though this event is being handled as part of a drag gesture, continue
// forwarding to the GestureDetector. The detector needs to see the entire cluster of
// events in order to properly interpret gestures.
@@ -1612,6 +1674,9 @@
@Override
public void onLoadFinished(Loader<DirectoryResult> loader, DirectoryResult result) {
if (!isAdded()) return;
+ if (mSearchMode) {
+ Metrics.logSearch(getContext());
+ }
State state = getDisplayState();
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
index faa8e38..016cc9e 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
@@ -148,11 +148,15 @@
MenuItem share = menu.findItem(R.id.menu_share);
MenuItem delete = menu.findItem(R.id.menu_delete);
MenuItem rename = menu.findItem(R.id.menu_rename);
+ MenuItem selectAll = menu.findItem(R.id.menu_select_all);
open.setVisible(true);
share.setVisible(false);
delete.setVisible(false);
rename.setVisible(false);
+ selectAll.setVisible(mState.allowMultiple);
+
+ Menus.disableHiddenItems(menu);
}
@Override
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/RenameDocumentFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/RenameDocumentFragment.java
index 884abbb..73aa366 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/RenameDocumentFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/RenameDocumentFragment.java
@@ -44,6 +44,7 @@
import com.android.documentsui.BaseActivity;
import com.android.documentsui.DocumentsApplication;
+import com.android.documentsui.Metrics;
import com.android.documentsui.R;
import com.android.documentsui.Shared;
import com.android.documentsui.Snackbars;
@@ -227,11 +228,13 @@
@Override
protected void onPostExecute(DocumentInfo result) {
- if (result == null) {
+ if (result != null) {
+ Metrics.logRenameFileOperation(getContext());
+ } else {
Snackbars.makeSnackbar(mActivity, R.string.rename_error, Snackbar.LENGTH_SHORT)
.show();
+ Metrics.logRenameFileError(getContext());
}
-
mActivity.setPending(false);
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java b/packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java
index 9ed2abf..f10af43 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java
@@ -45,6 +45,8 @@
import android.os.RemoteException;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
+import android.system.ErrnoException;
+import android.system.Os;
import android.text.format.DateUtils;
import android.util.Log;
import android.webkit.MimeTypeMap;
@@ -451,7 +453,7 @@
ParcelFileDescriptor srcFile = null;
ParcelFileDescriptor dstFile = null;
InputStream in = null;
- OutputStream out = null;
+ ParcelFileDescriptor.AutoCloseOutputStream out = null;
boolean success = false;
try {
@@ -502,6 +504,8 @@
makeCopyProgress(len);
}
+ // Need to invoke IoUtils.close explicitly to avoid from ignoring errors at flush.
+ IoUtils.close(dstFile.getFileDescriptor());
srcFile.checkError();
} catch (IOException e) {
throw new ResourceException(
diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/FileOperations.java b/packages/DocumentsUI/src/com/android/documentsui/services/FileOperations.java
index b53e165..748da00 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/services/FileOperations.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/services/FileOperations.java
@@ -158,11 +158,11 @@
}
/**
- * Starts the service for a move operation.
+ * Starts the service for a delete operation.
*
* @param jobId A unique jobid for this job.
* Use {@link #createJobId} if you don't have one handy.
- * @param srcDocs A list of src files to copy.
+ * @param srcDocs A list of src files to delete.
* @param srcParent Parent of all the source documents.
* @return Id of the job.
*/
@@ -184,7 +184,7 @@
*
* @param jobId A unique jobid for this job.
* Use {@link #createJobId} if you don't have one handy.
- * @param srcDocs A list of src files to copy.
+ * @param srcDocs A list of src files for an operation.
* @return Id of the job.
*/
public static Intent createBaseIntent(
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
index f294919..69f0e67 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
@@ -22,8 +22,10 @@
import android.app.DownloadManager;
import android.app.DownloadManager.Request;
import android.content.Context;
+import android.content.Intent;
import android.net.Uri;
import android.os.RemoteException;
+import android.provider.DocumentsContract;
import android.support.test.uiautomator.Configurator;
import android.support.test.uiautomator.UiObject;
import android.test.suitebuilder.annotation.LargeTest;
@@ -69,11 +71,10 @@
ROOT_1_ID);
// Separate logic for "Documents" root, which presence depends on the config setting
- boolean homeRootHidden = context.getResources().getBoolean(R.bool.home_root_hidden);
- if (homeRootHidden) {
- bots.roots.assertRootsAbsent("Documents");
- } else {
+ if (Shared.shouldShowDocumentsRoot(context, new Intent(DocumentsContract.ACTION_BROWSE))) {
bots.roots.assertRootsPresent("Documents");
+ } else {
+ bots.roots.assertRootsAbsent("Documents");
}
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java
index e73dd8c..2e81545 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java
@@ -55,6 +55,7 @@
mState = new State();
mState.action = State.ACTION_OPEN;
+ mState.showAdvanced = true;
mState.localOnly = false;
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/DirectoryListBot.java b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/DirectoryListBot.java
index da9f9c3..7c1e2196 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/DirectoryListBot.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/DirectoryListBot.java
@@ -167,7 +167,7 @@
return true;
}
- private UiObject findDocumentsList() {
+ public UiObject findDocumentsList() {
return findObject(
"com.android.documentsui:id/container_directory",
DIR_LIST_ID);
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java
index 5b53caf..4c8dc00 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java
@@ -162,7 +162,9 @@
}
UiObject findSearchViewIcon() {
- return findObject("com.android.documentsui:id/menu_search", "android:id/search_button");
+ return mContext.getResources().getBoolean(R.bool.full_bar_search_view)
+ ? findObject("com.android.documentsui:id/menu_search")
+ : findObject("com.android.documentsui:id/menu_search", "android:id/search_button");
}
UiObject findActionModeBar() {
diff --git a/packages/Keyguard/res/drawable/ic_backspace_24dp.xml b/packages/Keyguard/res/drawable/ic_backspace_24dp.xml
index 47c8d14..1e4022e 100644
--- a/packages/Keyguard/res/drawable/ic_backspace_24dp.xml
+++ b/packages/Keyguard/res/drawable/ic_backspace_24dp.xml
@@ -15,6 +15,7 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
+ android:autoMirrored="true"
android:height="24dp"
android:viewportWidth="48.0"
android:viewportHeight="48.0">
diff --git a/packages/Keyguard/res/values-ky-rKG/strings.xml b/packages/Keyguard/res/values-ky-rKG/strings.xml
index 5089512..098d5a2 100644
--- a/packages/Keyguard/res/values-ky-rKG/strings.xml
+++ b/packages/Keyguard/res/values-ky-rKG/strings.xml
@@ -108,7 +108,7 @@
<string name="kg_password_puk_failed" msgid="2838824369502455984">"SIM-картанын PUK-кодун ачуу кыйрады!"</string>
<string name="kg_pin_accepted" msgid="1448241673570020097">"Код кабыл алынды!"</string>
<string name="keyguard_carrier_default" msgid="8700650403054042153">"Байланыш жок."</string>
- <string name="accessibility_ime_switch_button" msgid="2829803408288433429">"Киргизүү ыкмасын которуу"</string>
+ <string name="accessibility_ime_switch_button" msgid="2829803408288433429">"Киргизүү ыкмасын өзгөртүү"</string>
<string name="airplane_mode" msgid="3122107900897202805">"Учак режими"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="5519822969283306009">"Түзмөк кайра күйгүзүлгөндөн кийин графикалык ачкыч талап кылынат"</string>
<string name="kg_prompt_reason_restart_pin" msgid="4411398237158448198">"Түзмөк кайра күйгүзүлгөндөн кийин PIN код талап кылынат"</string>
diff --git a/packages/Keyguard/res/values-zh-rCN/strings.xml b/packages/Keyguard/res/values-zh-rCN/strings.xml
index 81666fe..a73dcb6 100644
--- a/packages/Keyguard/res/values-zh-rCN/strings.xml
+++ b/packages/Keyguard/res/values-zh-rCN/strings.xml
@@ -108,7 +108,7 @@
<string name="kg_password_puk_failed" msgid="2838824369502455984">"SIM卡PUK码操作失败!"</string>
<string name="kg_pin_accepted" msgid="1448241673570020097">"代码正确!"</string>
<string name="keyguard_carrier_default" msgid="8700650403054042153">"无服务。"</string>
- <string name="accessibility_ime_switch_button" msgid="2829803408288433429">"输入法切换按钮。"</string>
+ <string name="accessibility_ime_switch_button" msgid="2829803408288433429">"切换输入法"</string>
<string name="airplane_mode" msgid="3122107900897202805">"飞行模式"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="5519822969283306009">"重启设备后需要绘制解锁图案"</string>
<string name="kg_prompt_reason_restart_pin" msgid="4411398237158448198">"重启设备后需要输入 PIN 码"</string>
diff --git a/packages/Keyguard/res/values/attrs.xml b/packages/Keyguard/res/values/attrs.xml
index 96a5bcc..7cfe631 100644
--- a/packages/Keyguard/res/values/attrs.xml
+++ b/packages/Keyguard/res/values/attrs.xml
@@ -32,6 +32,9 @@
<declare-styleable name="PasswordTextView">
<attr name="scaledTextSize" format="integer" />
+ <attr name="android:gravity" />
+ <attr name="dotSize" format="dimension" />
+ <attr name="charPadding" format="dimension" />
</declare-styleable>
<declare-styleable name="CarrierText">
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
index b1d42e7..a7e4e12 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -464,7 +464,7 @@
return 0;
}
- private int getLayoutIdFor(SecurityMode securityMode) {
+ protected int getLayoutIdFor(SecurityMode securityMode) {
switch (securityMode) {
case Pattern: return R.layout.keyguard_pattern_view;
case PIN: return R.layout.keyguard_pin_view;
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java
index 454221a..2b549f1 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java
@@ -77,6 +77,7 @@
case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
+ case DevicePolicyManager.PASSWORD_QUALITY_MANAGED:
return SecurityMode.Password;
case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
diff --git a/packages/Keyguard/src/com/android/keyguard/NumPadKey.java b/packages/Keyguard/src/com/android/keyguard/NumPadKey.java
index ef8bb0b..2ff7e12 100644
--- a/packages/Keyguard/src/com/android/keyguard/NumPadKey.java
+++ b/packages/Keyguard/src/com/android/keyguard/NumPadKey.java
@@ -71,6 +71,10 @@
}
public NumPadKey(Context context, AttributeSet attrs, int defStyle) {
+ this(context, attrs, defStyle, R.layout.keyguard_num_pad_key);
+ }
+
+ protected NumPadKey(Context context, AttributeSet attrs, int defStyle, int contentResource) {
super(context, attrs, defStyle);
setFocusable(true);
@@ -92,7 +96,7 @@
mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
- inflater.inflate(R.layout.keyguard_num_pad_key, this, true);
+ inflater.inflate(contentResource, this, true);
mDigitText = (TextView) findViewById(R.id.digit_text);
mDigitText.setText(Integer.toString(mDigit));
@@ -113,7 +117,11 @@
}
}
- setBackground(mContext.getDrawable(R.drawable.ripple_drawable));
+ a = context.obtainStyledAttributes(attrs, android.R.styleable.View);
+ if (!a.hasValueOrEmpty(android.R.styleable.View_background)) {
+ setBackground(mContext.getDrawable(R.drawable.ripple_drawable));
+ }
+ a.recycle();
setContentDescription(mDigitText.getText().toString());
}
diff --git a/packages/Keyguard/src/com/android/keyguard/PasswordTextView.java b/packages/Keyguard/src/com/android/keyguard/PasswordTextView.java
index 50e7ecb..6eea81b 100644
--- a/packages/Keyguard/src/com/android/keyguard/PasswordTextView.java
+++ b/packages/Keyguard/src/com/android/keyguard/PasswordTextView.java
@@ -33,6 +33,7 @@
import android.text.InputType;
import android.text.TextUtils;
import android.util.AttributeSet;
+import android.view.Gravity;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
@@ -81,6 +82,7 @@
* The raw text size, will be multiplied by the scaled density when drawn
*/
private final int mTextHeightRaw;
+ private final int mGravity;
private ArrayList<CharState> mTextChars = new ArrayList<>();
private String mText = "";
private Stack<CharState> mCharPool = new Stack<>();
@@ -118,6 +120,12 @@
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PasswordTextView);
try {
mTextHeightRaw = a.getInt(R.styleable.PasswordTextView_scaledTextSize, 0);
+ mGravity = a.getInt(R.styleable.PasswordTextView_android_gravity, Gravity.CENTER);
+ mDotSize = a.getDimensionPixelSize(R.styleable.PasswordTextView_dotSize,
+ getContext().getResources().getDimensionPixelSize(R.dimen.password_dot_size));
+ mCharPadding = a.getDimensionPixelSize(R.styleable.PasswordTextView_charPadding,
+ getContext().getResources().getDimensionPixelSize(
+ R.dimen.password_char_padding));
} finally {
a.recycle();
}
@@ -125,9 +133,6 @@
mDrawPaint.setTextAlign(Paint.Align.CENTER);
mDrawPaint.setColor(0xffffffff);
mDrawPaint.setTypeface(Typeface.create("sans-serif-light", 0));
- mDotSize = getContext().getResources().getDimensionPixelSize(R.dimen.password_dot_size);
- mCharPadding = getContext().getResources().getDimensionPixelSize(R.dimen
- .password_char_padding);
mShowPassword = Settings.System.getInt(mContext.getContentResolver(),
Settings.System.TEXT_SHOW_PASSWORD, 1) == 1;
mAppearInterpolator = AnimationUtils.loadInterpolator(mContext,
@@ -142,11 +147,23 @@
@Override
protected void onDraw(Canvas canvas) {
float totalDrawingWidth = getDrawingWidth();
- float currentDrawPosition = getWidth() / 2 - totalDrawingWidth / 2;
+ float currentDrawPosition;
+ if ((mGravity & Gravity.START) != 0) {
+ if (getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
+ currentDrawPosition = getWidth() - getPaddingRight() - totalDrawingWidth;
+ } else {
+ currentDrawPosition = getPaddingLeft();
+ }
+ } else {
+ currentDrawPosition = getWidth() / 2 - totalDrawingWidth / 2;
+ }
int length = mTextChars.size();
Rect bounds = getCharBounds();
int charHeight = (bounds.bottom - bounds.top);
- float yPosition = getHeight() / 2;
+ float yPosition =
+ (getHeight() - getPaddingBottom() - getPaddingTop()) / 2 + getPaddingTop();
+ canvas.clipRect(getPaddingLeft(), getPaddingTop(),
+ getWidth()-getPaddingRight(), getHeight()-getPaddingBottom());
float charLength = bounds.right - bounds.left;
for (int i = 0; i < length; i++) {
CharState charState = mTextChars.get(i);
diff --git a/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp b/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
index 2b44d51..eb96015 100644
--- a/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
+++ b/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
@@ -51,6 +51,9 @@
static jclass app_fuse_class;
static jmethodID app_fuse_get_file_size;
static jmethodID app_fuse_read_object_bytes;
+static jmethodID app_fuse_write_object_bytes;
+static jmethodID app_fuse_flush_file_handle;
+static jmethodID app_fuse_close_file_handle;
static jfieldID app_fuse_buffer;
// NOTE:
@@ -140,6 +143,9 @@
case FUSE_READ:
invoke_handler(fd, req, &AppFuse::handle_fuse_read);
return true;
+ case FUSE_WRITE:
+ invoke_handler(fd, req, &AppFuse::handle_fuse_write);
+ return true;
case FUSE_RELEASE:
invoke_handler(fd, req, &AppFuse::handle_fuse_release);
return true;
@@ -289,17 +295,41 @@
return 0;
}
+ int handle_fuse_write(const fuse_in_header& /* header */,
+ const fuse_write_in* in,
+ FuseResponse<fuse_write_out>* out) {
+ if (in->size > MAX_WRITE) {
+ return -EINVAL;
+ }
+ const std::map<uint32_t, uint64_t>::iterator it = handles_.find(in->fh);
+ if (it == handles_.end()) {
+ return -EBADF;
+ }
+ const uint64_t offset = in->offset;
+ const uint32_t size = in->size;
+ const void* const buffer = reinterpret_cast<const uint8_t*>(in) + sizeof(fuse_write_in);
+ uint32_t written_size;
+ const int result = write_object_bytes(
+ in->fh, it->second, offset, size, buffer, &written_size);
+ if (result < 0) {
+ return result;
+ }
+ out->prepare_buffer();
+ out->data()->size = written_size;
+ return 0;
+ }
+
int handle_fuse_release(const fuse_in_header& /* header */,
const fuse_release_in* in,
FuseResponse<void>* /* out */) {
handles_.erase(in->fh);
- return 0;
+ return env_->CallIntMethod(self_, app_fuse_close_file_handle, file_handle_to_jlong(in->fh));
}
int handle_fuse_flush(const fuse_in_header& /* header */,
- const void* /* in */,
+ const fuse_flush_in* in,
FuseResponse<void>* /* out */) {
- return 0;
+ return env_->CallIntMethod(self_, app_fuse_flush_file_handle, file_handle_to_jlong(in->fh));
}
template <typename T, typename S>
@@ -355,6 +385,42 @@
return read_size;
}
+ int write_object_bytes(uint64_t handle, int inode, uint64_t offset, uint32_t size,
+ const void* buffer, uint32_t* written_size) {
+ static_assert(sizeof(uint64_t) <= sizeof(jlong),
+ "jlong must be able to express any uint64_t values");
+ ScopedLocalRef<jbyteArray> array(
+ env_,
+ static_cast<jbyteArray>(env_->GetObjectField(self_, app_fuse_buffer)));
+ {
+ ScopedByteArrayRW bytes(env_, array.get());
+ if (bytes.get() == nullptr) {
+ return -EIO;
+ }
+ memcpy(bytes.get(), buffer, size);
+ }
+ const int result = env_->CallIntMethod(
+ self_,
+ app_fuse_write_object_bytes,
+ file_handle_to_jlong(handle),
+ inode,
+ offset,
+ size,
+ array.get());
+ if (result < 0) {
+ return result;
+ }
+ *written_size = result;
+ return 0;
+ }
+
+ static jlong file_handle_to_jlong(uint64_t handle) {
+ static_assert(
+ sizeof(uint64_t) <= sizeof(jlong),
+ "jlong must be able to express any uint64_t values");
+ return static_cast<jlong>(handle);
+ }
+
static void fuse_reply(int fd, int unique, int reply_code, void* reply_data,
size_t reply_size) {
// Don't send any data for error case.
@@ -463,12 +529,40 @@
return -1;
}
+ app_fuse_write_object_bytes = env->GetMethodID(app_fuse_class, "writeObjectBytes", "(JIJI[B)I");
+ if (app_fuse_write_object_bytes == nullptr) {
+ ALOGE("Can't find writeObjectBytes");
+ return -1;
+ }
+
+ app_fuse_flush_file_handle = env->GetMethodID(app_fuse_class, "flushFileHandle", "(J)I");
+ if (app_fuse_flush_file_handle == nullptr) {
+ ALOGE("Can't find flushFileHandle");
+ return -1;
+ }
+
+ app_fuse_close_file_handle = env->GetMethodID(app_fuse_class, "closeFileHandle", "(J)I");
+ if (app_fuse_close_file_handle == nullptr) {
+ ALOGE("Can't find closeFileHandle");
+ return -1;
+ }
+
app_fuse_buffer = env->GetFieldID(app_fuse_class, "mBuffer", "[B");
if (app_fuse_buffer == nullptr) {
ALOGE("Can't find mBuffer");
return -1;
}
+ const jfieldID read_max_fied = env->GetStaticFieldID(app_fuse_class, "MAX_READ", "I");
+ if (static_cast<int>(env->GetStaticIntField(app_fuse_class, read_max_fied)) != MAX_READ) {
+ return -1;
+ }
+
+ const jfieldID write_max_fied = env->GetStaticFieldID(app_fuse_class, "MAX_WRITE", "I");
+ if (static_cast<int>(env->GetStaticIntField(app_fuse_class, write_max_fied)) != MAX_WRITE) {
+ return -1;
+ }
+
const int result = android::AndroidRuntime::registerNativeMethods(
env, "com/android/mtp/AppFuse", gMethods, NELEM(gMethods));
if (result < 0) {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java b/packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java
index 38435f4..88858a8 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java
@@ -20,6 +20,7 @@
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.storage.StorageManager;
+import android.system.ErrnoException;
import android.system.OsConstants;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -34,12 +35,18 @@
System.loadLibrary("appfuse_jni");
}
+ private static final boolean DEBUG = false;
+
/**
* Max read amount specified at the FUSE kernel implementation.
* The value is copied from sdcard.c.
*/
+ @UsedByNative("com_android_mtp_AppFuse.cpp")
static final int MAX_READ = 128 * 1024;
+ @UsedByNative("com_android_mtp_AppFuse.cpp")
+ static final int MAX_WRITE = 256 * 1024;
+
private final String mName;
private final Callback mCallback;
@@ -47,7 +54,7 @@
* Buffer for read bytes request.
* Don't use the buffer from the out of AppFuseMessageThread.
*/
- private byte[] mBuffer = new byte[MAX_READ];
+ private byte[] mBuffer = new byte[Math.max(MAX_READ, MAX_WRITE)];
private Thread mMessageThread;
private ParcelFileDescriptor mDeviceFd;
@@ -79,11 +86,23 @@
}
}
- public ParcelFileDescriptor openFile(int i) throws FileNotFoundException {
+ /**
+ * Opens a file on app fuse and returns ParcelFileDescriptor.
+ *
+ * @param i ID for opened file.
+ * @param mode Mode for opening file.
+ * @see ParcelFileDescriptor#MODE_READ_ONLY
+ * @see ParcelFileDescriptor#MODE_WRITE_ONLY
+ */
+ public ParcelFileDescriptor openFile(int i, int mode) throws FileNotFoundException {
+ Preconditions.checkArgument(
+ mode == ParcelFileDescriptor.MODE_READ_ONLY ||
+ mode == (ParcelFileDescriptor.MODE_WRITE_ONLY |
+ ParcelFileDescriptor.MODE_TRUNCATE));
return ParcelFileDescriptor.open(new File(
getMountPoint(),
Integer.toString(i)),
- ParcelFileDescriptor.MODE_READ_ONLY);
+ mode);
}
File getMountPoint() {
@@ -100,7 +119,7 @@
long getFileSize(int inode) throws FileNotFoundException;
/**
- * Returns flie bytes for the give inode.
+ * Returns file bytes for the give inode.
* @param inode
* @param offset Offset for file bytes.
* @param size Size for file bytes.
@@ -109,6 +128,34 @@
* @throws IOException
*/
long readObjectBytes(int inode, long offset, long size, byte[] bytes) throws IOException;
+
+ /**
+ * Handles writing bytes for the give inode.
+ * @param fileHandle
+ * @param inode
+ * @param offset Offset for file bytes.
+ * @param size Size for file bytes.
+ * @param bytes Buffer to store file bytes.
+ * @return Number of read bytes. Must not be negative.
+ * @throws IOException
+ */
+ int writeObjectBytes(long fileHandle, int inode, long offset, int size, byte[] bytes)
+ throws IOException, ErrnoException;
+
+ /**
+ * Flushes bytes for file handle.
+ * @param fileHandle
+ * @throws IOException
+ * @throws ErrnoException
+ */
+ void flushFileHandle(long fileHandle) throws IOException, ErrnoException;
+
+ /**
+ * Closes file handle.
+ * @param fileHandle
+ * @throws IOException
+ */
+ void closeFileHandle(long fileHandle) throws IOException, ErrnoException;
}
@UsedByNative("com_android_mtp_AppFuse.cpp")
@@ -116,8 +163,8 @@
private long getFileSize(int inode) {
try {
return mCallback.getFileSize(inode);
- } catch (FileNotFoundException e) {
- return -OsConstants.ENOENT;
+ } catch (Exception error) {
+ return -getErrnoFromException(error);
}
}
@@ -131,10 +178,61 @@
// It's OK to share the same mBuffer among requests because the requests are processed
// by AppFuseMessageThread sequentially.
return mCallback.readObjectBytes(inode, offset, size, mBuffer);
- } catch (IOException e) {
- return -OsConstants.EIO;
- } catch (UnsupportedOperationException e) {
- return -OsConstants.ENOTSUP;
+ } catch (Exception error) {
+ return -getErrnoFromException(error);
+ }
+ }
+
+ @UsedByNative("com_android_mtp_AppFuse.cpp")
+ @WorkerThread
+ private /* unsgined */ int writeObjectBytes(long fileHandler,
+ int inode,
+ /* unsigned */ long offset,
+ /* unsigned */ int size,
+ byte[] bytes) {
+ try {
+ return mCallback.writeObjectBytes(fileHandler, inode, offset, size, bytes);
+ } catch (Exception error) {
+ return -getErrnoFromException(error);
+ }
+ }
+
+ @UsedByNative("com_android_mtp_AppFuse.cpp")
+ @WorkerThread
+ private int flushFileHandle(long fileHandle) {
+ try {
+ mCallback.flushFileHandle(fileHandle);
+ return 0;
+ } catch (Exception error) {
+ return -getErrnoFromException(error);
+ }
+ }
+
+ @UsedByNative("com_android_mtp_AppFuse.cpp")
+ @WorkerThread
+ private int closeFileHandle(long fileHandle) {
+ try {
+ mCallback.closeFileHandle(fileHandle);
+ return 0;
+ } catch (Exception error) {
+ return -getErrnoFromException(error);
+ }
+ }
+
+ private static int getErrnoFromException(Exception error) {
+ if (DEBUG) {
+ Log.e(MtpDocumentsProvider.TAG, "AppFuse callbacks", error);
+ }
+ if (error instanceof FileNotFoundException) {
+ return OsConstants.ENOENT;
+ } else if (error instanceof IOException) {
+ return OsConstants.EIO;
+ } else if (error instanceof UnsupportedOperationException) {
+ return OsConstants.ENOTSUP;
+ } else if (error instanceof IllegalArgumentException) {
+ return OsConstants.EINVAL;
+ } else {
+ return OsConstants.EIO;
}
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java b/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
index 68f426f..246b95de 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
@@ -20,6 +20,7 @@
import android.annotation.WorkerThread;
import android.content.ContentResolver;
import android.database.Cursor;
+import android.mtp.MtpConstants;
import android.mtp.MtpObjectInfo;
import android.net.Uri;
import android.os.Bundle;
@@ -340,13 +341,46 @@
Log.e(MtpDocumentsProvider.TAG, "Failed to load object info", error);
}
}
+ final long[] objectSizeList = new long[infoList.size()];
+ for (int i = 0; i < infoList.size(); i++) {
+ final MtpObjectInfo info = infoList.get(i);
+ // Compressed size is 32-bit unsigned integer but getCompressedSize returns the
+ // value in Java int (signed 32-bit integer). Use getCompressedSizeLong instead
+ // to get the value in Java long.
+ if (info.getCompressedSizeLong() != 0xffffffffl) {
+ objectSizeList[i] = info.getCompressedSizeLong();
+ continue;
+ }
+
+ if (!MtpDeviceRecord.isSupported(
+ mOperationsSupported,
+ MtpConstants.OPERATION_GET_OBJECT_PROP_DESC) ||
+ !MtpDeviceRecord.isSupported(
+ mOperationsSupported,
+ MtpConstants.OPERATION_GET_OBJECT_PROP_VALUE)) {
+ objectSizeList[i] = -1;
+ continue;
+ }
+
+ // Object size is more than 4GB.
+ try {
+ objectSizeList[i] = mManager.getObjectSizeLong(
+ mIdentifier.mDeviceId,
+ info.getObjectHandle(),
+ info.getFormat());
+ } catch (IOException error) {
+ Log.e(MtpDocumentsProvider.TAG, "Failed to get object size property.", error);
+ objectSizeList[i] = -1;
+ }
+ }
synchronized (this) {
try {
mDatabase.getMapper().putChildDocuments(
mIdentifier.mDeviceId,
mIdentifier.mDocumentId,
mOperationsSupported,
- infoList.toArray(new MtpObjectInfo[infoList.size()]));
+ infoList.toArray(new MtpObjectInfo[infoList.size()]),
+ objectSizeList);
} catch (FileNotFoundException error) {
// Looks like the parent document information is removed.
// Adding documents has already cancelled in Mapper so we don't need to invoke
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java b/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java
index 8058183..adc71ae 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java
@@ -128,16 +128,27 @@
* @param deviceId Device ID
* @param parentId Parent document ID.
* @param documents List of document information.
+ * @param documentSizes 64-bit size of documents. MtpObjectInfo#getComporessedSize will be
+ * ignored because it does not contain 4GB> object size. Can be -1 if the size is unknown.
* @throws FileNotFoundException
*/
synchronized void putChildDocuments(
- int deviceId, String parentId, int[] operationsSupported, MtpObjectInfo[] documents)
+ int deviceId, String parentId,
+ int[] operationsSupported,
+ MtpObjectInfo[] documents,
+ long[] documentSizes)
throws FileNotFoundException {
+ assert documents.length == documentSizes.length;
final ContentValues[] valuesList = new ContentValues[documents.length];
for (int i = 0; i < documents.length; i++) {
valuesList[i] = new ContentValues();
MtpDatabase.getObjectDocumentValues(
- valuesList[i], deviceId, parentId, operationsSupported, documents[i]);
+ valuesList[i],
+ deviceId,
+ parentId,
+ operationsSupported,
+ documents[i],
+ documentSizes[i]);
}
putDocuments(
parentId,
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
index 4564018..cce619e 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
@@ -372,12 +372,16 @@
* newly added and never mapped with existing ones.
* @param parentDocumentId
* @param info
+ * @param size Object size. info#getCompressedSize() will be ignored because it does not contain
+ * object size more than 4GB.
* @return Document ID of added document.
*/
String putNewDocument(
- int deviceId, String parentDocumentId, int[] operationsSupported, MtpObjectInfo info) {
+ int deviceId, String parentDocumentId, int[] operationsSupported, MtpObjectInfo info,
+ long size) {
final ContentValues values = new ContentValues();
- getObjectDocumentValues(values, deviceId, parentDocumentId, operationsSupported, info);
+ getObjectDocumentValues(
+ values, deviceId, parentDocumentId, operationsSupported, info, size);
mDatabase.beginTransaction();
try {
final long id = mDatabase.insert(TABLE_DOCUMENTS, null, values);
@@ -586,9 +590,9 @@
}
void updateObject(String documentId, int deviceId, String parentId, int[] operationsSupported,
- MtpObjectInfo info) {
+ MtpObjectInfo info, Long size) {
final ContentValues values = new ContentValues();
- getObjectDocumentValues(values, deviceId, parentId, operationsSupported, info);
+ getObjectDocumentValues(values, deviceId, parentId, operationsSupported, info, size);
mDatabase.beginTransaction();
try {
@@ -811,11 +815,12 @@
* @param values {@link ContentValues} that receives values.
* @param deviceId Device ID of the object.
* @param parentId Parent document ID of the object.
- * @param info MTP object info.
+ * @param info MTP object info. getCompressedSize will be ignored.
+ * @param size 64-bit size of documents. Negative value is regarded as unknown size.
*/
static void getObjectDocumentValues(
ContentValues values, int deviceId, String parentId,
- int[] operationsSupported, MtpObjectInfo info) {
+ int[] operationsSupported, MtpObjectInfo info, long size) {
values.clear();
final String mimeType = getMimeType(info);
values.put(COLUMN_DEVICE_ID, deviceId);
@@ -834,7 +839,11 @@
values.put(Document.COLUMN_FLAGS, getDocumentFlags(
operationsSupported, mimeType, info.getThumbCompressedSizeLong(),
info.getProtectionStatus(), DOCUMENT_TYPE_OBJECT));
- values.put(Document.COLUMN_SIZE, info.getCompressedSizeLong());
+ if (size >= 0) {
+ values.put(Document.COLUMN_SIZE, size);
+ } else {
+ values.putNull(Document.COLUMN_SIZE);
+ }
}
private static String getMimeType(MtpObjectInfo info) {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDeviceRecord.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDeviceRecord.java
index 393c4de..c52b81d 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDeviceRecord.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDeviceRecord.java
@@ -56,8 +56,15 @@
}
static boolean isPartialReadSupported(@Nullable int[] supportedList, long fileSize) {
- return fileSize <= 0xffffffffl &&
- isSupported(supportedList, MtpConstants.OPERATION_GET_PARTIAL_OBJECT);
+ if (isSupported(supportedList, MtpConstants.OPERATION_GET_PARTIAL_OBJECT_64)) {
+ return true;
+ }
+ if (0 <= fileSize &&
+ fileSize <= 0xffffffffL &&
+ isSupported(supportedList, MtpConstants.OPERATION_GET_PARTIAL_OBJECT)) {
+ return true;
+ }
+ return false;
}
static boolean isWritingSupported(@Nullable int[] supportedList) {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index cf5bee5..50781bf 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -17,6 +17,7 @@
package com.android.mtp;
import android.content.ContentResolver;
+import android.content.Context;
import android.content.UriPermission;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
@@ -30,7 +31,6 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.CancellationSignal;
-import android.os.FileUriExposedException;
import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
import android.os.storage.StorageManager;
@@ -39,11 +39,16 @@
import android.provider.DocumentsContract;
import android.provider.DocumentsProvider;
import android.provider.Settings;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import java.io.File;
+import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
@@ -83,6 +88,7 @@
private MtpDatabase mDatabase;
private AppFuse mAppFuse;
private ServiceIntentSender mIntentSender;
+ private Context mContext;
/**
* Provides singleton instance to MtpDocumentsService.
@@ -94,6 +100,7 @@
@Override
public boolean onCreate() {
sSingleton = this;
+ mContext = getContext();
mResources = getContext().getResources();
mMtpManager = new MtpManager(getContext());
mResolver = getContext().getContentResolver();
@@ -138,12 +145,14 @@
@VisibleForTesting
boolean onCreateForTesting(
+ Context context,
Resources resources,
MtpManager mtpManager,
ContentResolver resolver,
MtpDatabase database,
StorageManager storageManager,
ServiceIntentSender intentSender) {
+ mContext = context;
mResources = resources;
mMtpManager = mtpManager;
mResolver = resolver;
@@ -233,39 +242,43 @@
try {
openDevice(identifier.mDeviceId);
final MtpDeviceRecord device = getDeviceToolkit(identifier.mDeviceId).mDeviceRecord;
- switch (mode) {
- case "r":
- final long fileSize = getFileSize(documentId);
- // MTP getPartialObject operation does not support files that are larger than
- // 4GB. Fallback to non-seekable file descriptor.
- // TODO: Use getPartialObject64 for MTP devices that support Android vendor
- // extension.
- if (MtpDeviceRecord.isPartialReadSupported(
- device.operationsSupported, fileSize)) {
- return mAppFuse.openFile(Integer.parseInt(documentId));
- } else {
- return getPipeManager(identifier).readDocument(mMtpManager, identifier);
- }
- case "w":
- // TODO: Clear the parent document loader task (if exists) and call notify
- // when writing is completed.
- if (MtpDeviceRecord.isWritingSupported(device.operationsSupported)) {
- return getPipeManager(identifier).writeDocument(
- getContext(), mMtpManager, identifier, device.operationsSupported);
- } else {
- throw new UnsupportedOperationException(
- "The device does not support writing operation.");
- }
- case "rw":
- // TODO: Add support for "rw" mode.
+ // Turn off MODE_CREATE because openDocument does not allow to create new files.
+ final int modeFlag =
+ ParcelFileDescriptor.parseMode(mode) & ~ParcelFileDescriptor.MODE_CREATE;
+ if ((modeFlag & ParcelFileDescriptor.MODE_READ_ONLY) != 0) {
+ long fileSize;
+ try {
+ fileSize = getFileSize(documentId);
+ } catch (UnsupportedOperationException exception) {
+ fileSize = -1;
+ }
+ if (MtpDeviceRecord.isPartialReadSupported(
+ device.operationsSupported, fileSize)) {
+ return mAppFuse.openFile(Integer.parseInt(documentId), modeFlag);
+ } else {
+ // If getPartialObject{|64} are not supported for the device, returns
+ // non-seekable pipe FD instead.
+ return getPipeManager(identifier).readDocument(mMtpManager, identifier);
+ }
+ } else if ((modeFlag & ParcelFileDescriptor.MODE_WRITE_ONLY) != 0) {
+ // TODO: Clear the parent document loader task (if exists) and call notify
+ // when writing is completed.
+ if (MtpDeviceRecord.isWritingSupported(device.operationsSupported)) {
+ return mAppFuse.openFile(Integer.parseInt(documentId), modeFlag);
+ } else {
throw new UnsupportedOperationException(
- "The provider does not support 'rw' mode.");
- default:
- throw new IllegalArgumentException("Unknown mode for openDocument: " + mode);
+ "The device does not support writing operation.");
+ }
+ } else {
+ // TODO: Add support for "rw" mode.
+ throw new UnsupportedOperationException("The provider does not support 'rw' mode.");
}
+ } catch (FileNotFoundException | RuntimeException error) {
+ Log.e(MtpDocumentsProvider.TAG, "openDocument", error);
+ throw error;
} catch (IOException error) {
Log.e(MtpDocumentsProvider.TAG, "openDocument", error);
- throw new FileNotFoundException(error.getMessage());
+ throw new IllegalStateException(error);
}
}
@@ -388,7 +401,7 @@
new MtpObjectInfo.Builder(info).setObjectHandle(objectHandle).build();
final String documentId = mDatabase.putNewDocument(
parentId.mDeviceId, parentDocumentId, record.operationsSupported,
- infoWithHandle);
+ infoWithHandle, 0l);
getDocumentLoader(parentId).clearTask(parentId);
notifyChildDocumentsChange(parentDocumentId);
return documentId;
@@ -543,6 +556,9 @@
MtpDatabase.strings(Document.COLUMN_SIZE, Document.COLUMN_DISPLAY_NAME));
try {
if (cursor.moveToNext()) {
+ if (cursor.isNull(0)) {
+ throw new UnsupportedOperationException();
+ }
return cursor.getLong(0);
} else {
throw new FileNotFoundException();
@@ -589,22 +605,72 @@
}
private class AppFuseCallback implements AppFuse.Callback {
+ private final Map<Long, MtpFileWriter> mWriters = new HashMap<>();
+
+ @Override
+ public long getFileSize(int inode) throws FileNotFoundException {
+ return MtpDocumentsProvider.this.getFileSize(String.valueOf(inode));
+ }
+
@Override
public long readObjectBytes(
int inode, long offset, long size, byte[] buffer) throws IOException {
final Identifier identifier = mDatabase.createIdentifier(Integer.toString(inode));
final MtpDeviceRecord record = getDeviceToolkit(identifier.mDeviceId).mDeviceRecord;
- if (MtpDeviceRecord.isPartialReadSupported(record.operationsSupported, offset)) {
+
+ if (MtpDeviceRecord.isSupported(
+ record.operationsSupported, MtpConstants.OPERATION_GET_PARTIAL_OBJECT_64)) {
+ return mMtpManager.getPartialObject64(
+ identifier.mDeviceId, identifier.mObjectHandle, offset, size, buffer);
+ }
+
+ if (0 <= offset && offset <= 0xffffffffL && MtpDeviceRecord.isSupported(
+ record.operationsSupported, MtpConstants.OPERATION_GET_PARTIAL_OBJECT)) {
return mMtpManager.getPartialObject(
identifier.mDeviceId, identifier.mObjectHandle, offset, size, buffer);
- } else {
- throw new UnsupportedOperationException();
}
+
+ throw new UnsupportedOperationException();
}
@Override
- public long getFileSize(int inode) throws FileNotFoundException {
- return MtpDocumentsProvider.this.getFileSize(String.valueOf(inode));
+ public int writeObjectBytes(
+ long fileHandle, int inode, long offset, int size, byte[] bytes)
+ throws IOException, ErrnoException {
+ final MtpFileWriter writer;
+ if (mWriters.containsKey(fileHandle)) {
+ writer = mWriters.get(fileHandle);
+ } else {
+ writer = new MtpFileWriter(mContext, String.valueOf(inode));
+ mWriters.put(fileHandle, writer);
+ }
+ return writer.write(offset, size, bytes);
+ }
+
+ @Override
+ public void flushFileHandle(long fileHandle) throws IOException, ErrnoException {
+ final MtpFileWriter writer = mWriters.get(fileHandle);
+ if (writer == null) {
+ // File handle for reading.
+ return;
+ }
+ final MtpDeviceRecord device = getDeviceToolkit(
+ mDatabase.createIdentifier(writer.getDocumentId()).mDeviceId).mDeviceRecord;
+ writer.flush(mMtpManager, mDatabase, device.operationsSupported);
+ }
+
+ @Override
+ public void closeFileHandle(long fileHandle) throws IOException, ErrnoException {
+ final MtpFileWriter writer = mWriters.get(fileHandle);
+ if (writer == null) {
+ // File handle for reading.
+ return;
+ }
+ try {
+ writer.close();
+ } finally {
+ mWriters.remove(fileHandle);
+ }
}
}
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpFileWriter.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpFileWriter.java
new file mode 100644
index 0000000..3e1bedc
--- /dev/null
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpFileWriter.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mtp;
+
+import android.content.Context;
+import android.mtp.MtpObjectInfo;
+import android.os.ParcelFileDescriptor;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+
+import com.android.internal.util.Preconditions;
+
+import java.io.File;
+import java.io.IOException;
+
+class MtpFileWriter implements AutoCloseable {
+ final ParcelFileDescriptor mCacheFd;
+ final String mDocumentId;
+ boolean mDirty;
+
+ MtpFileWriter(Context context, String documentId) throws IOException {
+ mDocumentId = documentId;
+ mDirty = false;
+ final File tempFile = File.createTempFile("mtp", "tmp", context.getCacheDir());
+ mCacheFd = ParcelFileDescriptor.open(
+ tempFile,
+ ParcelFileDescriptor.MODE_READ_WRITE |
+ ParcelFileDescriptor.MODE_TRUNCATE |
+ ParcelFileDescriptor.MODE_CREATE);
+ tempFile.delete();
+ }
+
+ String getDocumentId() {
+ return mDocumentId;
+ }
+
+ int write(long offset, int size, byte[] bytes) throws IOException, ErrnoException {
+ Preconditions.checkArgumentNonnegative(offset, "offset");
+ Preconditions.checkArgumentNonnegative(size, "size");
+ Preconditions.checkArgument(size <= bytes.length);
+ if (size == 0) {
+ return 0;
+ }
+ mDirty = true;
+ Os.lseek(mCacheFd.getFileDescriptor(), offset, OsConstants.SEEK_SET);
+ return Os.write(mCacheFd.getFileDescriptor(), bytes, 0, size);
+ }
+
+ void flush(MtpManager manager, MtpDatabase database, int[] operationsSupported)
+ throws IOException, ErrnoException {
+ // Skip unnecessary flush.
+ if (!mDirty) {
+ return;
+ }
+
+ // Get the placeholder object info.
+ final Identifier identifier = database.createIdentifier(mDocumentId);
+ final MtpObjectInfo placeholderObjectInfo =
+ manager.getObjectInfo(identifier.mDeviceId, identifier.mObjectHandle);
+
+ // Delete the target object info if it already exists (as a placeholder).
+ manager.deleteDocument(identifier.mDeviceId, identifier.mObjectHandle);
+
+ // Create the target object info with a correct file size and upload the file.
+ final long size = Os.lseek(mCacheFd.getFileDescriptor(), 0, OsConstants.SEEK_END);
+ final MtpObjectInfo targetObjectInfo = new MtpObjectInfo.Builder(placeholderObjectInfo)
+ .setCompressedSize(size)
+ .build();
+
+ Os.lseek(mCacheFd.getFileDescriptor(), 0, OsConstants.SEEK_SET);
+ final int newObjectHandle = manager.createDocument(
+ identifier.mDeviceId, targetObjectInfo, mCacheFd);
+
+ final MtpObjectInfo newObjectInfo = manager.getObjectInfo(
+ identifier.mDeviceId, newObjectHandle);
+ final Identifier parentIdentifier =
+ database.getParentIdentifier(identifier.mDocumentId);
+ database.updateObject(
+ identifier.mDocumentId,
+ identifier.mDeviceId,
+ parentIdentifier.mDocumentId,
+ operationsSupported,
+ newObjectInfo,
+ size);
+
+ mDirty = false;
+ }
+
+ @Override
+ public void close() throws IOException {
+ mCacheFd.close();
+ }
+}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
index 0599b70..00d31a71a 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
@@ -170,6 +170,14 @@
}
}
+ long getPartialObject64(int deviceId, int objectHandle, long offset, long size, byte[] buffer)
+ throws IOException {
+ final MtpDevice device = getDevice(deviceId);
+ synchronized (device) {
+ return device.getPartialObject64(objectHandle, offset, size, buffer);
+ }
+ }
+
byte[] getThumbnail(int deviceId, int objectHandle) throws IOException {
final MtpDevice device = getDevice(deviceId);
synchronized (device) {
@@ -229,6 +237,11 @@
return device.readEvent(signal);
}
+ long getObjectSizeLong(int deviceId, int objectHandle, int format) throws IOException {
+ final MtpDevice device = getDevice(deviceId);
+ return device.getObjectSizeLong(objectHandle, format);
+ }
+
private synchronized MtpDevice getDevice(int deviceId) throws IOException {
final MtpDevice device = mDevices.get(deviceId);
if (device == null) {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/PipeManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/PipeManager.java
index c10a65e..795bbc1 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/PipeManager.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/PipeManager.java
@@ -16,13 +16,9 @@
package com.android.mtp;
-import android.content.Context;
-import android.mtp.MtpObjectInfo;
import android.os.ParcelFileDescriptor;
import android.util.Log;
-import java.io.File;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -52,15 +48,6 @@
return task.getReadingFileDescriptor();
}
- ParcelFileDescriptor writeDocument(Context context, MtpManager model, Identifier identifier,
- int[] operationsSupported)
- throws IOException {
- final Task task = new WriteDocumentTask(
- context, model, identifier, operationsSupported, mDatabase);
- mExecutor.execute(task);
- return task.getWritingFileDescriptor();
- }
-
ParcelFileDescriptor readThumbnail(MtpManager model, Identifier identifier) throws IOException {
final Task task = new GetThumbnailTask(model, identifier);
mExecutor.execute(task);
@@ -81,10 +68,6 @@
ParcelFileDescriptor getReadingFileDescriptor() {
return mDescriptors[0];
}
-
- ParcelFileDescriptor getWritingFileDescriptor() {
- return mDescriptors[1];
- }
}
private static class ImportFileTask extends Task {
@@ -108,84 +91,6 @@
}
}
- private static class WriteDocumentTask extends Task {
- private final Context mContext;
- private final MtpDatabase mDatabase;
- private final int[] mOperationsSupported;
-
- WriteDocumentTask(Context context,
- MtpManager model,
- Identifier identifier,
- int[] supportedOperations,
- MtpDatabase database)
- throws IOException {
- super(model, identifier);
- mContext = context;
- mDatabase = database;
- mOperationsSupported = supportedOperations;
- }
-
- @Override
- public void run() {
- File tempFile = null;
- try {
- // Obtain a temporary file and copy the data to it.
- tempFile = File.createTempFile("mtp", "tmp", mContext.getCacheDir());
- try (
- final FileOutputStream tempOutputStream =
- new ParcelFileDescriptor.AutoCloseOutputStream(
- ParcelFileDescriptor.open(
- tempFile, ParcelFileDescriptor.MODE_WRITE_ONLY));
- final ParcelFileDescriptor.AutoCloseInputStream inputStream =
- new ParcelFileDescriptor.AutoCloseInputStream(mDescriptors[0])
- ) {
- final byte[] buffer = new byte[32 * 1024];
- int bytes;
- while ((bytes = inputStream.read(buffer)) != -1) {
- mDescriptors[0].checkError();
- tempOutputStream.write(buffer, 0, bytes);
- }
- tempOutputStream.flush();
- }
-
- // Get the placeholder object info.
- final MtpObjectInfo placeholderObjectInfo =
- mManager.getObjectInfo(mIdentifier.mDeviceId, mIdentifier.mObjectHandle);
-
- // Delete the target object info if it already exists (as a placeholder).
- mManager.deleteDocument(mIdentifier.mDeviceId, mIdentifier.mObjectHandle);
-
- // Create the target object info with a correct file size and upload the file.
- final MtpObjectInfo targetObjectInfo =
- new MtpObjectInfo.Builder(placeholderObjectInfo)
- .setCompressedSize(tempFile.length())
- .build();
- final ParcelFileDescriptor tempInputDescriptor = ParcelFileDescriptor.open(
- tempFile, ParcelFileDescriptor.MODE_READ_ONLY);
- final int newObjectHandle = mManager.createDocument(
- mIdentifier.mDeviceId, targetObjectInfo, tempInputDescriptor);
-
- final MtpObjectInfo newObjectInfo = mManager.getObjectInfo(
- mIdentifier.mDeviceId, newObjectHandle);
- final Identifier parentIdentifier =
- mDatabase.getParentIdentifier(mIdentifier.mDocumentId);
- mDatabase.updateObject(
- mIdentifier.mDocumentId,
- mIdentifier.mDeviceId,
- parentIdentifier.mDocumentId,
- mOperationsSupported,
- newObjectInfo);
- } catch (IOException error) {
- Log.w(MtpDocumentsProvider.TAG,
- "Failed to send a file because of: " + error.getMessage());
- } finally {
- if (tempFile != null) {
- tempFile.delete();
- }
- }
- }
- }
-
private static class GetThumbnailTask extends Task {
GetThumbnailTask(MtpManager model, Identifier identifier) throws IOException {
super(model, identifier);
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/annotations/UsedByNative.java b/packages/MtpDocumentsProvider/src/com/android/mtp/annotations/UsedByNative.java
index a7f295f..2ded925 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/annotations/UsedByNative.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/annotations/UsedByNative.java
@@ -22,7 +22,7 @@
/**
* Annotation that shows the method is used by JNI.
*/
-@Target(ElementType.METHOD)
+@Target({ElementType.METHOD, ElementType.FIELD})
public @interface UsedByNative {
/**
* JNI file name that uses the method.
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/AppFuseTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/AppFuseTest.java
index c0973bd..e421de71 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/AppFuseTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/AppFuseTest.java
@@ -23,6 +23,8 @@
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.MediumTest;
+import libcore.io.IoUtils;
+
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -56,7 +58,8 @@
}
});
appFuse.mount(storageManager);
- final ParcelFileDescriptor fd = appFuse.openFile(INODE);
+ final ParcelFileDescriptor fd = appFuse.openFile(
+ INODE, ParcelFileDescriptor.MODE_READ_ONLY);
fd.close();
appFuse.close();
}
@@ -67,11 +70,21 @@
final AppFuse appFuse = new AppFuse("test", new TestCallback());
appFuse.mount(storageManager);
try {
- appFuse.openFile(INODE);
+ appFuse.openFile(INODE, ParcelFileDescriptor.MODE_READ_ONLY);
fail();
- } catch (Throwable t) {
- assertTrue(t instanceof FileNotFoundException);
- }
+ } catch (FileNotFoundException exp) {}
+ appFuse.close();
+ }
+
+ public void testOpenFile_illegalMode() throws IOException {
+ final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
+ final int INODE = 10;
+ final AppFuse appFuse = new AppFuse("test", new TestCallback());
+ appFuse.mount(storageManager);
+ try {
+ appFuse.openFile(INODE, ParcelFileDescriptor.MODE_READ_WRITE);
+ fail();
+ } catch (IllegalArgumentException exp) {}
appFuse.close();
}
@@ -105,7 +118,8 @@
}
});
appFuse.mount(storageManager);
- final ParcelFileDescriptor fd = appFuse.openFile(fileInode);
+ final ParcelFileDescriptor fd = appFuse.openFile(
+ fileInode, ParcelFileDescriptor.MODE_READ_ONLY);
try (final ParcelFileDescriptor.AutoCloseInputStream stream =
new ParcelFileDescriptor.AutoCloseInputStream(fd)) {
final byte[] buffer = new byte[1024];
@@ -115,6 +129,112 @@
appFuse.close();
}
+ public void testWriteFile() throws IOException {
+ final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
+ final int INODE = 10;
+ final byte[] resultBytes = new byte[5];
+ final AppFuse appFuse = new AppFuse(
+ "test",
+ new TestCallback() {
+ @Override
+ public long getFileSize(int inode) throws FileNotFoundException {
+ if (inode != INODE) {
+ throw new FileNotFoundException();
+ }
+ return resultBytes.length;
+ }
+
+ @Override
+ public int writeObjectBytes(
+ long fileHandle, int inode, long offset, int size, byte[] bytes) {
+ for (int i = 0; i < size; i++) {
+ resultBytes[(int)(offset + i)] = bytes[i];
+ }
+ return size;
+ }
+ });
+ appFuse.mount(storageManager);
+ final ParcelFileDescriptor fd = appFuse.openFile(
+ INODE, ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_TRUNCATE);
+ try (final ParcelFileDescriptor.AutoCloseOutputStream stream =
+ new ParcelFileDescriptor.AutoCloseOutputStream(fd)) {
+ stream.write('a');
+ stream.write('b');
+ stream.write('c');
+ stream.write('d');
+ stream.write('e');
+ }
+ final byte[] BYTES = new byte[] { 'a', 'b', 'c', 'd', 'e' };
+ assertTrue(Arrays.equals(BYTES, resultBytes));
+ appFuse.close();
+ }
+
+ public void testWriteFile_writeError() throws IOException {
+ final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
+ final int INODE = 10;
+ final AppFuse appFuse = new AppFuse(
+ "test",
+ new TestCallback() {
+ @Override
+ public long getFileSize(int inode) throws FileNotFoundException {
+ if (inode != INODE) {
+ throw new FileNotFoundException();
+ }
+ return 5;
+ }
+ });
+ appFuse.mount(storageManager);
+ final ParcelFileDescriptor fd = appFuse.openFile(
+ INODE, ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_TRUNCATE);
+ try (final ParcelFileDescriptor.AutoCloseOutputStream stream =
+ new ParcelFileDescriptor.AutoCloseOutputStream(fd)) {
+ stream.write('a');
+ fail();
+ } catch (IOException e) {
+ }
+ appFuse.close();
+ }
+
+ public void testWriteFile_flushError() throws IOException {
+ final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
+ final int INODE = 10;
+ final AppFuse appFuse = new AppFuse(
+ "test",
+ new TestCallback() {
+ @Override
+ public long getFileSize(int inode) throws FileNotFoundException {
+ if (inode != INODE) {
+ throw new FileNotFoundException();
+ }
+ return 5;
+ }
+
+ @Override
+ public int writeObjectBytes(
+ long fileHandle, int inode, long offset, int size, byte[] bytes) {
+ return size;
+ }
+
+ @Override
+ public void flushFileHandle(long fileHandle) throws IOException {
+ throw new IOException();
+ }
+ });
+ appFuse.mount(storageManager);
+ final ParcelFileDescriptor fd = appFuse.openFile(
+ INODE, ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_TRUNCATE);
+ try (final ParcelFileDescriptor.AutoCloseOutputStream stream =
+ new ParcelFileDescriptor.AutoCloseOutputStream(fd)) {
+ stream.write('a');
+ try {
+ IoUtils.close(fd.getFileDescriptor());
+ fail();
+ } catch (IOException e) {
+ }
+ }
+ appFuse.close();
+ }
+
private static class TestCallback implements AppFuse.Callback {
@Override
public long getFileSize(int inode) throws FileNotFoundException {
@@ -126,5 +246,17 @@
throws IOException {
throw new IOException();
}
+
+ @Override
+ public int writeObjectBytes(long fileHandle, int inode, long offset, int size, byte[] bytes)
+ throws IOException {
+ throw new IOException();
+ }
+
+ @Override
+ public void flushFileHandle(long fileHandle) throws IOException {}
+
+ @Override
+ public void closeFileHandle(long fileHandle) {}
}
}
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
index 284223b..404047b 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
@@ -200,7 +200,7 @@
createDocument(100, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
createDocument(101, "image.jpg", MtpConstants.FORMAT_EXIF_JPEG, 2 * 1024 * 1024),
createDocument(102, "music.mp3", MtpConstants.FORMAT_MP3, 3 * 1024 * 1024)
- });
+ }, new long[] { 1024L, 2L * 1024L * 1024L, 3L * 1024L * 1024L});
final Cursor cursor = mDatabase.queryChildDocuments(COLUMN_NAMES, "2");
assertEquals(3, cursor.getCount());
@@ -273,7 +273,7 @@
mDatabase.getMapper().startAddingDocuments("2");
mDatabase.getMapper().putChildDocuments(0, "2", new int[0], new MtpObjectInfo[] {
createDocument(100, "note.txt", MtpConstants.FORMAT_TEXT, 1024)
- });
+ }, new long[] { 1024L });
mDatabase.getMapper().stopAddingDocuments("2");
try (final Cursor cursor =
@@ -290,7 +290,7 @@
MtpConstants.OPERATION_SEND_OBJECT_INFO,
}, new MtpObjectInfo[] {
createDocument(100, "note.txt", MtpConstants.FORMAT_TEXT, 1024)
- });
+ }, new long[] { 1024L });
mDatabase.getMapper().stopAddingDocuments("2");
try (final Cursor cursor =
@@ -306,7 +306,7 @@
MtpConstants.OPERATION_DELETE_OBJECT
}, new MtpObjectInfo[] {
createDocument(100, "note.txt", MtpConstants.FORMAT_TEXT, 1024)
- });
+ }, new long[] { 1024L });
mDatabase.getMapper().stopAddingDocuments("2");
try (final Cursor cursor =
@@ -395,7 +395,7 @@
createDocument(100, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
createDocument(101, "image.jpg", MtpConstants.FORMAT_EXIF_JPEG, 2 * 1024 * 1024),
createDocument(102, "music.mp3", MtpConstants.FORMAT_MP3, 3 * 1024 * 1024)
- });
+ }, new long[] { 1024L, 2L * 1024L * 1024L, 3L * 1024L * 1024L});
mDatabase.getMapper().clearMapping();
addTestDevice();
@@ -412,7 +412,7 @@
mDatabase.getMapper().putChildDocuments(0, "2", OPERATIONS_SUPPORTED, new MtpObjectInfo[] {
createDocument(200, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
createDocument(203, "video.mp4", MtpConstants.FORMAT_MP4_CONTAINER, 1024),
- });
+ }, new long[] { 1024L, 1024L });
mDatabase.getMapper().stopAddingDocuments("2");
{
@@ -545,7 +545,7 @@
mDatabase.getMapper().putChildDocuments(0, "2", OPERATIONS_SUPPORTED, new MtpObjectInfo[] {
createDocument(50, "A", MtpConstants.FORMAT_ASSOCIATION, 0),
createDocument(51, "B", MtpConstants.FORMAT_ASSOCIATION, 0),
- });
+ }, new long[] { 0L, 0L });
mDatabase.getMapper().stopAddingDocuments("2");
// Put note.txt in each directory.
@@ -553,10 +553,10 @@
mDatabase.getMapper().startAddingDocuments("4");
mDatabase.getMapper().putChildDocuments(0, "3", OPERATIONS_SUPPORTED, new MtpObjectInfo[] {
createDocument(100, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
- });
+ }, new long[] { 1024L });
mDatabase.getMapper().putChildDocuments(0, "4", OPERATIONS_SUPPORTED, new MtpObjectInfo[] {
createDocument(101, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
- });
+ }, new long[] { 1024L });
// Clear mapping.
mDatabase.getMapper().clearMapping();
@@ -568,7 +568,7 @@
mDatabase.getMapper().putChildDocuments(0, "2", OPERATIONS_SUPPORTED, new MtpObjectInfo[] {
createDocument(50, "A", MtpConstants.FORMAT_ASSOCIATION, 0),
createDocument(51, "B", MtpConstants.FORMAT_ASSOCIATION, 0),
- });
+ }, new long[] { 0L, 0L });
mDatabase.getMapper().stopAddingDocuments("2");
// Add note.txt in each directory again.
@@ -576,10 +576,10 @@
mDatabase.getMapper().startAddingDocuments("4");
mDatabase.getMapper().putChildDocuments(0, "3", OPERATIONS_SUPPORTED, new MtpObjectInfo[] {
createDocument(200, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
- });
+ }, new long[] { 1024L });
mDatabase.getMapper().putChildDocuments(0, "4", OPERATIONS_SUPPORTED, new MtpObjectInfo[] {
createDocument(201, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
- });
+ }, new long[] { 1024L });
mDatabase.getMapper().stopAddingDocuments("3");
mDatabase.getMapper().stopAddingDocuments("4");
@@ -860,7 +860,7 @@
mDatabase.getMapper().startAddingDocuments("2");
mDatabase.getMapper().putChildDocuments(0, "2", OPERATIONS_SUPPORTED, new MtpObjectInfo[] {
createDocument(200, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
- });
+ }, new long[] { 1024L });
mDatabase.getMapper().stopAddingDocuments("2");
assertEquals("2", mDatabase.getParentIdentifier("3").mDocumentId);
@@ -873,13 +873,13 @@
mDatabase.getMapper().startAddingDocuments("2");
mDatabase.getMapper().putChildDocuments(0, "2", OPERATIONS_SUPPORTED, new MtpObjectInfo[] {
createDocument(200, "dir", MtpConstants.FORMAT_ASSOCIATION, 1024),
- });
+ }, new long[] { 1024L });
mDatabase.getMapper().stopAddingDocuments("2");
mDatabase.getMapper().startAddingDocuments("3");
mDatabase.getMapper().putChildDocuments(0, "3", OPERATIONS_SUPPORTED, new MtpObjectInfo[] {
createDocument(200, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
- });
+ }, new long[] { 1024L });
mDatabase.getMapper().stopAddingDocuments("3");
mDatabase.deleteDocument("3");
@@ -909,7 +909,8 @@
"3",
mDatabase.putNewDocument(
0, "2", OPERATIONS_SUPPORTED,
- createDocument(200, "note.txt", MtpConstants.FORMAT_TEXT, 1024)));
+ createDocument(200, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
+ 1024L));
{
final Cursor cursor =
@@ -928,7 +929,8 @@
mDatabase.getMapper().startAddingDocuments("2");
mDatabase.putNewDocument(
0, "2", OPERATIONS_SUPPORTED,
- createDocument(201, "note.txt", MtpConstants.FORMAT_TEXT, 1024));
+ createDocument(201, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
+ 1024L);
mDatabase.getMapper().stopAddingDocuments("2");
{
@@ -1045,7 +1047,7 @@
mDatabase.getMapper().putChildDocuments(0, "2", OPERATIONS_SUPPORTED, new MtpObjectInfo[] {
createDocument(100, "apple.txt", MtpConstants.FORMAT_TEXT, 1024),
createDocument(101, "orange.txt", MtpConstants.FORMAT_TEXT, 1024),
- });
+ }, new long[] { 1024L, 1024L });
mDatabase.getMapper().stopAddingDocuments("2");
// Disconnect the device.
@@ -1064,7 +1066,7 @@
mDatabase.getMapper().putChildDocuments(0, "2", OPERATIONS_SUPPORTED, new MtpObjectInfo[] {
createDocument(100, "apple.txt", MtpConstants.FORMAT_TEXT, 1024),
createDocument(101, "orange.txt", MtpConstants.FORMAT_TEXT, 1024),
- });
+ }, new long[] { 1024L, 1024L });
mDatabase.getMapper().stopAddingDocuments("2");
try (final Cursor cursor = mDatabase.queryChildDocuments(
@@ -1093,7 +1095,7 @@
createDocument(102, "unknown.mp4", MtpConstants.FORMAT_MPEG, 1000),
createDocument(103, "inconsistent.txt", MtpConstants.FORMAT_MPEG, 1000),
createDocument(104, "noext", MtpConstants.FORMAT_UNDEFINED, 1000),
- });
+ }, new long[] { 1000L, 1000L, 1000L, 1000L, 1000L });
mDatabase.getMapper().stopAddingDocuments("2");
try (final Cursor cursor = mDatabase.queryChildDocuments(
strings(COLUMN_DISPLAY_NAME, COLUMN_MIME_TYPE),
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index da6af37..9ed15c8 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -21,6 +21,7 @@
import android.mtp.MtpObjectInfo;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
+import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
import android.os.storage.StorageManager;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
@@ -533,6 +534,30 @@
}
}
+ public void testOpenDocument_writing() throws Exception {
+ setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
+ setupRoots(0, new MtpRoot[] {
+ new MtpRoot(0, 0, "Storage", 0, 0, "")
+ });
+ final String documentId = mProvider.createDocument("2", "text/plain", "test.txt");
+ {
+ final ParcelFileDescriptor fd = mProvider.openDocument(documentId, "w", null);
+ try (ParcelFileDescriptor.AutoCloseOutputStream stream =
+ new ParcelFileDescriptor.AutoCloseOutputStream(fd)) {
+ stream.write("Hello".getBytes());
+ }
+ }
+ {
+ final ParcelFileDescriptor fd = mProvider.openDocument(documentId, "r", null);
+ try (ParcelFileDescriptor.AutoCloseInputStream stream =
+ new ParcelFileDescriptor.AutoCloseInputStream(fd)) {
+ final byte[] bytes = new byte[5];
+ stream.read(bytes);
+ assertTrue(Arrays.equals("Hello".getBytes(), bytes));
+ }
+ }
+ }
+
public void testBusyDevice() throws Exception {
mMtpManager = new TestMtpManager(getContext()) {
@Override
@@ -708,11 +733,39 @@
} catch (UnsupportedOperationException exception) {}
}
+ public void testObjectSizeLong() throws Exception {
+ setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
+ setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Storage", 1000, 1000, "") });
+ mMtpManager.setObjectSizeLong(0, 100, MtpConstants.FORMAT_EXIF_JPEG, 0x400000000L);
+ setupDocuments(
+ 0,
+ 0,
+ MtpManager.OBJECT_HANDLE_ROOT_CHILDREN,
+ "1",
+ new MtpObjectInfo[] {
+ new MtpObjectInfo.Builder()
+ .setObjectHandle(100)
+ .setFormat(MtpConstants.FORMAT_EXIF_JPEG)
+ .setName("image.jpg")
+ .setCompressedSize(0xffffffffl)
+ .build()
+ });
+
+ final Cursor cursor = mProvider.queryDocument("3", new String[] {
+ DocumentsContract.Document.COLUMN_SIZE
+ });
+ assertEquals(1, cursor.getCount());
+
+ cursor.moveToNext();
+ assertEquals(0x400000000L, cursor.getLong(0));
+ }
+
private void setupProvider(int flag) {
mDatabase = new MtpDatabase(getContext(), flag);
mProvider = new MtpDocumentsProvider();
final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
assertTrue(mProvider.onCreateForTesting(
+ getContext(),
mResources,
mMtpManager,
mResolver,
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/PipeManagerTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/PipeManagerTest.java
index a08d9ee..53dc3db 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/PipeManagerTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/PipeManagerTest.java
@@ -16,10 +16,7 @@
package com.android.mtp;
-import android.database.Cursor;
-import android.mtp.MtpObjectInfo;
import android.os.ParcelFileDescriptor;
-import android.provider.DocumentsContract.Document;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.MediumTest;
@@ -66,63 +63,6 @@
assertDescriptorError(descriptor);
}
- public void testWriteDocument_basic() throws Exception {
- TestUtil.addTestDevice(mDatabase);
- TestUtil.addTestStorage(mDatabase, "1");
-
- final MtpObjectInfo info =
- new MtpObjectInfo.Builder().setObjectHandle(1).setName("note.txt").build();
- mDatabase.getMapper().startAddingDocuments("2");
- mDatabase.getMapper().putChildDocuments(
- 0, "2", TestUtil.OPERATIONS_SUPPORTED,
- new MtpObjectInfo[] { info });
- mDatabase.getMapper().stopAddingDocuments("2");
- // Create a placeholder file which should be replaced by a real file later.
- mtpManager.setObjectInfo(0, info);
-
- // Upload testing bytes.
- final ParcelFileDescriptor descriptor = mPipeManager.writeDocument(
- getContext(),
- mtpManager,
- new Identifier(0, 0, 1, "2", MtpDatabaseConstants.DOCUMENT_TYPE_OBJECT),
- TestUtil.OPERATIONS_SUPPORTED);
- final ParcelFileDescriptor.AutoCloseOutputStream outputStream =
- new ParcelFileDescriptor.AutoCloseOutputStream(descriptor);
- outputStream.write(HELLO_BYTES, 0, HELLO_BYTES.length);
- outputStream.close();
- mExecutor.shutdown();
- assertTrue(mExecutor.awaitTermination(1000, TimeUnit.MILLISECONDS));
-
- // Check if the placeholder file is removed.
- try {
- mtpManager.getObjectInfo(0, 1);
- fail(); // The placeholder file has not been deleted.
- } catch (IOException e) {
- // Expected error, as the file is gone.
- }
-
- // Confirm that the target file is created.
- final MtpObjectInfo targetDocument = mtpManager.getObjectInfo(
- 0, TestMtpManager.CREATED_DOCUMENT_HANDLE);
- assertTrue(targetDocument != null);
-
- // Confirm the object handle is updated.
- try (final Cursor cursor = mDatabase.queryDocument(
- "2", new String[] { MtpDatabaseConstants.COLUMN_OBJECT_HANDLE })) {
- assertEquals(1, cursor.getCount());
- cursor.moveToNext();
- assertEquals(TestMtpManager.CREATED_DOCUMENT_HANDLE, cursor.getInt(0));
- }
-
- // Verify uploaded bytes.
- final byte[] uploadedBytes = mtpManager.getImportFileBytes(
- 0, TestMtpManager.CREATED_DOCUMENT_HANDLE);
- assertEquals(HELLO_BYTES.length, uploadedBytes.length);
- for (int i = 0; i < HELLO_BYTES.length; i++) {
- assertEquals(HELLO_BYTES[i], uploadedBytes[i]);
- }
- }
-
public void testReadThumbnail_basic() throws Exception {
mtpManager.setThumbnail(0, 1, HELLO_BYTES);
final ParcelFileDescriptor descriptor = mPipeManager.readThumbnail(
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
index 5171bd2..9a81489 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
@@ -39,6 +39,7 @@
private final Map<String, int[]> mObjectHandles = new HashMap<>();
private final Map<String, byte[]> mThumbnailBytes = new HashMap<>();
private final Map<String, byte[]> mImportFileBytes = new HashMap<>();
+ private final Map<String, Long> mObjectSizeLongs = new HashMap<>();
TestMtpManager(Context context) {
super(context);
@@ -68,6 +69,10 @@
mThumbnailBytes.put(pack(deviceId, objectHandle), bytes);
}
+ void setObjectSizeLong(int deviceId, int objectHandle, int format, long value) {
+ mObjectSizeLongs.put(pack(deviceId, objectHandle, format), value);
+ }
+
@Override
MtpDeviceRecord[] getDevices() {
final MtpDeviceRecord[] result = new MtpDeviceRecord[mDevices.size()];
@@ -214,4 +219,14 @@
}
return i;
}
+
+ @Override
+ long getObjectSizeLong(int deviceId, int objectHandle, int format) throws IOException {
+ final String key = pack(deviceId, objectHandle, format);
+ if (mObjectSizeLongs.containsKey(key)) {
+ return mObjectSizeLongs.get(key);
+ } else {
+ throw new IOException();
+ }
+ }
}
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestUtil.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestUtil.java
index 5ceab01..8805d19 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestUtil.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestUtil.java
@@ -38,6 +38,8 @@
MtpConstants.OPERATION_SEND_OBJECT,
MtpConstants.OPERATION_SEND_OBJECT_INFO,
MtpConstants.OPERATION_DELETE_OBJECT,
+ MtpConstants.OPERATION_GET_OBJECT_PROP_DESC,
+ MtpConstants.OPERATION_GET_OBJECT_PROP_VALUE
};
/**
diff --git a/packages/PrintServiceRecommendationService/Android.mk b/packages/PrintServiceRecommendationService/Android.mk
new file mode 100644
index 0000000..66cb057
--- /dev/null
+++ b/packages/PrintServiceRecommendationService/Android.mk
@@ -0,0 +1,29 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := PrintRecommendationService
+
+include $(BUILD_PACKAGE)
+
+LOCAL_SDK_VERSION := system_current
+
+include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/packages/PrintServiceRecommendationService/AndroidManifest.xml b/packages/PrintServiceRecommendationService/AndroidManifest.xml
new file mode 100644
index 0000000..0eb218c
--- /dev/null
+++ b/packages/PrintServiceRecommendationService/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.printservice.recommendation">
+
+ <uses-permission android:name="android.permission.INTERNET" />
+
+ <application
+ android:allowClearUserData="false"
+ android:label="@string/app_label"
+ android:allowBackup= "false">
+
+ <service
+ android:name=".RecommendationServiceImpl"
+ android:permission="android.permission.BIND_PRINT_RECOMMENDATION_SERVICE">
+
+ <intent-filter>
+ <action android:name="android.printservice.recommendation.RecommendationService" />
+ </intent-filter>
+ </service>
+
+ </application>
+
+</manifest>
diff --git a/packages/PrintServiceRecommendationService/MODULE_LICENSE_APACHE2 b/packages/PrintServiceRecommendationService/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/packages/PrintServiceRecommendationService/MODULE_LICENSE_APACHE2
diff --git a/packages/PrintServiceRecommendationService/NOTICE b/packages/PrintServiceRecommendationService/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/packages/PrintServiceRecommendationService/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-2008, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/packages/PrintServiceRecommendationService/res/values/donottranslate.xml b/packages/PrintServiceRecommendationService/res/values/donottranslate.xml
new file mode 100644
index 0000000..4cf0eaf
--- /dev/null
+++ b/packages/PrintServiceRecommendationService/res/values/donottranslate.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <string name="app_label">Print Service Recommendation Service</string>
+</resources>
diff --git a/packages/PrintServiceRecommendationService/res/values/strings.xml b/packages/PrintServiceRecommendationService/res/values/strings.xml
new file mode 100644
index 0000000..07d0004
--- /dev/null
+++ b/packages/PrintServiceRecommendationService/res/values/strings.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ (c) Copyright 2016 Mopria Alliance, Inc.
+ (c) Copyright 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <string name="plugin_vendor_hp">HP</string>
+ <string name="plugin_vendor_lexmark">Lexmark</string>
+ <string name="plugin_vendor_brother">Brother</string>
+ <string name="plugin_vendor_canon">Canon</string>
+ <string name="plugin_vendor_xerox">Xerox</string>
+ <string name="plugin_vendor_samsung">Samsung Electronics</string>
+ <string name="plugin_vendor_epson">Epson</string>
+ <string name="plugin_vendor_konika_minolta">Konika Minolta</string>
+ <string name="plugin_vendor_fuji">Fuji</string>
+</resources>
diff --git a/packages/PrintServiceRecommendationService/res/xml/vendorconfigs.xml b/packages/PrintServiceRecommendationService/res/xml/vendorconfigs.xml
new file mode 100644
index 0000000..119943c
--- /dev/null
+++ b/packages/PrintServiceRecommendationService/res/xml/vendorconfigs.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ (c) Copyright 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<vendors>
+ <vendor>
+ <name>@string/plugin_vendor_hp</name>
+ <package>com.hp.android.printservice</package>
+ <mdns-names>
+ <mdns-name>HP</mdns-name>
+ <mdns-name>Hewlett-Packard</mdns-name>
+ <mdns-name>Hewlett Packard</mdns-name>
+ </mdns-names>
+ </vendor>
+
+ <vendor>
+ <name>@string/plugin_vendor_lexmark</name>
+ <package>com.lexmark.print.plugin</package>
+ <mdns-names>
+ <mdns-name>Lexmark</mdns-name>
+ <mdns-name>Lexmark International</mdns-name>
+ </mdns-names>
+ </vendor>
+
+ <vendor>
+ <name>@string/plugin_vendor_brother</name>
+ <package>com.brother.printservice</package>
+ <mdns-names>
+ <mdns-name>Brother</mdns-name>
+ </mdns-names>
+ </vendor>
+
+ <vendor>
+ <name>@string/plugin_vendor_canon</name>
+ <package>jp.co.canon.android.printservice.plugin</package>
+ <mdns-names>
+ <mdns-name>Canon</mdns-name>
+ </mdns-names>
+ </vendor>
+
+ <vendor>
+ <name>@string/plugin_vendor_xerox</name>
+ <package>com.xerox.printservice</package>
+ <mdns-names>
+ <mdns-name>Xerox</mdns-name>
+ </mdns-names>
+ </vendor>
+
+ <vendor>
+ <name>@string/plugin_vendor_samsung</name>
+ <package>com.sec.app.samsungprintservice</package>
+ <mdns-names>
+ <mdns-name>Samsung</mdns-name>
+ </mdns-names>
+ </vendor>
+
+ <vendor>
+ <name>@string/plugin_vendor_epson</name>
+ <package>com.epson.mobilephone.android.epsonprintserviceplugin</package>
+ <mdns-names>
+ <mdns-name>Epson</mdns-name>
+ </mdns-names>
+ </vendor>
+
+ <vendor>
+ <name>@string/plugin_vendor_konika_minolta</name>
+ <package>com.kmbt.printservice</package>
+ <mdns-names>
+ <mdns-name>kmkmkm</mdns-name>
+ <mdns-name>Konica Minolta</mdns-name>
+ <mdns-name>Minolta</mdns-name>
+ </mdns-names>
+ </vendor>
+
+ <vendor>
+ <name>@string/plugin_vendor_fuji</name>
+ <package>jp.co.fujixerox.prt.PrintUtil.PCL</package>
+ <mdns-names>
+ <mdns-name>FUJI XEROX</mdns-name>
+ </mdns-names>
+ </vendor>
+</vendors>
diff --git a/packages/PrintServiceRecommendationService/src/com/android/printservice/recommendation/PrintServicePlugin.java b/packages/PrintServiceRecommendationService/src/com/android/printservice/recommendation/PrintServicePlugin.java
new file mode 100644
index 0000000..d604ef8
--- /dev/null
+++ b/packages/PrintServiceRecommendationService/src/com/android/printservice/recommendation/PrintServicePlugin.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.printservice.recommendation;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.StringRes;
+
+/**
+ * Interface to be implemented by each print service plugin.
+ * <p/>
+ * A print service plugin is a minimal version of a real {@link android.printservice.PrintService
+ * print service}. You cannot print using the plugin. The only functionality in the plugin is to
+ * report the number of printers that the real service would discover.
+ */
+public interface PrintServicePlugin {
+ /**
+ * Call back used by the print service plugins.
+ */
+ interface PrinterDiscoveryCallback {
+ /**
+ * Announce that something changed and the UI for this plugin should be updated.
+ *
+ * @param numDiscoveredPrinters The number of printers discovered.
+ */
+ void onChanged(@IntRange(from = 0) int numDiscoveredPrinters);
+ }
+
+ /**
+ * Get the name (a string reference) of the {@link android.printservice.PrintService print
+ * service} with the {@link #getPackageName specified package name}. This is read once, hence
+ * returning different data at different times is not allowed.
+ *
+ * @return The name of the print service as a string reference. The localization is handled
+ * outside of the plugin.
+ */
+ @StringRes int getName();
+
+ /**
+ * The package name of the full print service.
+ *
+ * @return The package name
+ */
+ @NonNull CharSequence getPackageName();
+
+ /**
+ * Start the discovery plugin.
+ *
+ * @param callback Callbacks used by this plugin.
+ *
+ * @throws Exception If anything went wrong when starting the plugin
+ */
+ void start(@NonNull PrinterDiscoveryCallback callback) throws Exception;
+
+ /**
+ * Stop the plugin. This can only return once the plugin is completely finished and cleaned up.
+ *
+ * @throws Exception If anything went wrong while stopping plugin
+ */
+ void stop() throws Exception;
+}
diff --git a/packages/PrintServiceRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java b/packages/PrintServiceRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java
new file mode 100644
index 0000000..9f6dad8
--- /dev/null
+++ b/packages/PrintServiceRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.printservice.recommendation;
+
+import android.content.res.Configuration;
+import android.printservice.recommendation.RecommendationInfo;
+import android.printservice.recommendation.RecommendationService;
+import android.printservice.PrintService;
+import android.util.Log;
+import com.android.printservice.recommendation.plugin.mdnsFilter.MDNSFilterPlugin;
+import com.android.printservice.recommendation.plugin.mdnsFilter.VendorConfig;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * Service that recommends {@link PrintService print services} that might be a good idea to install.
+ */
+public class RecommendationServiceImpl extends RecommendationService
+ implements RemotePrintServicePlugin.OnChangedListener {
+ private static final String LOG_TAG = "PrintServiceRecService";
+
+ /** All registered plugins */
+ private ArrayList<RemotePrintServicePlugin> mPlugins;
+
+ @Override
+ public void onConnected() {
+ mPlugins = new ArrayList<>();
+
+ try {
+ for (VendorConfig config : VendorConfig.getAllConfigs(this)) {
+ try {
+ mPlugins.add(new RemotePrintServicePlugin(new MDNSFilterPlugin(this,
+ config.name, config.packageName, config.mDNSNames), this, false));
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Could not initiate simple MDNS plugin for " +
+ config.packageName, e);
+ }
+ }
+ } catch (IOException | XmlPullParserException e) {
+ new RuntimeException("Could not parse vendorconfig", e);
+ }
+
+ final int numPlugins = mPlugins.size();
+ for (int i = 0; i < numPlugins; i++) {
+ try {
+ mPlugins.get(i).start();
+ } catch (RemotePrintServicePlugin.PluginException e) {
+ Log.e(LOG_TAG, "Could not start plugin", e);
+ }
+ }
+ }
+
+ @Override
+ public void onDisconnected() {
+ final int numPlugins = mPlugins.size();
+ for (int i = 0; i < numPlugins; i++) {
+ try {
+ mPlugins.get(i).stop();
+ } catch (RemotePrintServicePlugin.PluginException e) {
+ Log.e(LOG_TAG, "Could not stop plugin", e);
+ }
+ }
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ // Need to update plugin names as they might be localized
+ onChanged();
+ }
+
+ @Override
+ public void onChanged() {
+ ArrayList<RecommendationInfo> recommendations = new ArrayList<>();
+
+ final int numPlugins = mPlugins.size();
+ for (int i = 0; i < numPlugins; i++) {
+ RemotePrintServicePlugin plugin = mPlugins.get(i);
+
+ try {
+ int numPrinters = plugin.getNumPrinters();
+
+ if (numPrinters > 0) {
+ recommendations.add(new RecommendationInfo(plugin.packageName,
+ getString(plugin.name), numPrinters,
+ plugin.recommendsMultiVendorService));
+ }
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Could not read state of plugin for " + plugin.packageName, e);
+ }
+ }
+
+ updateRecommendations(recommendations);
+ }
+}
diff --git a/packages/PrintServiceRecommendationService/src/com/android/printservice/recommendation/RemotePrintServicePlugin.java b/packages/PrintServiceRecommendationService/src/com/android/printservice/recommendation/RemotePrintServicePlugin.java
new file mode 100644
index 0000000..dbd1649
--- /dev/null
+++ b/packages/PrintServiceRecommendationService/src/com/android/printservice/recommendation/RemotePrintServicePlugin.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.printservice.recommendation;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.StringRes;
+import com.android.internal.util.Preconditions;
+
+/**
+ * Wrapper for a {@link PrintServicePlugin}, isolating issues with the plugin as good as possible
+ * from the {@link RecommendationServiceImpl service}.
+ */
+class RemotePrintServicePlugin implements PrintServicePlugin.PrinterDiscoveryCallback {
+ /** Lock for this object */
+ private final Object mLock = new Object();
+
+ /** The name of the print service. */
+ public final @StringRes int name;
+
+ /** If the print service if for more than a single vendor */
+ public final boolean recommendsMultiVendorService;
+
+ /** The package name of the full print service */
+ public final @NonNull CharSequence packageName;
+
+ /** Wrapped plugin */
+ private final @NonNull PrintServicePlugin mPlugin;
+
+ /** The number of printers discovered by the plugin */
+ private @IntRange(from = 0) int mNumPrinters;
+
+ /** If the plugin is started by not yet stopped */
+ private boolean isRunning;
+
+ /** Listener for changes to {@link #mNumPrinters}. */
+ private @NonNull OnChangedListener mListener;
+
+ /**
+ * Create a new remote for a {@link PrintServicePlugin plugin}.
+ *
+ * @param plugin The plugin to be wrapped
+ * @param listener The listener to be notified about changes in this plugin
+ * @param recommendsMultiVendorService If the plugin detects printers of more than a single
+ * vendor
+ *
+ * @throws PluginException If the plugin has issues while caching basic stub properties
+ */
+ public RemotePrintServicePlugin(@NonNull PrintServicePlugin plugin,
+ @NonNull OnChangedListener listener, boolean recommendsMultiVendorService)
+ throws PluginException {
+ mListener = listener;
+ mPlugin = plugin;
+ this.recommendsMultiVendorService = recommendsMultiVendorService;
+
+ // We handle any throwable to isolate our self from bugs in the plugin code.
+ // Cache simple properties to avoid having to deal with exceptions later in the code.
+ try {
+ name = Preconditions.checkArgumentPositive(mPlugin.getName(), "name");
+ packageName = Preconditions.checkStringNotEmpty(mPlugin.getPackageName(),
+ "packageName");
+ } catch (Throwable e) {
+ throw new PluginException(mPlugin, "Cannot cache simple properties ", e);
+ }
+
+ isRunning = false;
+ }
+
+ /**
+ * Start the plugin. From now on there might be callbacks to the registered listener.
+ */
+ public void start()
+ throws PluginException {
+ // We handle any throwable to isolate our self from bugs in the stub code
+ try {
+ synchronized (mLock) {
+ isRunning = true;
+ mPlugin.start(this);
+ }
+ } catch (Throwable e) {
+ throw new PluginException(mPlugin, "Cannot start", e);
+ }
+ }
+
+ /**
+ * Stop the plugin. From this call on there will not be any more callbacks.
+ */
+ public void stop() throws PluginException {
+ // We handle any throwable to isolate our self from bugs in the stub code
+ try {
+ synchronized (mLock) {
+ mPlugin.stop();
+ isRunning = false;
+ }
+ } catch (Throwable e) {
+ throw new PluginException(mPlugin, "Cannot stop", e);
+ }
+ }
+
+ /**
+ * Get the current number of printers reported by the stub.
+ *
+ * @return The number of printers reported by the stub.
+ */
+ public @IntRange(from = 0) int getNumPrinters() {
+ return mNumPrinters;
+ }
+
+ @Override
+ public void onChanged(@IntRange(from = 0) int numDiscoveredPrinters) {
+ synchronized (mLock) {
+ Preconditions.checkState(isRunning);
+
+ mNumPrinters = Preconditions.checkArgumentNonnegative(numDiscoveredPrinters,
+ "numDiscoveredPrinters");
+
+ if (mNumPrinters > 0) {
+ mListener.onChanged();
+ }
+ }
+ }
+
+ /**
+ * Listener to listen for changes to {@link #getNumPrinters}
+ */
+ public interface OnChangedListener {
+ void onChanged();
+ }
+
+ /**
+ * Exception thrown if the stub has any issues.
+ */
+ public class PluginException extends Exception {
+ private PluginException(PrintServicePlugin plugin, String message, Throwable e) {
+ super(plugin + ": " + message, e);
+ }
+ }
+}
diff --git a/packages/PrintServiceRecommendationService/src/com/android/printservice/recommendation/plugin/mdnsFilter/MDNSFilterPlugin.java b/packages/PrintServiceRecommendationService/src/com/android/printservice/recommendation/plugin/mdnsFilter/MDNSFilterPlugin.java
new file mode 100644
index 0000000..26300b1
--- /dev/null
+++ b/packages/PrintServiceRecommendationService/src/com/android/printservice/recommendation/plugin/mdnsFilter/MDNSFilterPlugin.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.printservice.recommendation.plugin.mdnsFilter;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringRes;
+import android.content.Context;
+import android.net.nsd.NsdManager;
+import android.net.nsd.NsdServiceInfo;
+import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
+import com.android.printservice.recommendation.PrintServicePlugin;
+import com.android.printservice.recommendation.util.MDNSUtils;
+import com.android.printservice.recommendation.util.NsdResolveQueue;
+
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * A plugin listening for mDNS results and only adding the ones that {@link
+ * MDNSUtils#isVendorPrinter match} configured list
+ */
+public class MDNSFilterPlugin implements PrintServicePlugin, NsdManager.DiscoveryListener {
+ private static final String LOG_TAG = "MDNSFilterPlugin";
+
+ private static final String PRINTER_SERVICE_TYPE = "_ipp._tcp";
+
+ /** Name of the print service this plugin is for */
+ private final @StringRes int mName;
+
+ /** Package name of the print service this plugin is for */
+ private final @NonNull CharSequence mPackageName;
+
+ /** mDNS names handled by the print service this plugin is for */
+ private final @NonNull HashSet<String> mMDNSNames;
+
+ /** Printer identifiers of the mPrinters found. */
+ @GuardedBy("mLock")
+ private final @NonNull HashSet<String> mPrinters;
+
+ /** Context of the user of this plugin */
+ private final @NonNull Context mContext;
+
+ /**
+ * Call back to report the number of mPrinters found.
+ *
+ * We assume that {@link #start} and {@link #stop} are never called in parallel, hence it is
+ * safe to not synchronize access to this field.
+ */
+ private @Nullable PrinterDiscoveryCallback mCallback;
+
+ /** Queue used to resolve nsd infos */
+ private final @NonNull NsdResolveQueue mResolveQueue;
+
+ /**
+ * Create new stub that assumes that a print service can be used to print on all mPrinters
+ * matching some mDNS names.
+ *
+ * @param context The context the plugin runs in
+ * @param name The user friendly name of the print service
+ * @param packageName The package name of the print service
+ * @param mDNSNames The mDNS names of the printer.
+ */
+ public MDNSFilterPlugin(@NonNull Context context, @NonNull String name,
+ @NonNull CharSequence packageName, @NonNull List<String> mDNSNames) {
+ mContext = Preconditions.checkNotNull(context, "context");
+ mName = mContext.getResources().getIdentifier(Preconditions.checkStringNotEmpty(name,
+ "name"), null, mContext.getPackageName());
+ mPackageName = Preconditions.checkStringNotEmpty(packageName);
+ mMDNSNames = new HashSet<>(Preconditions
+ .checkCollectionNotEmpty(Preconditions.checkCollectionElementsNotNull(mDNSNames,
+ "mDNSNames"), "mDNSNames"));
+
+ mResolveQueue = NsdResolveQueue.getInstance();
+ mPrinters = new HashSet<>();
+ }
+
+ @Override
+ public @NonNull CharSequence getPackageName() {
+ return mPackageName;
+ }
+
+ /**
+ * @return The NDS manager
+ */
+ private NsdManager getNDSManager() {
+ return (NsdManager) mContext.getSystemService(Context.NSD_SERVICE);
+ }
+
+ @Override
+ public void start(@NonNull PrinterDiscoveryCallback callback) throws Exception {
+ mCallback = callback;
+
+ getNDSManager().discoverServices(PRINTER_SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD,
+ this);
+ }
+
+ @Override
+ public @StringRes int getName() {
+ return mName;
+ }
+
+ @Override
+ public void stop() throws Exception {
+ mCallback.onChanged(0);
+ mCallback = null;
+
+ getNDSManager().stopServiceDiscovery(this);
+ }
+
+ @Override
+ public void onStartDiscoveryFailed(String serviceType, int errorCode) {
+ Log.w(LOG_TAG, "Failed to start network discovery for type " + serviceType + ": "
+ + errorCode);
+ }
+
+ @Override
+ public void onStopDiscoveryFailed(String serviceType, int errorCode) {
+ Log.w(LOG_TAG, "Failed to stop network discovery for type " + serviceType + ": "
+ + errorCode);
+ }
+
+ @Override
+ public void onDiscoveryStarted(String serviceType) {
+ // empty
+ }
+
+ @Override
+ public void onDiscoveryStopped(String serviceType) {
+ mPrinters.clear();
+ }
+
+ @Override
+ public void onServiceFound(NsdServiceInfo serviceInfo) {
+ mResolveQueue.resolve(getNDSManager(), serviceInfo,
+ new NsdManager.ResolveListener() {
+ @Override
+ public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
+ Log.w(LOG_TAG, "Service found: could not resolve " + serviceInfo + ": " +
+ errorCode);
+ }
+
+ @Override
+ public void onServiceResolved(NsdServiceInfo serviceInfo) {
+ if (MDNSUtils.isVendorPrinter(serviceInfo, mMDNSNames)) {
+ if (mCallback != null) {
+ boolean added = mPrinters.add(serviceInfo.getHost().getHostAddress());
+
+ if (added) {
+ mCallback.onChanged(mPrinters.size());
+ }
+ }
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onServiceLost(NsdServiceInfo serviceInfo) {
+ mResolveQueue.resolve(getNDSManager(), serviceInfo,
+ new NsdManager.ResolveListener() {
+ @Override
+ public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
+ Log.w(LOG_TAG, "Service lost: Could not resolve " + serviceInfo + ": "
+ + errorCode);
+ }
+
+ @Override
+ public void onServiceResolved(NsdServiceInfo serviceInfo) {
+ if (MDNSUtils.isVendorPrinter(serviceInfo, mMDNSNames)) {
+ if (mCallback != null) {
+ boolean removed = mPrinters
+ .remove(serviceInfo.getHost().getHostAddress());
+
+ if (removed) {
+ mCallback.onChanged(mPrinters.size());
+ }
+ }
+ }
+ }
+ });
+ }
+}
diff --git a/packages/PrintServiceRecommendationService/src/com/android/printservice/recommendation/plugin/mdnsFilter/VendorConfig.java b/packages/PrintServiceRecommendationService/src/com/android/printservice/recommendation/plugin/mdnsFilter/VendorConfig.java
new file mode 100644
index 0000000..57d5c71
--- /dev/null
+++ b/packages/PrintServiceRecommendationService/src/com/android/printservice/recommendation/plugin/mdnsFilter/VendorConfig.java
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.printservice.recommendation.plugin.mdnsFilter;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.XmlResourceParser;
+import android.util.ArrayMap;
+import com.android.internal.annotations.Immutable;
+import com.android.internal.util.Preconditions;
+import com.android.printservice.recommendation.R;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Vendor configuration as read from {@link R.xml#vendorconfigs vendorconfigs.xml}. Configuration
+ * can be read via {@link #getConfig(Context, String)}.
+ */
+@Immutable
+public class VendorConfig {
+ /** Lock for {@link #sConfigs} */
+ private static final Object sLock = new Object();
+
+ /** Strings used as XML tags */
+ private static final String VENDORS_TAG = "vendors";
+ private static final String VENDOR_TAG = "vendor";
+ private static final String NAME_TAG = "name";
+ private static final String PACKAGE_TAG = "package";
+ private static final String MDNSNAMES_TAG = "mdns-names";
+ private static final String MDNSNAME_TAG = "mdns-name";
+
+ /** Map from vendor name to config. Initialized on first {@link #getConfig use}. */
+ private static @Nullable ArrayMap<String, VendorConfig> sConfigs;
+
+ /** Localized vendor name */
+ public final @NonNull String name;
+
+ /** Package name containing the print service for this vendor */
+ public final @NonNull String packageName;
+
+ /** mDNS names used by this vendor */
+ public final @NonNull List<String> mDNSNames;
+
+ /**
+ * Create an immutable configuration.
+ */
+ private VendorConfig(@NonNull String name, @NonNull String packageName,
+ @NonNull List<String> mDNSNames) {
+ this.name = Preconditions.checkStringNotEmpty(name);
+ this.packageName = Preconditions.checkStringNotEmpty(packageName);
+ this.mDNSNames = Preconditions.checkCollectionElementsNotNull(mDNSNames, "mDNSName");
+ }
+
+ /**
+ * Get the configuration for a vendor.
+ *
+ * @param context Calling context
+ * @param name The name of the config to read
+ *
+ * @return the config for the vendor or null if not found
+ *
+ * @throws IOException
+ * @throws XmlPullParserException
+ */
+ public static @Nullable VendorConfig getConfig(@NonNull Context context, @NonNull String name)
+ throws IOException, XmlPullParserException {
+ synchronized (sLock) {
+ if (sConfigs == null) {
+ sConfigs = readVendorConfigs(context);
+ }
+
+ return sConfigs.get(name);
+ }
+ }
+
+ /**
+ * Get all known vendor configurations.
+ *
+ * @param context Calling context
+ *
+ * @return The known configurations
+ *
+ * @throws IOException
+ * @throws XmlPullParserException
+ */
+ public static @NonNull Collection<VendorConfig> getAllConfigs(@NonNull Context context)
+ throws IOException, XmlPullParserException {
+ synchronized (sLock) {
+ if (sConfigs == null) {
+ sConfigs = readVendorConfigs(context);
+ }
+
+ return sConfigs.values();
+ }
+ }
+
+ /**
+ * Read the text from a XML tag.
+ *
+ * @param parser XML parser to read from
+ *
+ * @return The text or "" if no text was found
+ *
+ * @throws IOException
+ * @throws XmlPullParserException
+ */
+ private static @NonNull String readText(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ String result = "";
+
+ if (parser.next() == XmlPullParser.TEXT) {
+ result = parser.getText();
+ parser.nextTag();
+ }
+
+ return result;
+ }
+
+ /**
+ * Read a tag with a text content from the parser.
+ *
+ * @param parser XML parser to read from
+ * @param tagName The name of the tag to read
+ *
+ * @return The text content of the tag
+ *
+ * @throws IOException
+ * @throws XmlPullParserException
+ */
+ private static @NonNull String readSimpleTag(@NonNull Context context,
+ @NonNull XmlPullParser parser, @NonNull String tagName, boolean resolveReferences)
+ throws IOException, XmlPullParserException {
+ parser.require(XmlPullParser.START_TAG, null, tagName);
+ String text = readText(parser);
+ parser.require(XmlPullParser.END_TAG, null, tagName);
+
+ if (resolveReferences && text.startsWith("@")) {
+ return context.getResources().getString(
+ context.getResources().getIdentifier(text, null, context.getPackageName()));
+ } else {
+ return text;
+ }
+ }
+
+ /**
+ * Read content of a list of tags.
+ *
+ * @param parser XML parser to read from
+ * @param tagName The name of the list tag
+ * @param subTagName The name of the list-element tags
+ * @param tagReader The {@link TagReader reader} to use to read the tag content
+ * @param <T> The type of the parsed tag content
+ *
+ * @return A list of {@link T}
+ *
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private static @NonNull <T> ArrayList<T> readTagList(@NonNull XmlPullParser parser,
+ @NonNull String tagName, @NonNull String subTagName, @NonNull TagReader<T> tagReader)
+ throws XmlPullParserException, IOException {
+ ArrayList<T> entries = new ArrayList<>();
+
+ parser.require(XmlPullParser.START_TAG, null, tagName);
+ while (parser.next() != XmlPullParser.END_TAG) {
+ if (parser.getEventType() != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ if (parser.getName().equals(subTagName)) {
+ entries.add(tagReader.readTag(parser, subTagName));
+ } else {
+ throw new XmlPullParserException(
+ "Unexpected subtag of " + tagName + ": " + parser.getName());
+ }
+ }
+
+ return entries;
+ }
+
+ /**
+ * Read the vendor configuration file.
+ *
+ * @param context The content issuing the read
+ *
+ * @return An map pointing from vendor name to config
+ *
+ * @throws IOException
+ * @throws XmlPullParserException
+ */
+ private static @NonNull ArrayMap<String, VendorConfig> readVendorConfigs(
+ @NonNull final Context context) throws IOException, XmlPullParserException {
+ try (XmlResourceParser parser = context.getResources().getXml(R.xml.vendorconfigs)) {
+ // Skip header
+ int parsingEvent;
+ do {
+ parsingEvent = parser.next();
+ } while (parsingEvent != XmlResourceParser.START_TAG);
+
+ ArrayList<VendorConfig> configs = readTagList(parser, VENDORS_TAG, VENDOR_TAG,
+ new TagReader<VendorConfig>() {
+ public VendorConfig readTag(XmlPullParser parser, String tagName)
+ throws XmlPullParserException, IOException {
+ return readVendorConfig(context, parser, tagName);
+ }
+ });
+
+ ArrayMap<String, VendorConfig> configMap = new ArrayMap<>(configs.size());
+ final int numConfigs = configs.size();
+ for (int i = 0; i < numConfigs; i++) {
+ VendorConfig config = configs.get(i);
+
+ configMap.put(config.name, config);
+ }
+
+ return configMap;
+ }
+ }
+
+ /**
+ * Read a single vendor configuration.
+ *
+ * @param parser XML parser to read from
+ * @param tagName The vendor tag
+ * @param context Calling context
+ *
+ * @return A config
+ *
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private static VendorConfig readVendorConfig(@NonNull final Context context,
+ @NonNull XmlPullParser parser, @NonNull String tagName) throws XmlPullParserException,
+ IOException {
+ parser.require(XmlPullParser.START_TAG, null, tagName);
+
+ String name = null;
+ String packageName = null;
+ List<String> mDNSNames = null;
+
+ while (parser.next() != XmlPullParser.END_TAG) {
+ if (parser.getEventType() != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ String subTagName = parser.getName();
+
+ switch (subTagName) {
+ case NAME_TAG:
+ name = readSimpleTag(context, parser, NAME_TAG, false);
+ break;
+ case PACKAGE_TAG:
+ packageName = readSimpleTag(context, parser, PACKAGE_TAG, true);
+ break;
+ case MDNSNAMES_TAG:
+ mDNSNames = readTagList(parser, MDNSNAMES_TAG, MDNSNAME_TAG,
+ new TagReader<String>() {
+ public String readTag(XmlPullParser parser, String tagName)
+ throws XmlPullParserException, IOException {
+ return readSimpleTag(context, parser, tagName, true);
+ }
+ }
+ );
+ break;
+ default:
+ throw new XmlPullParserException("Unexpected subtag of " + tagName + ": "
+ + subTagName);
+
+ }
+ }
+
+ if (name == null) {
+ throw new XmlPullParserException("name is required");
+ }
+
+ if (packageName == null) {
+ throw new XmlPullParserException("package is required");
+ }
+
+ if (mDNSNames == null) {
+ mDNSNames = Collections.emptyList();
+ }
+
+ // A vendor config should be immutable
+ mDNSNames = Collections.unmodifiableList(mDNSNames);
+
+ return new VendorConfig(name, packageName, mDNSNames);
+ }
+
+ @Override
+ public String toString() {
+ return name + " -> " + packageName + ", " + mDNSNames;
+ }
+
+ /**
+ * Used a a "function pointer" when reading a tag in {@link #readTagList(XmlPullParser, String,
+ * String, TagReader)}.
+ *
+ * @param <T> The type of content to read
+ */
+ private interface TagReader<T> {
+ T readTag(XmlPullParser parser, String tagName) throws XmlPullParserException, IOException;
+ }
+}
diff --git a/packages/PrintServiceRecommendationService/src/com/android/printservice/recommendation/util/MDNSUtils.java b/packages/PrintServiceRecommendationService/src/com/android/printservice/recommendation/util/MDNSUtils.java
new file mode 100644
index 0000000..0541c35
--- /dev/null
+++ b/packages/PrintServiceRecommendationService/src/com/android/printservice/recommendation/util/MDNSUtils.java
@@ -0,0 +1,98 @@
+/*
+ * (c) Copyright 2016 Mopria Alliance, Inc.
+ * (c) Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.printservice.recommendation.util;
+
+import android.annotation.NonNull;
+import android.net.nsd.NsdServiceInfo;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Utils for dealing with mDNS attributes
+ */
+public class MDNSUtils {
+ public static final String ATTRIBUTE_TY = "ty";
+ public static final String ATTRIBUTE_PRODUCT = "product";
+ public static final String ATTRIBUTE_USB_MFG = "usb_mfg";
+ public static final String ATTRIBUTE_MFG = "mfg";
+
+ /**
+ * Check if the service has any of a set of vendor names.
+ *
+ * @param serviceInfo The service
+ * @param vendorNames The vendors
+ *
+ * @return true iff the has any of the set of vendor names
+ */
+ public static boolean isVendorPrinter(@NonNull NsdServiceInfo serviceInfo,
+ @NonNull Set<String> vendorNames) {
+ for (Map.Entry<String, byte[]> entry : serviceInfo.getAttributes().entrySet()) {
+ // keys are case insensitive
+ String key = entry.getKey().toLowerCase();
+
+ switch (key) {
+ case ATTRIBUTE_TY:
+ case ATTRIBUTE_PRODUCT:
+ case ATTRIBUTE_USB_MFG:
+ case ATTRIBUTE_MFG:
+ if (entry.getValue() != null) {
+ if (containsVendor(new String(entry.getValue(), StandardCharsets.UTF_8),
+ vendorNames)) {
+ return true;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Check if the attribute matches any of the vendor names, ignoring capitalization.
+ *
+ * @param attr The attribute
+ * @param vendorNames The vendor names
+ *
+ * @return true iff the attribute matches any of the vendor names
+ */
+ private static boolean containsVendor(@NonNull String attr, @NonNull Set<String> vendorNames) {
+ for (String name : vendorNames) {
+ if (containsString(attr.toLowerCase(), name.toLowerCase())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check if a string in another string.
+ *
+ * @param container The string that contains the string
+ * @param contained The string that is contained
+ *
+ * @return true if the string is contained in the other
+ */
+ private static boolean containsString(@NonNull String container, @NonNull String contained) {
+ return container.equalsIgnoreCase(contained) || container.contains(contained + " ");
+ }
+}
diff --git a/packages/PrintServiceRecommendationService/src/com/android/printservice/recommendation/util/NsdResolveQueue.java b/packages/PrintServiceRecommendationService/src/com/android/printservice/recommendation/util/NsdResolveQueue.java
new file mode 100644
index 0000000..fad50f6
--- /dev/null
+++ b/packages/PrintServiceRecommendationService/src/com/android/printservice/recommendation/util/NsdResolveQueue.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.printservice.recommendation.util;
+
+import android.annotation.NonNull;
+import android.net.nsd.NsdManager;
+import android.net.nsd.NsdServiceInfo;
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.LinkedList;
+
+/**
+ * Nsd resolve requests for the same info cancel each other. Hence this class synchronizes the
+ * resolutions to hide this effect.
+ */
+public class NsdResolveQueue {
+ /** Lock for {@link #sInstance} */
+ private static final Object sLock = new Object();
+
+ /** Instance of this singleton */
+ @GuardedBy("sLock")
+ private static NsdResolveQueue sInstance;
+
+ /** Lock for {@link #mResolveRequests} */
+ private final Object mLock = new Object();
+
+ /** Current set of registered service info resolve attempts */
+ @GuardedBy("mLock")
+ private final LinkedList<NsdResolveRequest> mResolveRequests = new LinkedList<>();
+
+ public static NsdResolveQueue getInstance() {
+ synchronized (sLock) {
+ if (sInstance == null) {
+ sInstance = new NsdResolveQueue();
+ }
+
+ return sInstance;
+ }
+ }
+
+ /**
+ * Container for a request to resolve a serviceInfo.
+ */
+ private static class NsdResolveRequest {
+ final @NonNull NsdManager nsdManager;
+ final @NonNull NsdServiceInfo serviceInfo;
+ final @NonNull NsdManager.ResolveListener listener;
+
+ private NsdResolveRequest(@NonNull NsdManager nsdManager,
+ @NonNull NsdServiceInfo serviceInfo, @NonNull NsdManager.ResolveListener listener) {
+ this.nsdManager = nsdManager;
+ this.serviceInfo = serviceInfo;
+ this.listener = listener;
+ }
+ }
+
+ /**
+ * Resolve a serviceInfo or queue the request if there is a request currently in flight.
+ *
+ * @param nsdManager The nsd manager to use
+ * @param serviceInfo The service info to resolve
+ * @param listener The listener to call back once the info is resolved.
+ */
+ public void resolve(@NonNull NsdManager nsdManager, @NonNull NsdServiceInfo serviceInfo,
+ @NonNull NsdManager.ResolveListener listener) {
+ synchronized (mLock) {
+ mResolveRequests.addLast(new NsdResolveRequest(nsdManager, serviceInfo,
+ new ListenerWrapper(listener)));
+
+ if (mResolveRequests.size() == 1) {
+ resolveNextRequest();
+ }
+ }
+ }
+
+ /**
+ * Wrapper for a {@link NsdManager.ResolveListener}. Calls the listener and then
+ * {@link #resolveNextRequest()}.
+ */
+ private class ListenerWrapper implements NsdManager.ResolveListener {
+ private final @NonNull NsdManager.ResolveListener mListener;
+
+ private ListenerWrapper(@NonNull NsdManager.ResolveListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
+ mListener.onResolveFailed(serviceInfo, errorCode);
+
+ synchronized (mLock) {
+ mResolveRequests.pop();
+ resolveNextRequest();
+ }
+ }
+
+ @Override
+ public void onServiceResolved(NsdServiceInfo serviceInfo) {
+ mListener.onServiceResolved(serviceInfo);
+
+ synchronized (mLock) {
+ mResolveRequests.pop();
+ resolveNextRequest();
+ }
+ }
+ }
+
+ /**
+ * Resolve the next request if there is one.
+ */
+ private void resolveNextRequest() {
+ if (!mResolveRequests.isEmpty()) {
+ NsdResolveRequest request = mResolveRequests.getFirst();
+
+ request.nsdManager.resolveService(request.serviceInfo, request.listener);
+ }
+ }
+
+}
diff --git a/packages/PrintSpooler/AndroidManifest.xml b/packages/PrintSpooler/AndroidManifest.xml
index 2bcd851..ed6fdb7 100644
--- a/packages/PrintSpooler/AndroidManifest.xml
+++ b/packages/PrintSpooler/AndroidManifest.xml
@@ -57,7 +57,7 @@
<activity
android:name=".ui.PrintActivity"
- android:configChanges="screenSize|smallestScreenSize|orientation"
+ android:configChanges="screenSize|smallestScreenSize|orientation|locale|keyboard|keyboardHidden|fontScale|uiMode|layoutDirection"
android:permission="android.permission.BIND_PRINT_SPOOLER_SERVICE"
android:theme="@style/Theme.PrintActivity">
<intent-filter>
diff --git a/packages/PrintSpooler/res/drawable/ic_download_from_market.xml b/packages/PrintSpooler/res/drawable/ic_download_from_market.xml
new file mode 100644
index 0000000..44a5edf
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable/ic_download_from_market.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path
+ android:pathData="M40,12h-8L32,8l-4,-4h-8l-4,4v4L8,12c-2.21,0 -3.98,1.79 -3.98,4L4,38c0,2.21 1.79,4 4,4h32c2.21,0 4,-1.79 4,-4L44,16c0,-2.21 -1.79,-4 -4,-4zM20,8h8v4h-8L20,8zM24,38L14,28h6v-8h8v8h6L24,38z"
+ android:fillColor="?android:attr/colorAccent"/>
+</vector>
diff --git a/packages/PrintSpooler/res/layout/print_activity_controls.xml b/packages/PrintSpooler/res/layout/print_activity_controls.xml
index a87afe0..248d0c0 100644
--- a/packages/PrintSpooler/res/layout/print_activity_controls.xml
+++ b/packages/PrintSpooler/res/layout/print_activity_controls.xml
@@ -239,7 +239,8 @@
android:singleLine="true"
android:ellipsize="end"
android:visibility="visible"
- android:inputType="textNoSuggestions">
+ android:inputType="number"
+ android:digits="0123456789 ,-">
</com.android.printspooler.widget.CustomErrorEditText>
</LinearLayout>
diff --git a/packages/PrintSpooler/res/layout/print_service_recommendations_list_item.xml b/packages/PrintSpooler/res/layout/print_service_recommendations_list_item.xml
new file mode 100644
index 0000000..86ac26d
--- /dev/null
+++ b/packages/PrintSpooler/res/layout/print_service_recommendations_list_item.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:orientation="horizontal"
+ android:gravity="start|center_vertical">
+
+ <ImageView
+ android:layout_width="36dip"
+ android:layout_height="36dip"
+ android:src="@drawable/ic_download_from_market"
+ android:layout_marginRight="4dip"
+ android:layout_gravity="center_vertical"
+ android:contentDescription="@null" />
+
+ <RelativeLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dip">
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:singleLine="true"
+ android:ellipsize="end" />
+
+ <TextView
+ android:id="@+id/subtitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/title"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:text="@string/enable_print_service" />
+
+ </RelativeLayout>
+
+</LinearLayout>
diff --git a/packages/PrintSpooler/res/values-af/strings.xml b/packages/PrintSpooler/res/values-af/strings.xml
index c8478f2..fa5ec3f 100644
--- a/packages/PrintSpooler/res/values-af/strings.xml
+++ b/packages/PrintSpooler/res/values-af/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Aanbevole dienste"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Gedeaktiveerde dienste"</string>
<string name="all_services_title" msgid="5578662754874906455">"Alle dienste"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other">Installeer om <xliff:g id="COUNT_1">%1$s</xliff:g> drukkers te ontdek</item>
+ <item quantity="one">Installeer om <xliff:g id="COUNT_0">%1$s</xliff:g> drukker ontdek</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Druk tans <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Kanselleer tans <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Drukkerfout by <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Herbegin"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Geen verbinding met drukker nie"</string>
<string name="reason_unknown" msgid="5507940196503246139">"onbekend"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – nie beskikbaar nie"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Gebruik <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Jou dokument kan dalk deur een of meer bedieners op pad na die drukker gaan."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Jammer, dit het nie gewerk nie. Probeer weer."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Herprobeer"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Hierdie drukker is nie op die oomblik beskikbaar nie."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Kan nie voorskou wys nie"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Berei tans voorskou voor …"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-am/strings.xml b/packages/PrintSpooler/res/values-am/strings.xml
index d4426cc..5ada8d3 100644
--- a/packages/PrintSpooler/res/values-am/strings.xml
+++ b/packages/PrintSpooler/res/values-am/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"የሚመከሩ አገልግሎቶች"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"የተሰናከሉ አገልግሎቶች"</string>
<string name="all_services_title" msgid="5578662754874906455">"ሁሉም አገልግሎቶች"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$s</xliff:g> አታሚዎች ለማግኘት ይጫኑ</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> አታሚዎች ለማግኘት ይጫኑ</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>ን በማተም ላይ"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>ን በመተው ላይ"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"የአታሚ ስህተት <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"እንደገና ጀምር"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"ከአታሚ ጋር ምንም ግንኙነት የለም"</string>
<string name="reason_unknown" msgid="5507940196503246139">"አይታወቅም"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – አይገኝም"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> ይጠቀሙ?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"ሰነድዎ ወደ አታሚው በሚሄድበት ወቅት በአንድ ወይም ከዚያ በላይ አገልጋዮች ውስጥ ሊያልፍ ይችላል።"</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"ይቅርታ፣ ያ አልሰራም። እንደገና ይሞክሩ።"</string>
<string name="print_error_retry" msgid="1426421728784259538">"እንደገና ይሞክሩ"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"አታሚው አሁን አይገኝም።"</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"ቅድመ ዕይታን ማሳየት አይቻልም"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"ቅድመ እይታን በማዘጋጀት ላይ…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-ar/strings.xml b/packages/PrintSpooler/res/values-ar/strings.xml
index 2e1b6d0..1208298 100644
--- a/packages/PrintSpooler/res/values-ar/strings.xml
+++ b/packages/PrintSpooler/res/values-ar/strings.xml
@@ -76,6 +76,14 @@
<string name="recommended_services_title" msgid="3799434882937956924">"الخدمات الموصى بها"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"الخدمات المعطَّلة"</string>
<string name="all_services_title" msgid="5578662754874906455">"جميع الخدمات"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="zero">التثبيت لاستكشاف <xliff:g id="COUNT_1">%1$s</xliff:g> طابعات</item>
+ <item quantity="two">التثبيت لاستكشاف طابعتين (<xliff:g id="COUNT_1">%1$s</xliff:g>)</item>
+ <item quantity="few">التثبيت لاستكشاف <xliff:g id="COUNT_1">%1$s</xliff:g> طابعات</item>
+ <item quantity="many">التثبيت لاستكشاف <xliff:g id="COUNT_1">%1$s</xliff:g> طابعة</item>
+ <item quantity="other">التثبيت لاستكشاف <xliff:g id="COUNT_1">%1$s</xliff:g> طابعات</item>
+ <item quantity="one">التثبيت لاستكشاف <xliff:g id="COUNT_0">%1$s</xliff:g> طابعة</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"جارٍ طباعة <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"جارٍ إلغاء <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"خطا في الطابعة <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -84,7 +92,6 @@
<string name="restart" msgid="2472034227037808749">"إعادة تشغيل"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"لا يوجد اتصال بالطابعة"</string>
<string name="reason_unknown" msgid="5507940196503246139">"غير معروف"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – غير متاحة"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"هل تريد استخدام <xliff:g id="SERVICE">%1$s</xliff:g>؟"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"من الممكن أن يمر المستند عبر خادم أو أكثر أثناء إرساله إلى الطابعة."</string>
<string-array name="color_mode_labels">
@@ -104,5 +111,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"عذرًا، هذا لا يعمل. أعد المحاولة."</string>
<string name="print_error_retry" msgid="1426421728784259538">"إعادة المحاولة"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"الطابعة ليست متوفرة في الوقت الحالي."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"يتعذر عرض المعاينة."</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"جارٍ تحضير المعاينة…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-az-rAZ/strings.xml b/packages/PrintSpooler/res/values-az-rAZ/strings.xml
index 5490b84..4404aad 100644
--- a/packages/PrintSpooler/res/values-az-rAZ/strings.xml
+++ b/packages/PrintSpooler/res/values-az-rAZ/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Tövsiyə olunan xidmətlər"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Deaktiv edilmiş xidmətlər"</string>
<string name="all_services_title" msgid="5578662754874906455">"Bütün xidmətlər"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> printeri kəşf etmək üçün quraşdırın</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$s</xliff:g> printeri kəşf etmək üçün quraşdırın</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> çap edilir"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ləğv edilir"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Printer xətası <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Yenidən başlat"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Printerə heç bir bağlantı yoxdur"</string>
<string name="reason_unknown" msgid="5507940196503246139">"naməlum"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>– əlçatmaz"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> xidmətindən istifadə edilsin?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Sənədiniz printerə qədər bir və ya daha çox server vasitəsilə keçə bilər."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Üzr istəyirik, alınmadı. Yenidən cəhd edin."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Yenidən yoxla"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Bu printer hazırda əlçatan deyil."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Önizləmə göstərilə bilmir"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Önizləməyə hazırlıq gedir..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-b+sr+Latn/strings.xml b/packages/PrintSpooler/res/values-b+sr+Latn/strings.xml
index 0574dae..50ce1f9 100644
--- a/packages/PrintSpooler/res/values-b+sr+Latn/strings.xml
+++ b/packages/PrintSpooler/res/values-b+sr+Latn/strings.xml
@@ -73,6 +73,11 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Preporučene usluge"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Onemogućene usluge"</string>
<string name="all_services_title" msgid="5578662754874906455">"Sve usluge"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="one">Instalirajte da biste otkrili <xliff:g id="COUNT_1">%1$s</xliff:g> štampač</item>
+ <item quantity="few">Instalirajte da biste otkrili <xliff:g id="COUNT_1">%1$s</xliff:g> štampača</item>
+ <item quantity="other">Instalirajte da biste otkrili <xliff:g id="COUNT_1">%1$s</xliff:g> štampača</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Štampa se <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Otkazuje se <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Greška štampača <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -81,7 +86,6 @@
<string name="restart" msgid="2472034227037808749">"Ponovo pokreni"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Nema veze sa štampačem"</string>
<string name="reason_unknown" msgid="5507940196503246139">"nepoznato"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – nedostupan"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Želite li da koristite <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Dokument može da prođe kroz jedan ili više servera na putu do štampača."</string>
<string-array name="color_mode_labels">
@@ -101,5 +105,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Žao nam je, ovo nije uspelo. Pokušajte ponovo."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Pokušajte ponovo"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Ovaj štampač trenutno nije dostupan."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Nije uspeo prikaz pregleda"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Priprema pregleda..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-be-rBY/strings.xml b/packages/PrintSpooler/res/values-be-rBY/strings.xml
index b581045..c264f92 100644
--- a/packages/PrintSpooler/res/values-be-rBY/strings.xml
+++ b/packages/PrintSpooler/res/values-be-rBY/strings.xml
@@ -74,6 +74,12 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Рэкамендаваныя службы"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Адключаныя службы"</string>
<string name="all_services_title" msgid="5578662754874906455">"Усе службы"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="one">Усталюйце, каб знайсці <xliff:g id="COUNT_1">%1$s</xliff:g> прынтар</item>
+ <item quantity="few">Усталюйце, каб знайсці <xliff:g id="COUNT_1">%1$s</xliff:g> прынтары</item>
+ <item quantity="many">Усталюйце, каб знайсці <xliff:g id="COUNT_1">%1$s</xliff:g> прынтараў</item>
+ <item quantity="other">Усталюйце, каб знайсці <xliff:g id="COUNT_1">%1$s</xliff:g> прынтара</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Друк <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Скасоўваецца <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Памылка друку <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -82,7 +88,6 @@
<string name="restart" msgid="2472034227037808749">"Перазапусціць"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Няма падлучэння да прынтара"</string>
<string name="reason_unknown" msgid="5507940196503246139">"невядома"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – недаступна"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Выкарыстоўваць службу <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Ваш дакумент можа прайсці праз адзін ці больш сервераў перад тым, як будзе надрукаваны."</string>
<string-array name="color_mode_labels">
@@ -102,5 +107,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"На жаль, не атрымалася. Паспрабуйце яшчэ раз."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Паўтарыць спробу"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Гэты прынтар зараз недаступны."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Папярэдні прагляд немагчымы"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Падрыхтоўка папярэдняга прагляду..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-bg/strings.xml b/packages/PrintSpooler/res/values-bg/strings.xml
index 88af8e4..3dd6ec5 100644
--- a/packages/PrintSpooler/res/values-bg/strings.xml
+++ b/packages/PrintSpooler/res/values-bg/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Препоръчителни услуги"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Деактивирани услуги"</string>
<string name="all_services_title" msgid="5578662754874906455">"Всички услуги"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other">Инсталирайте, за да бъдат открити <xliff:g id="COUNT_1">%1$s</xliff:g> принтера</item>
+ <item quantity="one">Инсталирайте, за да бъде открит <xliff:g id="COUNT_0">%1$s</xliff:g> принтер</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"„<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>“ се отпечатва"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"„<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>“ се анулира"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Грешка в принтера при „<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>“"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Рестартиране"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Няма връзка с принтера"</string>
<string name="reason_unknown" msgid="5507940196503246139">"няма данни"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – не е налице"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Да се използва ли „<xliff:g id="SERVICE">%1$s</xliff:g>“?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"По пътя към принтера документът ви може да премине през един или повече сървъри."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"За съжаление това не проработи. Опитайте отново."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Нов опит"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"В момента този принтер не е налице."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Визуализацията не може да се покаже"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Визуализацията се подготвя…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-bn-rBD/strings.xml b/packages/PrintSpooler/res/values-bn-rBD/strings.xml
index c61ef74..3789f21 100644
--- a/packages/PrintSpooler/res/values-bn-rBD/strings.xml
+++ b/packages/PrintSpooler/res/values-bn-rBD/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"প্রস্তাবিত পরিষেবাগুলি"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"অক্ষম করা পরিষেবাগুলি"</string>
<string name="all_services_title" msgid="5578662754874906455">"সমস্ত পরিষেবা"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$s</xliff:g>টি মুদ্রক খুঁজে পেতে ইনস্টল করুন</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g>টি মুদ্রক খুঁজে পেতে ইনস্টল করুন</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> প্রিন্ট করা হচ্ছে"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> বাতিল করা হচ্ছে"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> মুদ্রক ত্রুটি"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"পুনর্সূচনা"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"মুদ্রকে কোনো সংযোগ নেই"</string>
<string name="reason_unknown" msgid="5507940196503246139">"অজানা"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – অনুপলব্ধ"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> ব্যবহার করবেন?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"আপনার দস্তাবেজ মুদ্রকে যাওয়ার সময় এক বা একাধিক সার্ভারের মাধ্যমে পাস হতে পারে।"</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"দুঃখিত, এটি কাজ করেনি৷ আবার চেষ্টা করুন৷"</string>
<string name="print_error_retry" msgid="1426421728784259538">"পুনরায় চেষ্টা করুন"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"এই মূহুর্তে প্রিন্টার উপলব্ধ নয়।"</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"পূর্বরূপ প্রদর্শন করা যাবে না"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"পূর্বরূপ প্রস্তুত করছে..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-bs-rBA/strings.xml b/packages/PrintSpooler/res/values-bs-rBA/strings.xml
index 7465c3c..c3c9bb33 100644
--- a/packages/PrintSpooler/res/values-bs-rBA/strings.xml
+++ b/packages/PrintSpooler/res/values-bs-rBA/strings.xml
@@ -73,6 +73,11 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Preporučene usluge"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Isključene usluge"</string>
<string name="all_services_title" msgid="5578662754874906455">"Sve usluge"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="one"> Instaliraj da pronađeš <xliff:g id="COUNT_1">%1$s</xliff:g> štampač</item>
+ <item quantity="few"> Instaliraj da pronađeš <xliff:g id="COUNT_1">%1$s</xliff:g> štampača</item>
+ <item quantity="other"> Instaliraj da pronađeš <xliff:g id="COUNT_1">%1$s</xliff:g> štampača</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Štampa se <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Otkazivanje <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Greška pri štampanju <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -81,7 +86,6 @@
<string name="restart" msgid="2472034227037808749">"Ponovo pokreni"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Nema konekcije sa štampačem"</string>
<string name="reason_unknown" msgid="5507940196503246139">"nepoznat"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – nedostupan"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Zaista želite koristiti uslugu <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Moguće je da dokument prije štampanja prođe kroz jedan ili više servera."</string>
<string-array name="color_mode_labels">
@@ -101,5 +105,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Nažalost, nije uspjelo. Pokušajte ponovo."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Ponovi"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Štampač trenutno nije dostupan."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Pregled se ne može prikazati"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Priprema pregleda..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-ca/strings.xml b/packages/PrintSpooler/res/values-ca/strings.xml
index 482100a..f65a63c 100644
--- a/packages/PrintSpooler/res/values-ca/strings.xml
+++ b/packages/PrintSpooler/res/values-ca/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Serveis recomanats"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Serveis desactivats"</string>
<string name="all_services_title" msgid="5578662754874906455">"Tots els serveis"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other">Instal·la\'l per detectar <xliff:g id="COUNT_1">%1$s</xliff:g> impressores</item>
+ <item quantity="one">Instal·la\'l per detectar <xliff:g id="COUNT_0">%1$s</xliff:g> impressora</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"S\'està imprimint <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"S\'està cancel·lant <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Error d\'impressora <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Reinicia"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"No hi ha connexió amb la impressora"</string>
<string name="reason_unknown" msgid="5507940196503246139">"desconegut"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>: no disponible"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Vols fer servir <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"És possible que el document passi com a mínim per un servidor abans d\'imprimir-se."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"No ha funcionat. Torna-ho a provar."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Torna-ho a provar"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Ara mateix, aquesta impressora no està disponible."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"La previsualització no es pot mostrar"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"S\'està preparant la previsualització..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-cs/strings.xml b/packages/PrintSpooler/res/values-cs/strings.xml
index a4c412c..9bfa271 100644
--- a/packages/PrintSpooler/res/values-cs/strings.xml
+++ b/packages/PrintSpooler/res/values-cs/strings.xml
@@ -74,6 +74,12 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Doporučené služby"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Deaktivované služby"</string>
<string name="all_services_title" msgid="5578662754874906455">"Všechny služby"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="few">Instalací objevíte <xliff:g id="COUNT_1">%1$s</xliff:g> tiskárny</item>
+ <item quantity="many">Instalací objevíte <xliff:g id="COUNT_1">%1$s</xliff:g> tiskárny</item>
+ <item quantity="other">Instalací objevíte <xliff:g id="COUNT_1">%1$s</xliff:g> tiskáren</item>
+ <item quantity="one">Instalací objevíte <xliff:g id="COUNT_0">%1$s</xliff:g> tiskárnu</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Tisk úlohy <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Rušení úlohy <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Chyba tiskárny u úlohy <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -82,7 +88,6 @@
<string name="restart" msgid="2472034227037808749">"Restartovat"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Nelze se připojit k tiskárně"</string>
<string name="reason_unknown" msgid="5507940196503246139">"neznámé"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – není k dispozici"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Použít službu <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Dokument může cestou do tiskárny projít jedním i více servery."</string>
<string-array name="color_mode_labels">
@@ -102,5 +107,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Litujeme, nepodařilo se. Zkuste to znovu."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Opakovat"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Tiskárna aktuálně není k dispozici."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Náhled nelze zobrazit"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Příprava náhledu…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-da/strings.xml b/packages/PrintSpooler/res/values-da/strings.xml
index 9ee252167..75a0b56 100644
--- a/packages/PrintSpooler/res/values-da/strings.xml
+++ b/packages/PrintSpooler/res/values-da/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Anbefalede tjenester"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Deaktiverede tjenester"</string>
<string name="all_services_title" msgid="5578662754874906455">"Alle tjenester"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="one">Installer for at finde <xliff:g id="COUNT_1">%1$s</xliff:g> printer</item>
+ <item quantity="other">Installer for at finde <xliff:g id="COUNT_1">%1$s</xliff:g> printere</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> udskrives"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> annulleres"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Udskriften <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> mislykkedes"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Genstart"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Ingen forbindelse til printer"</string>
<string name="reason_unknown" msgid="5507940196503246139">"ukendt"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – ikke tilgængelig"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Vil du bruge <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Dit dokument passerer muligvis gennem én eller flere servere på vej til printeren."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Det virkede desværre ikke. Prøv igen."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Prøv igen"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Denne printer er i øjeblikket ikke tilgængelig."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Eksempelvisning kan ikke vises"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Eksempelvisning forberedes..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-de/strings.xml b/packages/PrintSpooler/res/values-de/strings.xml
index ef451b7..8c6181d 100644
--- a/packages/PrintSpooler/res/values-de/strings.xml
+++ b/packages/PrintSpooler/res/values-de/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Empfohlene Dienste"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Deaktivierte Dienste"</string>
<string name="all_services_title" msgid="5578662754874906455">"Alle Dienste"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other">Installieren, um <xliff:g id="COUNT_1">%1$s</xliff:g> Drucker zu erkennen</item>
+ <item quantity="one">Installieren, um <xliff:g id="COUNT_0">%1$s</xliff:g> Drucker zu erkennen</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> wird gedruckt..."</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> wird abgebrochen..."</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Druckerfehler <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Neu starten"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Keine Verbindung zum Drucker"</string>
<string name="reason_unknown" msgid="5507940196503246139">"unbekannt"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – nicht verfügbar"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> verwenden?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Dein Dokument passiert bei der Übermittlung an den Drucker möglicherweise einen oder mehrere Server."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Fehler. Bitte versuche es erneut."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Erneut versuchen"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Dieser Drucker ist momentan nicht verfügbar."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Vorschau kann nicht angezeigt werden"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Vorschau wird vorbereitet…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-el/strings.xml b/packages/PrintSpooler/res/values-el/strings.xml
index 9be81c1..31d3f83 100644
--- a/packages/PrintSpooler/res/values-el/strings.xml
+++ b/packages/PrintSpooler/res/values-el/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Προτεινόμενες υπηρεσίες"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Υπηρεσίες για άτομα με αναπηρία"</string>
<string name="all_services_title" msgid="5578662754874906455">"Όλες οι υπηρεσίες"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other">Κάντε εγκατάσταση για να ανακαλύψετε <xliff:g id="COUNT_1">%1$s</xliff:g> εκτυπωτές</item>
+ <item quantity="one">Κάντε εγκατάσταση για να ανακαλύψετε <xliff:g id="COUNT_0">%1$s</xliff:g> εκτυπωτή</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Εκτύπωση <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Ακύρωση <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Σφάλμα εκτυπωτή <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Επανεκκίνηση"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Δεν υπάρχει σύνδεση με εκτυπωτή"</string>
<string name="reason_unknown" msgid="5507940196503246139">"άγνωστο"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – μη διαθέσιμο"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Να χρησιμοποιηθεί η υπηρεσία <xliff:g id="SERVICE">%1$s</xliff:g>;"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Το έγγραφό σας μπορεί να περάσει από έναν ή περισσότερους διακομιστές κατά τη μετάβαση στον εκτυπωτή."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Δυστυχώς, αυτό δεν λειτούργησε. Δοκιμάστε ξανά."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Επανάληψη"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Αυτός ο εκτυπωτής δεν είναι διαθέσιμος αυτήν τη στιγμή."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Αδυναμία προβολής προεπισκόπησης"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Προετοιμασία προεπισκόπησης…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-en-rAU/strings.xml b/packages/PrintSpooler/res/values-en-rAU/strings.xml
index 8b58011..5e32ae4 100644
--- a/packages/PrintSpooler/res/values-en-rAU/strings.xml
+++ b/packages/PrintSpooler/res/values-en-rAU/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Recommended services"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Disabled services"</string>
<string name="all_services_title" msgid="5578662754874906455">"All services"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other">Install to discover <xliff:g id="COUNT_1">%1$s</xliff:g> printers</item>
+ <item quantity="one">Install to discover <xliff:g id="COUNT_0">%1$s</xliff:g> printer</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Printing <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelling <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Printer error <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Restart"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"No connection to printer"</string>
<string name="reason_unknown" msgid="5507940196503246139">"unknown"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – unavailable"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Use <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Your document may pass through one or more servers on its way to the printer."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Sorry, that didn\'t work. Try again."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Retry"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"This printer isn\'t available right now."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Can\'t display preview"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Preparing preview…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-en-rGB/strings.xml b/packages/PrintSpooler/res/values-en-rGB/strings.xml
index 8b58011..5e32ae4 100644
--- a/packages/PrintSpooler/res/values-en-rGB/strings.xml
+++ b/packages/PrintSpooler/res/values-en-rGB/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Recommended services"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Disabled services"</string>
<string name="all_services_title" msgid="5578662754874906455">"All services"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other">Install to discover <xliff:g id="COUNT_1">%1$s</xliff:g> printers</item>
+ <item quantity="one">Install to discover <xliff:g id="COUNT_0">%1$s</xliff:g> printer</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Printing <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelling <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Printer error <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Restart"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"No connection to printer"</string>
<string name="reason_unknown" msgid="5507940196503246139">"unknown"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – unavailable"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Use <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Your document may pass through one or more servers on its way to the printer."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Sorry, that didn\'t work. Try again."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Retry"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"This printer isn\'t available right now."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Can\'t display preview"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Preparing preview…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-en-rIN/strings.xml b/packages/PrintSpooler/res/values-en-rIN/strings.xml
index 8b58011..5e32ae4 100644
--- a/packages/PrintSpooler/res/values-en-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-en-rIN/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Recommended services"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Disabled services"</string>
<string name="all_services_title" msgid="5578662754874906455">"All services"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other">Install to discover <xliff:g id="COUNT_1">%1$s</xliff:g> printers</item>
+ <item quantity="one">Install to discover <xliff:g id="COUNT_0">%1$s</xliff:g> printer</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Printing <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelling <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Printer error <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Restart"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"No connection to printer"</string>
<string name="reason_unknown" msgid="5507940196503246139">"unknown"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – unavailable"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Use <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Your document may pass through one or more servers on its way to the printer."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Sorry, that didn\'t work. Try again."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Retry"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"This printer isn\'t available right now."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Can\'t display preview"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Preparing preview…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-es-rUS/strings.xml b/packages/PrintSpooler/res/values-es-rUS/strings.xml
index 8fa6094..a6a9f07 100644
--- a/packages/PrintSpooler/res/values-es-rUS/strings.xml
+++ b/packages/PrintSpooler/res/values-es-rUS/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Servicios recomendados"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Servicios inhabilitados"</string>
<string name="all_services_title" msgid="5578662754874906455">"Todos los servicios"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other">Instala para ver <xliff:g id="COUNT_1">%1$s</xliff:g> impresoras</item>
+ <item quantity="one">Instala para ver <xliff:g id="COUNT_0">%1$s</xliff:g> impresora</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Imprimiendo <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelando <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Error de impresora <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Reiniciar"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"No hay conexión con la impresora."</string>
<string name="reason_unknown" msgid="5507940196503246139">"desconocido"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>: no disponible"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"¿Deseas usar <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Es posible que el documento pase por uno o varios servidores antes de imprimirse."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"No funcionó. Vuelve a intentarlo."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Volver a intentar"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Esta impresora no está disponible en este momento."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"No se puede mostrar la vista previa"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Preparando vista previa…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-es/strings.xml b/packages/PrintSpooler/res/values-es/strings.xml
index dba0491..4f6731d 100644
--- a/packages/PrintSpooler/res/values-es/strings.xml
+++ b/packages/PrintSpooler/res/values-es/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Servicios recomendados"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Servicios inhabilitados"</string>
<string name="all_services_title" msgid="5578662754874906455">"Todos los servicios"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other">Instalar para descubrir <xliff:g id="COUNT_1">%1$s</xliff:g> impresoras</item>
+ <item quantity="one">Instalar para descubrir <xliff:g id="COUNT_0">%1$s</xliff:g> impresora</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Imprimiendo <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelando <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Error de impresora <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Volver a empezar"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"No hay conexión con la impresora"</string>
<string name="reason_unknown" msgid="5507940196503246139">"desconocido"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – no disponible"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"¿Usar <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Es posible que el documento pase por uno o varios servidores antes de imprimirse."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"No ha funcionado. Prueba de nuevo."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Reintentar"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Esta impresora no está disponible en este momento."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"No se puede mostrar la vista previa"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Preparando vista previa…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-et-rEE/strings.xml b/packages/PrintSpooler/res/values-et-rEE/strings.xml
index 6dde083..3d0516c 100644
--- a/packages/PrintSpooler/res/values-et-rEE/strings.xml
+++ b/packages/PrintSpooler/res/values-et-rEE/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Soovitatud teenused"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Keelatud teenused"</string>
<string name="all_services_title" msgid="5578662754874906455">"Kõik teenused"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other">Installige <xliff:g id="COUNT_1">%1$s</xliff:g> printeri avastamiseks</item>
+ <item quantity="one">Installige <xliff:g id="COUNT_0">%1$s</xliff:g> printeri avastamiseks</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Prinditöö <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> printimine"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Prinditöö <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> tühistamine"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Printeri viga: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Taaskäivita"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Printeriühendus puudub"</string>
<string name="reason_unknown" msgid="5507940196503246139">"teadmata"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – pole saadaval"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Kas soovite kasutada teenust <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Printerini jõudmiseks võib dokument läbida ühe või mitu serverit."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Kahjuks see ei toiminud. Proovige uuesti."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Proovi uuesti"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"See printer ei ole praegu saadaval."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Eelvaadet ei õnnestu kuvada"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Eelvaate ettevalmistamine ..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-eu-rES/strings.xml b/packages/PrintSpooler/res/values-eu-rES/strings.xml
index 858444b..c56692f 100644
--- a/packages/PrintSpooler/res/values-eu-rES/strings.xml
+++ b/packages/PrintSpooler/res/values-eu-rES/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Gomendatutako zerbitzuak"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Desgaitutako zerbitzuak"</string>
<string name="all_services_title" msgid="5578662754874906455">"Zerbitzu guztiak"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other">Instalatu <xliff:g id="COUNT_1">%1$s</xliff:g> inprimagailu aurkitzeko</item>
+ <item quantity="one">Instalatu <xliff:g id="COUNT_0">%1$s</xliff:g> inprimagailu aurkitzeko</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> inprimatzen"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> bertan behera uzten"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Errorea <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> inprimatzean"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Berrabiarazi"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Inprimagailua ez dago konektatuta"</string>
<string name="reason_unknown" msgid="5507940196503246139">"ezezaguna"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>: ez dago erabilgarri"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> erabili nahi duzu?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Baliteke dokumentuak zerbitzari batean edo gehiagotan zehar igarotzea inprimagailurako bidean."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Horrek ez du funtzionatu. Saiatu berriro."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Saiatu berriro"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Une honetan inprimagailua ez dago erabilgarri."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Ezin da bistaratu aurrebista"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Aurrebista prestatzen…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-fa/strings.xml b/packages/PrintSpooler/res/values-fa/strings.xml
index 7c69c27..8a6c0dd 100644
--- a/packages/PrintSpooler/res/values-fa/strings.xml
+++ b/packages/PrintSpooler/res/values-fa/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"خدمات توصیهشده"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"خدمات غیرفعال"</string>
<string name="all_services_title" msgid="5578662754874906455">"همه خدمات"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="one">نصب کنید تا <xliff:g id="COUNT_1">%1$s</xliff:g> چاپگر را پیدا کنید</item>
+ <item quantity="other">نصب کنید تا <xliff:g id="COUNT_1">%1$s</xliff:g> چاپگر را پیدا کنید</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"در حال چاپ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"در حال لغو <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"خطای چاپگر <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"راهاندازی مجدد"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"اتصال با چاپگر برقرار نیست"</string>
<string name="reason_unknown" msgid="5507940196503246139">"نامعلوم"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> - در دسترس نیست"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"از <xliff:g id="SERVICE">%1$s</xliff:g> استفاده شود؟"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"ممکن است سندتان برای رسیدن به چاپگر از یک یا چند سرور عبور کند."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"متأسفیم، تلاش ناموفق بود. دوباره امتحان کنید."</string>
<string name="print_error_retry" msgid="1426421728784259538">"امتحان مجدد"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"این چاپگر اکنون در دسترس نیست."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"نمایش پیشنمایش امکانپذیر نیست"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"در حال آمادهسازی پیشنمایش…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-fi/strings.xml b/packages/PrintSpooler/res/values-fi/strings.xml
index dfd98f8..3d6897d 100644
--- a/packages/PrintSpooler/res/values-fi/strings.xml
+++ b/packages/PrintSpooler/res/values-fi/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Suositellut palvelut"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Käytöstä poistetut palvelut"</string>
<string name="all_services_title" msgid="5578662754874906455">"Kaikki palvelut"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other">Asentamalla voit löytää <xliff:g id="COUNT_1">%1$s</xliff:g> tulostinta.</item>
+ <item quantity="one">Asentamalla voit löytää <xliff:g id="COUNT_0">%1$s</xliff:g> tulostimen.</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Tulostetaan <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Peruutetaan työ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Tulostinvirhe työlle <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Käynnistä uudelleen"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Ei yhteyttä tulostimeen"</string>
<string name="reason_unknown" msgid="5507940196503246139">"tuntematon"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – ei käytettävissä"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Käytetäänkö palvelua <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Asiakirja saattaa kulkea yhden tai useamman palvelimen kautta matkalla tulostimeen."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Ei valitettavasti onnistunut. Yritä uudelleen."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Yritä uudelleen"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Tämä tulostin ei ole käyttävissä juuri nyt."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Esikatselua ei voi näyttää."</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Esikatselua valmistellaan…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-fr-rCA/strings.xml b/packages/PrintSpooler/res/values-fr-rCA/strings.xml
index a95d565..6c9539a 100644
--- a/packages/PrintSpooler/res/values-fr-rCA/strings.xml
+++ b/packages/PrintSpooler/res/values-fr-rCA/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Services recommandés"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Services désactivés"</string>
<string name="all_services_title" msgid="5578662754874906455">"Tous les services"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="one">Installer pour détecter <xliff:g id="COUNT_1">%1$s</xliff:g> imprimante</item>
+ <item quantity="other">Installer pour détecter <xliff:g id="COUNT_1">%1$s</xliff:g> imprimantes</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Impression de <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> en cours…"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Annulation de « <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> »…"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Erreur impression : « <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> »"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Recommencer"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Aucune connexion à l\'imprimante"</string>
<string name="reason_unknown" msgid="5507940196503246139">"inconnu"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> — indisponible"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Utiliser <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Votre document peut passer par un ou plusieurs serveurs avant d\'arriver à l\'imprimante."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Échec de l\'action. Réessayez."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Réessayer"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Cette imprimante n\'est pas accessible pour le moment."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Impossible d\'afficher l\'aperçu"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Préparation de l\'aperçu en cours…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-fr/strings.xml b/packages/PrintSpooler/res/values-fr/strings.xml
index dd1f490..64add68 100644
--- a/packages/PrintSpooler/res/values-fr/strings.xml
+++ b/packages/PrintSpooler/res/values-fr/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Services recommandés"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Services désactivés"</string>
<string name="all_services_title" msgid="5578662754874906455">"Tous les services"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="one">Installer pour détecter <xliff:g id="COUNT_1">%1$s</xliff:g> imprimante</item>
+ <item quantity="other">Installer pour détecter <xliff:g id="COUNT_1">%1$s</xliff:g> imprimantes</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Impression de \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\" en cours…"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Annulation de \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\" en cours…"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Erreur impression pour \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\""</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Redémarrer"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Aucune connexion à l\'imprimante."</string>
<string name="reason_unknown" msgid="5507940196503246139">"inconnue"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – indisponible"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Utiliser <xliff:g id="SERVICE">%1$s</xliff:g> ?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Votre document peut passer par un ou plusieurs serveurs avant d\'être envoyé sur l\'imprimante."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Échec de l\'opération. Veuillez réessayer."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Réessayer"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Cette imprimante n\'est pas disponible actuellement."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Impossible d\'afficher l\'aperçu"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Préparation de l\'aperçu en cours…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-gl-rES/strings.xml b/packages/PrintSpooler/res/values-gl-rES/strings.xml
index 81e080e..099159d 100644
--- a/packages/PrintSpooler/res/values-gl-rES/strings.xml
+++ b/packages/PrintSpooler/res/values-gl-rES/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Servizos recomendados"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Servizos desactivados"</string>
<string name="all_services_title" msgid="5578662754874906455">"Todos os servizos"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other">Instala o servizo para atopar <xliff:g id="COUNT_1">%1$s</xliff:g> impresoras</item>
+ <item quantity="one">Instala o servizo para atopar <xliff:g id="COUNT_0">%1$s</xliff:g> impresora</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Imprimindo <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelando <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Erro da impresora <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Reiniciar"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Non hai conexión coa impresora"</string>
<string name="reason_unknown" msgid="5507940196503246139">"descoñecido"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>: non dispoñible"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Queres usar <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"É posible que o teu documento pase por un ou máis servidores antes de imprimirse."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Non funcionou. Téntao de novo."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Tentar de novo"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Esta impresora non está dispoñible nestes momentos."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Non se pode mostrar a vista previa"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Preparando a vista previa…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-gu-rIN/strings.xml b/packages/PrintSpooler/res/values-gu-rIN/strings.xml
index 44ede86..b42fdab 100644
--- a/packages/PrintSpooler/res/values-gu-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-gu-rIN/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"ભલામણ કરેલી સેવાઓ"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"અક્ષમ કરેલી સેવાઓ"</string>
<string name="all_services_title" msgid="5578662754874906455">"બધી સેવાઓ"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$s</xliff:g> પ્રિન્ટરની શોધ કરવા માટે ઇન્સ્ટૉલ કરો</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> પ્રિન્ટરની શોધ કરવા માટે ઇન્સ્ટૉલ કરો</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> છાપી રહ્યાં છે"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ને રદ કરી રહ્યું છે"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"પ્રિન્ટર ભૂલ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"પુનઃપ્રારંભ કરો"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"પ્રિન્ટર માટે કોઈ કનેક્શન નથી"</string>
<string name="reason_unknown" msgid="5507940196503246139">"અજાણ્યું"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – અનુપલબ્ધ"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> નો ઉપયોગ કરીએ?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"તમારો દસ્તાવેજ પ્રિન્ટર સુધીના તેના માર્ગમાં એક અથવા વધુ સર્વર્સથી પસાર થઈ શકે છે."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"માફ કરશો, તે કામ કરતું નહોતું. ફરીથી પ્રયાસ કરો."</string>
<string name="print_error_retry" msgid="1426421728784259538">"ફરી પ્રયાસ કરો"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"આ પ્રિન્ટર અત્યારે ઉપલબ્ધ નથી."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"પૂર્વાવલોકન પ્રદર્શિત કરી શકતાં નથી"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"પૂર્વાવલોકનની તૈયારી કરી રહ્યું છે..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-hi/strings.xml b/packages/PrintSpooler/res/values-hi/strings.xml
index f75630e..507754a0 100644
--- a/packages/PrintSpooler/res/values-hi/strings.xml
+++ b/packages/PrintSpooler/res/values-hi/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"सुझाई गई सेवाएं"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"अक्षम सेवाएं"</string>
<string name="all_services_title" msgid="5578662754874906455">"सभी सेवाएं"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$s</xliff:g> प्रिंटर खोजने के लिए इंस्टॉल करें</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> प्रिंटर खोजने के लिए इंस्टॉल करें</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> प्रिंट हो रहा है"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> रद्द हो रहा है"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"प्रिंटर त्रुटि <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"पुन: आरंभ करें"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"प्रिंटर के लिए कोई कनेक्शन नहीं"</string>
<string name="reason_unknown" msgid="5507940196503246139">"अज्ञात"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – अनुपलब्ध"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> का उपयोग करें?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"प्रिंटर पर जाते समय आपका दस्तावेज़ एक या अधिक सर्वर से गुज़र सकता है."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"क्षमा करें, उससे बात नहीं बनी. पुन: प्रयास करें."</string>
<string name="print_error_retry" msgid="1426421728784259538">"फिर से प्रयास करें"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"यह प्रिंटर इस समय उपलब्ध नहीं है."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"पूर्वावलोकन प्रदर्शित नहीं किया जा सकता"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"पूर्वावलोकन तैयार हो रहा है..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-hr/strings.xml b/packages/PrintSpooler/res/values-hr/strings.xml
index bd29d02..92c97ea 100644
--- a/packages/PrintSpooler/res/values-hr/strings.xml
+++ b/packages/PrintSpooler/res/values-hr/strings.xml
@@ -73,6 +73,11 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Preporučene usluge"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Onemogućene usluge"</string>
<string name="all_services_title" msgid="5578662754874906455">"Sve usluge"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="one">Instalirajte da biste pronašli <xliff:g id="COUNT_1">%1$s</xliff:g> pisač</item>
+ <item quantity="few">Instalirajte da biste pronašli <xliff:g id="COUNT_1">%1$s</xliff:g> pisača</item>
+ <item quantity="other">Instalirajte da biste pronašli <xliff:g id="COUNT_1">%1$s</xliff:g> pisača</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Ispisivanje <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Otkazivanje zadatka <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Pogreška pisača <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -81,7 +86,6 @@
<string name="restart" msgid="2472034227037808749">"Ponovo pokreni"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Nema veze s pisačem"</string>
<string name="reason_unknown" msgid="5507940196503246139">"nepoznato"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – zadatak nije dostupan"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Želite li upotrijebiti uslugu <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Na putu do pisača vaš dokument može proći kroz jedan ili više poslužitelja."</string>
<string-array name="color_mode_labels">
@@ -101,5 +105,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Nažalost, to nije uspjelo. Pokušajte ponovo."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Pokušajte ponovno"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Pisač trenutačno nije dostupan."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Pregled nije dostupan"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Priprema pregleda…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-hu/strings.xml b/packages/PrintSpooler/res/values-hu/strings.xml
index 356cb76..a2e53db 100644
--- a/packages/PrintSpooler/res/values-hu/strings.xml
+++ b/packages/PrintSpooler/res/values-hu/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Javasolt szolgáltatások"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Letiltott szolgáltatások"</string>
<string name="all_services_title" msgid="5578662754874906455">"Minden szolgáltatás"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other">Telepítés <xliff:g id="COUNT_1">%1$s</xliff:g> nyomtató felfedezéséhez</item>
+ <item quantity="one">Telepítés <xliff:g id="COUNT_0">%1$s</xliff:g> nyomtató felfedezéséhez</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"A(z) <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> nyomtatása"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"A(z) <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> törlése"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Nyomtatási hiba: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Újraindítás"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Nincs kapcsolat a nyomtatóval"</string>
<string name="reason_unknown" msgid="5507940196503246139">"ismeretlen"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – nem érhető el"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Használni szeretné a következő szolgáltatást: <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"A dokumentum áthaladhat egy vagy több szerveren, mielőtt a nyomtatóhoz érne."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Sajnáljuk, de nem sikerült. Próbálja újra."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Újra"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Ez a nyomtató jelenleg nem érhető el."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Nem lehet megjeleníteni az előnézetet"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Előnézet előkészítése…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-hy-rAM/strings.xml b/packages/PrintSpooler/res/values-hy-rAM/strings.xml
index 2d10166..e26c244 100644
--- a/packages/PrintSpooler/res/values-hy-rAM/strings.xml
+++ b/packages/PrintSpooler/res/values-hy-rAM/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Խորհուրդ տրվող ծառայությունները"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Կասեցված ծառայությունները"</string>
<string name="all_services_title" msgid="5578662754874906455">"Բոլոր ծառայությունները"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="one">Տեղադրեք՝ <xliff:g id="COUNT_1">%1$s</xliff:g> տպիչ հայտնաբերելու համար</item>
+ <item quantity="other">Տեղադրեք՝ <xliff:g id="COUNT_1">%1$s</xliff:g> տպիչ հայտնաբերելու համար</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Տպվում է՝ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>-ը չեղարկվում է"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Տպիչի սխալ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Վերագործարկել"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Տպիչի հետ կապ չկա"</string>
<string name="reason_unknown" msgid="5507940196503246139">"անհայտ"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> տպիչն անհասանելի է"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Օգտագործե՞լ <xliff:g id="SERVICE">%1$s</xliff:g>-ը:"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Հնարավոր է՝ փաստաթուղթը մի քանի սերվերներով անցնի մինչ տպվելը:"</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Չհաջողվեց: Նորից փորձեք:"</string>
<string name="print_error_retry" msgid="1426421728784259538">"Կրկնել"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Տպիչն այս պահին հասանելի չէ:"</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Նախադիտումը հնարավոր չէ"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Նախադիտումը պատրաստվում է…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-in/strings.xml b/packages/PrintSpooler/res/values-in/strings.xml
index 8e20d27..4ec0644 100644
--- a/packages/PrintSpooler/res/values-in/strings.xml
+++ b/packages/PrintSpooler/res/values-in/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Layanan yang disarankan"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Layanan dinonaktifkan"</string>
<string name="all_services_title" msgid="5578662754874906455">"Semua layanan"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other">Pasang untuk menemukan <xliff:g id="COUNT_1">%1$s</xliff:g> printer</item>
+ <item quantity="one">Pasang untuk menemukan <xliff:g id="COUNT_0">%1$s</xliff:g> printer</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Mencetak <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Membatalkan <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Ada kesalahan printer <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Mulai Ulang"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Tidak ada sambungan ke printer"</string>
<string name="reason_unknown" msgid="5507940196503246139">"tak diketahui"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – tidak tersedia"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Gunakan <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Dokumen Anda dapat melewati satu atau beberapa server saat menuju printer."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Maaf, tidak berhasil. Coba lagi."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Coba lagi"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Saat ini printer ini tidak tersedia."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Tidak dapat menampilkan pratinjau"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Menyiapkan pratinjau..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-is-rIS/strings.xml b/packages/PrintSpooler/res/values-is-rIS/strings.xml
index 73660fb..e05f07f 100644
--- a/packages/PrintSpooler/res/values-is-rIS/strings.xml
+++ b/packages/PrintSpooler/res/values-is-rIS/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Þjónusta sem mælt er með"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Þjónusta við fatlaða"</string>
<string name="all_services_title" msgid="5578662754874906455">"Öll þjónusta"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="one">Settu upp til að finna <xliff:g id="COUNT_1">%1$s</xliff:g> prentara</item>
+ <item quantity="other">Settu upp til að finna <xliff:g id="COUNT_1">%1$s</xliff:g> prentara</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Prentar <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Hættir við <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Prentaravilla <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Endurræsa"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Engin tenging við prentara"</string>
<string name="reason_unknown" msgid="5507940196503246139">"óþekkt"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – ekki í boði"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Nota <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Skjalið gæti þurft að fara í gegnum einn eða fleiri þjóna á leið sinni til prentarans."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Þetta virkaði því miður ekki. Reyndu aftur."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Reyna aftur"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Þessi prentari er ekki í boði núna."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Ekki hægt að birta forskoðun"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Undirbýr forskoðun…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-it/strings.xml b/packages/PrintSpooler/res/values-it/strings.xml
index 46a570d..39a0a61 100644
--- a/packages/PrintSpooler/res/values-it/strings.xml
+++ b/packages/PrintSpooler/res/values-it/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Servizi consigliati"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Servizi disattivati"</string>
<string name="all_services_title" msgid="5578662754874906455">"Tutti i servizi"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other">Installa per rilevare <xliff:g id="COUNT_1">%1$s</xliff:g> stampanti</item>
+ <item quantity="one">Installa per rilevare <xliff:g id="COUNT_0">%1$s</xliff:g> stampante</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Stampa di <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Annullamento di <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Errore della stampante: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Riavvia"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Nessun collegamento alla stampante"</string>
<string name="reason_unknown" msgid="5507940196503246139">"sconosciuto"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> - non disponibile"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Utilizzare <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Il tuo documento potrebbe passare da uno o più server per raggiungere la stampante."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Non ha funzionato. Riprova."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Riprova"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Al momento la stampante non è disponibile."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Impossibile visualizzare l\'anteprima"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Preparazione anteprima…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-iw/strings.xml b/packages/PrintSpooler/res/values-iw/strings.xml
index c26c3d1..9aa4104 100644
--- a/packages/PrintSpooler/res/values-iw/strings.xml
+++ b/packages/PrintSpooler/res/values-iw/strings.xml
@@ -74,6 +74,12 @@
<string name="recommended_services_title" msgid="3799434882937956924">"שירותים מומלצים"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"שירותים מושבתים"</string>
<string name="all_services_title" msgid="5578662754874906455">"כל השירותים"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="two">התקן כדי לגלות <xliff:g id="COUNT_1">%1$s</xliff:g> מדפסות</item>
+ <item quantity="many">התקן כדי לגלות <xliff:g id="COUNT_1">%1$s</xliff:g> מדפסות</item>
+ <item quantity="other">התקן כדי לגלות <xliff:g id="COUNT_1">%1$s</xliff:g> מדפסות</item>
+ <item quantity="one">התקן כדי לגלות מדפסת <xliff:g id="COUNT_0">%1$s</xliff:g></item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"מדפיס את <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"מבטל את <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"שגיאת מדפסת ב-<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -82,7 +88,6 @@
<string name="restart" msgid="2472034227037808749">"הפעל מחדש"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"אין חיבור למדפסת"</string>
<string name="reason_unknown" msgid="5507940196503246139">"לא ידוע"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – לא זמינה"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"האם להשתמש ב-<xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"ייתכן שהמסמך שלך יעבור בשרת אחד או יותר בדרכו למדפסת."</string>
<string-array name="color_mode_labels">
@@ -102,5 +107,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"מצטערים, אך זה לא עבד. נסה שוב."</string>
<string name="print_error_retry" msgid="1426421728784259538">"נסה שוב"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"המדפסת הזו אינה זמינה כעת."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"לא ניתן להציג תצוגה מקדימה"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"מכין תצוגה מקדימה…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-ja/strings.xml b/packages/PrintSpooler/res/values-ja/strings.xml
index a6e243f..f97efc1 100644
--- a/packages/PrintSpooler/res/values-ja/strings.xml
+++ b/packages/PrintSpooler/res/values-ja/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"推奨されているサービス"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"無効になっているサービス"</string>
<string name="all_services_title" msgid="5578662754874906455">"すべてのサービス"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> 台のプリンタを検索するにはインストールします</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$s</xliff:g> 台のプリンタを検索するにはインストールします</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>を印刷しています"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>をキャンセルしています"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"プリンタエラー: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"再試行"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"プリンタに接続されていません"</string>
<string name="reason_unknown" msgid="5507940196503246139">"不明"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>–使用不可"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g>を利用しますか?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"ドキュメントは1つ以上のサーバーを経由してプリンタに送信されることがあります。"</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"エラーです。もう一度お試しください。"</string>
<string name="print_error_retry" msgid="1426421728784259538">"再試行"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"現在このプリンターは使用できません。"</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"プレビューを表示できません"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"プレビューを準備しています…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-ka-rGE/strings.xml b/packages/PrintSpooler/res/values-ka-rGE/strings.xml
index 2608ed4..9cb8b39 100644
--- a/packages/PrintSpooler/res/values-ka-rGE/strings.xml
+++ b/packages/PrintSpooler/res/values-ka-rGE/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"რეკომენდებული სერვისები"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"გათიშული სერვისები"</string>
<string name="all_services_title" msgid="5578662754874906455">"ყველა სერვისი"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other">დააინსტალირეთ <xliff:g id="COUNT_1">%1$s</xliff:g> პრინტერის აღმოსაჩენად</item>
+ <item quantity="one">დააინსტალირეთ <xliff:g id="COUNT_0">%1$s</xliff:g> პრინტერის აღმოსაჩენად</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"იბეჭდება <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"მიმდინარეობს <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>-ის გაუქმება"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"ბეჭდვის შეცდომა <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"გადატვირთვა"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"პრინტერთან კავშირი არ არის"</string>
<string name="reason_unknown" msgid="5507940196503246139">"უცნობი"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – მიუწვდომელია"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"გსურთ, გამოიყენოთ <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"პრინტერამდე გზად დოკუმენტმა შეიძლება ერთი ან მეტი სერვერი გაიაროს."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"უკაცრავად, ვერ მოხერხდა. სცადეთ ისევ."</string>
<string name="print_error_retry" msgid="1426421728784259538">"გამეორება"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"პრინტერი ამჟამად მიუწვდომელია."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"გადახედვის ჩვენება ვერ ხერხდება"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"მზადდება გადახედვა…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-kk-rKZ/strings.xml b/packages/PrintSpooler/res/values-kk-rKZ/strings.xml
index def0c3c..37b2cd3 100644
--- a/packages/PrintSpooler/res/values-kk-rKZ/strings.xml
+++ b/packages/PrintSpooler/res/values-kk-rKZ/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Ұсынылған қызметтер"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Өшірілген қызметтер"</string>
<string name="all_services_title" msgid="5578662754874906455">"Барлық қызметтер"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> принтерді табу үшін орнатыңыз</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$s</xliff:g> принтерді табу үшін орнатыңыз</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> басып шығарылуда"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> жұмысын тоқтатуда"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> принтер қателігі"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Қайта бастау"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Принтермен байланыс жоқ"</string>
<string name="reason_unknown" msgid="5507940196503246139">"белгісіз"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – қол жетімсіз"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> қолданылсын ба?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Құжат принтерге жеткенше бір немесе бірнеше серверден өтуі мүмкін."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Кешіріңіз, бұл нәтиже бермеді. Әрекетті қайталаңыз."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Қайталау"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Бұл принтер дәл қазір қол жетімді емес."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Бетті алдын ала қарау мүмкін емес"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Алдын ала қарау дайындалуда…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-km-rKH/strings.xml b/packages/PrintSpooler/res/values-km-rKH/strings.xml
index 24048cf..12d296d 100644
--- a/packages/PrintSpooler/res/values-km-rKH/strings.xml
+++ b/packages/PrintSpooler/res/values-km-rKH/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"សេវាកម្មដែលបានណែនាំ"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"សេវាកម្មដែលបិទដំណើរការ"</string>
<string name="all_services_title" msgid="5578662754874906455">"សេវាកម្មទាំងអស់"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other">ដំឡើងដើម្បីរកមើលម៉ាស៊ីនបោះពុម្ព <xliff:g id="COUNT_1">%1$s</xliff:g></item>
+ <item quantity="one">ដំឡើងដើម្បីរកមើលម៉ាស៊ីនបោះពុម្ព <xliff:g id="COUNT_0">%1$s</xliff:g></item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"កំពុងបោះពុម្ព <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"ការបោះបង់ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"កំហុសម៉ាស៊ីនបោះពុម្ព <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"ចាប់ផ្ដើមឡើងវិញ"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"គ្មានការភ្ជាប់ទៅម៉ាស៊ីនបោះពុម្ព"</string>
<string name="reason_unknown" msgid="5507940196503246139">"មិនស្គាល់"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – មិនអាចប្រើបាន"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"ប្រើ <xliff:g id="SERVICE">%1$s</xliff:g> ឬ?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"ឯកសាររបស់អ្នកអាចនឹងឆ្លងកាត់ម៉ាស៊ីនមេមួយ ឬច្រើននៅពេលដែលវាធ្វើដំណើរទៅកាន់ម៉ាស៊ីនបោះពុម្ព។"</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"សូមទោស វាមិនដំណើរការទេ។ ព្យាយាមម្ដងទៀត។"</string>
<string name="print_error_retry" msgid="1426421728784259538">"ព្យាយាមម្ដងទៀត"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"ឥឡូវនេះ ម៉ាស៊ីនបោះពុម្ពនេះមិនអាចប្រើបាន។"</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"មិនអាចបង្ហាញការមើលជាមុនបានទេ"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"កំពុងរៀបចំមើលជាមុន…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-kn-rIN/strings.xml b/packages/PrintSpooler/res/values-kn-rIN/strings.xml
index af20965..8b1acdd 100644
--- a/packages/PrintSpooler/res/values-kn-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-kn-rIN/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"ಶಿಫಾರಸು ಮಾಡಲಾದ ಸೇವೆಗಳು"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾದ ಸೇವೆಗಳು"</string>
<string name="all_services_title" msgid="5578662754874906455">"ಎಲ್ಲ ಸೇವೆಗಳು"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$s</xliff:g> ಪ್ರಿಂಟರ್ಗಳನ್ನು ಶೋಧಿಸಲು ಸ್ಥಾಪಿಸಿ</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> ಪ್ರಿಂಟರ್ಗಳನ್ನು ಶೋಧಿಸಲು ಸ್ಥಾಪಿಸಿ</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ಮುದ್ರಿಸಲಾಗುತ್ತಿದೆ"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ರದ್ದು ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"ಮುದ್ರಕ ದೋಷ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"ಮರುಪ್ರಾರಂಭಿಸು"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"ಮುದ್ರಕಕ್ಕೆ ಸಂಪರ್ಕವಿಲ್ಲ"</string>
<string name="reason_unknown" msgid="5507940196503246139">"ಅಜ್ಞಾತ"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – ಲಭ್ಯವಿಲ್ಲ"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> ಬಳಸುವುದೇ?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"ನಿಮ್ಮ ಡಾಕ್ಯುಮೆಂಟ್ ಪ್ರಿಂಟರ್ಗೆ ಹೋಗುವ ಸಂದರ್ಭದಲ್ಲಿ ಒಂದು ಅಥವಾ ಅದಕ್ಕಿಂತ ಹೆಚ್ಚು ಸರ್ವರ್ಗಳ ಮೂಲಕ ಹಾದು ಹೋಗಬಹುದು."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"ಕ್ಷಮಿಸಿ, ಅದು ಕೆಲಸ ಮಾಡುತ್ತಿಲ್ಲ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="print_error_retry" msgid="1426421728784259538">"ಮರುಪ್ರಯತ್ನಿಸು"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"ಈ ಪ್ರಿಂಟರ್ ಸದ್ಯಕ್ಕೆ ಲಭ್ಯವಿಲ್ಲ."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"ಪೂರ್ವವೀಕ್ಷಣೆ ಪ್ರದರ್ಶಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"ಪೂರ್ವವೀಕ್ಷಣೆ ತಯಾರಾಗುತ್ತಿದೆ…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-ko/strings.xml b/packages/PrintSpooler/res/values-ko/strings.xml
index 0b297a2..e6ca240 100644
--- a/packages/PrintSpooler/res/values-ko/strings.xml
+++ b/packages/PrintSpooler/res/values-ko/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"권장 서비스"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"사용 중지된 서비스"</string>
<string name="all_services_title" msgid="5578662754874906455">"모든 서비스"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g>개 프린터를 표시하려면 설치하세요.</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$s</xliff:g>개 프린터를 표시하려면 설치하세요.</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> 인쇄 중"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> 취소 중"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"프린터 오류: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"다시 시작"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"프린터와 연결되지 않음"</string>
<string name="reason_unknown" msgid="5507940196503246139">"알 수 없음"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – 사용할 수 없음"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g>을(를) 사용할까요?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"문서가 프린터로 전송되는 중에 하나 이상의 서버를 통과할 수 있습니다."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"죄송합니다. 오류가 발생했습니다. 다시 시도해 보세요."</string>
<string name="print_error_retry" msgid="1426421728784259538">"다시 시도"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"현재 이 프린터를 사용할 수 없습니다."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"미리보기를 표시할 수 없음"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"미리보기 준비 중…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-ky-rKG/strings.xml b/packages/PrintSpooler/res/values-ky-rKG/strings.xml
index 85b2526..ae0b05e 100644
--- a/packages/PrintSpooler/res/values-ky-rKG/strings.xml
+++ b/packages/PrintSpooler/res/values-ky-rKG/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Сунушталган кызматтар"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Өчүрүлгөн кызматтар"</string>
<string name="all_services_title" msgid="5578662754874906455">"Бардык кызматтар"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other">Орнотсоңуз <xliff:g id="COUNT_1">%1$s</xliff:g> принтер таап аласыз</item>
+ <item quantity="one">Орнотсоңуз <xliff:g id="COUNT_0">%1$s</xliff:g> принтер таап аласыз</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> басылууда"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> токтотулууда"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Принтерде ката кетти: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Кайра баштоо"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Принтер менен байланыш жок"</string>
<string name="reason_unknown" msgid="5507940196503246139">"белгисиз"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – жеткиликтүү эмес"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> колдонулсунбу?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Принтерге жеткиче документиңиз бир же андан көп серверлерден өтүшү мүмкүн."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Кечиресиз, иштеген жок. Дагы бир жолу аракет кылып көрүңүз."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Дагы бир жолу аракет кылуу"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Учурда бул принтерди колдонуу мүмкүн эмес."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Алдын ала көрүнүшү көрсөтүлбөй жатат"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Алдын-ала көрүүгө даярданууда…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-lo-rLA/strings.xml b/packages/PrintSpooler/res/values-lo-rLA/strings.xml
index 81ace83..2392e4a 100644
--- a/packages/PrintSpooler/res/values-lo-rLA/strings.xml
+++ b/packages/PrintSpooler/res/values-lo-rLA/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"ບໍລິການທີ່ແນະນຳ"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"ບໍລິການທີ່ຖືກປິດການນຳໃຊ້"</string>
<string name="all_services_title" msgid="5578662754874906455">"ບໍລິການທັງໝົດ"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other">ຕິດຕັ້ງເພື່ອຄົ້ນພົບເຄື່ອງພິມ <xliff:g id="COUNT_1">%1$s</xliff:g> ເຄື່ອງ</item>
+ <item quantity="one">ຕິດຕັ້ງເພື່ອຄົ້ນພົບເຄື່ອງພິມ <xliff:g id="COUNT_0">%1$s</xliff:g> ເຄື່ອງ</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"ກຳລັງພິມ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"ກຳລັງຍົກເລີກ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"ເຄື່ອງພິມເກີດຂໍ້ຜິດພາດ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"ປິດເປີດໃໝ່"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"ບໍ່ມີການເຊື່ອມຕໍ່ຫາເຄື່ອງພິມ"</string>
<string name="reason_unknown" msgid="5507940196503246139">"ບໍ່ຮູ້ຈັກ"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> - ບໍ່ມີຢູ່"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"ໃຊ້ <xliff:g id="SERVICE">%1$s</xliff:g> ບໍ?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"ເອກະສານຂອງທ່ານອາດເດີນທາງຜ່ານໜຶ່ງ ຫຼື ຫຼາຍເຊີບເວີ ເພື່ອໄປຮອດເຄື່ອງພິມ."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"ຂໍອະໄພ, ໃຊ້ບໍ່ໄດ້. ໃຫ້ລອງໃໝ່ອີກເທື່ອນຶ່ງ."</string>
<string name="print_error_retry" msgid="1426421728784259538">"ລອງໃໝ່"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"ບໍ່ສາມາດໃຊ້ເຄື່ອງພິມນີ້ໃນເວລານີ້ໄດ້."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"ບໍ່ສາມາດສະແດງຕົວຢ່າງໄດ້"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"ກຳລັງກະກຽມຕົວຢ່າງ…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-lt/strings.xml b/packages/PrintSpooler/res/values-lt/strings.xml
index 40bc7f1..65ccc2b 100644
--- a/packages/PrintSpooler/res/values-lt/strings.xml
+++ b/packages/PrintSpooler/res/values-lt/strings.xml
@@ -74,6 +74,12 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Rekomenduojamos paslaugos"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Išjungtos paslaugos"</string>
<string name="all_services_title" msgid="5578662754874906455">"Visos paslaugos"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="one">Įdiekite, kad būtų rastas <xliff:g id="COUNT_1">%1$s</xliff:g> spausdintuvas</item>
+ <item quantity="few">Įdiekite, kad būtų rasti <xliff:g id="COUNT_1">%1$s</xliff:g> spausdintuvai</item>
+ <item quantity="many">Įdiekite, kad būtų rasta <xliff:g id="COUNT_1">%1$s</xliff:g> spausdintuvo</item>
+ <item quantity="other">Įdiekite, kad būtų rasta <xliff:g id="COUNT_1">%1$s</xliff:g> spausdintuvų</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Spausdinama: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Atšaukiama: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Spausdintuvo klaida: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -82,7 +88,6 @@
<string name="restart" msgid="2472034227037808749">"Paleisti iš naujo"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Nėra ryšio su spausdintuvu"</string>
<string name="reason_unknown" msgid="5507940196503246139">"nežinoma"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"„<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>“ – nepasiekiama"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Naudoti „<xliff:g id="SERVICE">%1$s</xliff:g>“?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Kai dokumentas siunčiamas į spausdintuvą, jis gali būti perduodamas per vieną ar daugiau serverių."</string>
<string-array name="color_mode_labels">
@@ -102,5 +107,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Deja, tai neveikia. Bandykite dar kartą."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Bandykite dar kartą"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Šis spausdintuvas šiuo metu nepasiekiamas."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Nepavyksta pateikti peržiūros"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Ruošiama peržiūra…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-lv/strings.xml b/packages/PrintSpooler/res/values-lv/strings.xml
index 11e689b..1bcfe78 100644
--- a/packages/PrintSpooler/res/values-lv/strings.xml
+++ b/packages/PrintSpooler/res/values-lv/strings.xml
@@ -73,6 +73,11 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Ieteiktie pakalpojumi"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Atspējotie pakalpojumi"</string>
<string name="all_services_title" msgid="5578662754874906455">"Visi pakalpojumi"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="zero">Instalējiet, lai atklātu <xliff:g id="COUNT_1">%1$s</xliff:g> printerus</item>
+ <item quantity="one">Instalējiet, lai atklātu <xliff:g id="COUNT_1">%1$s</xliff:g> printeri</item>
+ <item quantity="other">Instalējiet, lai atklātu <xliff:g id="COUNT_1">%1$s</xliff:g> printerus</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Notiek darba <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> drukāšana…"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Pārtrauc drukas darbu <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>…"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Printera kļūda ar darbu <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -81,7 +86,6 @@
<string name="restart" msgid="2472034227037808749">"Restartēt"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Nav savienojuma ar printeri"</string>
<string name="reason_unknown" msgid="5507940196503246139">"nezināms"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> — nav pieejams"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Vai izmantot pakalpojumu <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Dokuments, iespējams, tiek pārsūtīts caur vienu vai vairākiem serveriem, līdz tas nonāk līdz printerim."</string>
<string-array name="color_mode_labels">
@@ -101,5 +105,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Diemžēl tas neizdevās. Mēģiniet vēlreiz."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Mēģināt vēlreiz"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Šis printeris šobrīd nav pieejams."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Nevar attēlot priekšskatījumu"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Notiek priekšskatījuma sagatavošana..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-mk-rMK/strings.xml b/packages/PrintSpooler/res/values-mk-rMK/strings.xml
index bc2b498..d29566bc 100644
--- a/packages/PrintSpooler/res/values-mk-rMK/strings.xml
+++ b/packages/PrintSpooler/res/values-mk-rMK/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Препорачани услуги"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Оневозможени услуги"</string>
<string name="all_services_title" msgid="5578662754874906455">"Сите услуги"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="one">Инсталирајте за да пронајдете <xliff:g id="COUNT_1">%1$s</xliff:g> печатач</item>
+ <item quantity="other">Инсталирајте за да пронајдете <xliff:g id="COUNT_1">%1$s</xliff:g> печатачи</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> се печати"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> се откажува"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Грешка при печатење <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Рестартирај"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Нема поврзување со печатач"</string>
<string name="reason_unknown" msgid="5507940196503246139">"непознато"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> - недостапен"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Користи <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"На пат до печатачот, документот може да помине преку еден или повеќе сервери."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"За жал, тоа не успеа. Обидете се повторно."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Обиди се повторно"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Овој печатач не е достапен во моментов."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Прегледот не може да се прикаже"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Се подготвува преглед…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-ml-rIN/strings.xml b/packages/PrintSpooler/res/values-ml-rIN/strings.xml
index ade7fb39..16d654c 100644
--- a/packages/PrintSpooler/res/values-ml-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-ml-rIN/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"ശുപാർശ ചെയ്യപ്പെടുന്ന സേവനങ്ങൾ"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"പ്രവർത്തനരഹിതമാക്കിയ സേവനങ്ങൾ"</string>
<string name="all_services_title" msgid="5578662754874906455">"എല്ലാ സേവനങ്ങളും"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> പ്രിന്ററുകൾ കണ്ടെത്തുന്നതിന് ഇൻസ്റ്റാൾ ചെയ്യുക</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$s</xliff:g> പ്രിന്റർ കണ്ടെത്തുന്നതിന് ഇൻസ്റ്റാൾ ചെയ്യുക</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> പ്രിന്റുചെയ്യുന്നു"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> റദ്ദാക്കുന്നു"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"പ്രിന്റർ പിശക് <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"പുനരാരംഭിക്കുക"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"പ്രിന്ററിൽ കണക്ഷനൊന്നുമില്ല"</string>
<string name="reason_unknown" msgid="5507940196503246139">"അജ്ഞാതം"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – ലഭ്യമല്ല"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> ഉപയോഗിക്കണോ?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"നിങ്ങളുടെ പ്രമാണം പ്രിന്ററിലേക്ക് പോകുന്നതിനിടെ അത് ഒന്നോ അതിലധികമോ സെർവറുകളിലൂടെ കടന്നുപോകാനിടയുണ്ട്."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"ക്ഷമിക്കണം, അത് പ്രവർത്തിച്ചില്ല. വീണ്ടും ശ്രമിക്കുക."</string>
<string name="print_error_retry" msgid="1426421728784259538">"വീണ്ടും ശ്രമിക്കുക"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"ഈ പ്രിന്ററർ ഇപ്പോൾ ലഭ്യമല്ല."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"പ്രിവ്യൂ കാണിക്കാൻ കഴിയില്ല"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"പ്രിവ്യൂ തയ്യാറാക്കുന്നു…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-mn-rMN/strings.xml b/packages/PrintSpooler/res/values-mn-rMN/strings.xml
index 133d88c..ded0665 100644
--- a/packages/PrintSpooler/res/values-mn-rMN/strings.xml
+++ b/packages/PrintSpooler/res/values-mn-rMN/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Санал болгосон үйлчилгээ"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Идэвхгүй болгосон үйлчилгээ"</string>
<string name="all_services_title" msgid="5578662754874906455">"Бүх үйлчилгээ"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other"> <xliff:g id="COUNT_1">%1$s</xliff:g> хэвлэгч олохын тулд суулгах</item>
+ <item quantity="one"> <xliff:g id="COUNT_0">%1$s</xliff:g> хэвлэгч олохын тулд суулгах</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Хэвлэж байна <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Цуцлаж байна <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Принтерийн алдаа <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Дахин эхлүүлэх"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Принтер холбогдоогүй байна"</string>
<string name="reason_unknown" msgid="5507940196503246139">"тодорхойгүй"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – ашиглах боломжгүй"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g>-г ашиглах уу?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Таны документ хэвлэгчид иртэл нэг эсвэл хэд хэдэн серверээр дамжина."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Уучлаарай, ажилласангүй. Дахин оролдоно уу."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Дахин оролдох"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Одоо хэвлэгч ашиглах боломжгүй."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Урьдчилан үзүүлэх боломжгүй"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Урьдчилан харахыг бэлтгэж байна…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-mr-rIN/strings.xml b/packages/PrintSpooler/res/values-mr-rIN/strings.xml
index 2b3b29f..5436635 100644
--- a/packages/PrintSpooler/res/values-mr-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-mr-rIN/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"शिफारस केलेल्या सेवा"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"अक्षम केलल्या सेवा"</string>
<string name="all_services_title" msgid="5578662754874906455">"सर्व सेवा"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$s</xliff:g> प्रिंटर शोधण्यासाठी स्थापित करा</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> प्रिंटर शोधण्यासाठी स्थापित करा</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> मुद्रण करीत आहे"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> रद्द करीत आहे"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"प्रिंटर त्रुटी <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"रीस्टार्ट करा"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"प्रिंटरवर कोणतेही कनेक्शन नाही"</string>
<string name="reason_unknown" msgid="5507940196503246139">"अज्ञात"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – अनुपलब्ध"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> वापरायची?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"आपला दस्तऐवज प्रिंटरपर्यंत पोहचण्यापूर्वी एक किंवा अधिक सर्व्हरद्वारे जाऊ शकतो."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"क्षमस्व, त्याने कार्य केले नाही. पुन्हा प्रयत्न करा."</string>
<string name="print_error_retry" msgid="1426421728784259538">"पुन्हा प्रयत्न करा"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"हा प्रिंटर आत्ता उपलब्ध नाही."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"पूर्वावलोकन प्रदर्शित करू शकत नाही"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"पूर्वावलोकनाची तयारी करत आहे..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-ms-rMY/strings.xml b/packages/PrintSpooler/res/values-ms-rMY/strings.xml
index 73104e1..8af5232 100644
--- a/packages/PrintSpooler/res/values-ms-rMY/strings.xml
+++ b/packages/PrintSpooler/res/values-ms-rMY/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Perkhidmatan yang disyorkan"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Perkhidmatan yang dilumpuhkan"</string>
<string name="all_services_title" msgid="5578662754874906455">"Semua perkhidmatan"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other">Pasang untuk menemui <xliff:g id="COUNT_1">%1$s</xliff:g> pencetak</item>
+ <item quantity="one">Pasang untuk menemui <xliff:g id="COUNT_0">%1$s</xliff:g> pencetak</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Mencetak <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Membatalkan <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Ralat pencetak <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Mulakan semula"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Tiada sambungan ke pencetak"</string>
<string name="reason_unknown" msgid="5507940196503246139">"tidak diketahui"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – tidak tersedia"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Gunakan <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Dokumen anda mungkin melalui satu atau beberapa pelayan dalam perjalanan ke pencetak."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Maaf, itu tidak berjaya. Cuba lagi."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Cuba semula"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Pencetak ini tidak tersedia sekarang."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Tidak dapat memaparkan pratonton"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Menyediakan pratonton..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-my-rMM/strings.xml b/packages/PrintSpooler/res/values-my-rMM/strings.xml
index 8cec068..9b5f46a 100644
--- a/packages/PrintSpooler/res/values-my-rMM/strings.xml
+++ b/packages/PrintSpooler/res/values-my-rMM/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"အကြံပြုထားသည့် ဝန်ဆောင်မှုများ"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"ပိတ်ထားသည့် ဝန်ဆောင်မှုများ"</string>
<string name="all_services_title" msgid="5578662754874906455">"ဝန်ဆောင်မှုများ အားလုံး"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other">ပုံနှိပ်စက် <xliff:g id="COUNT_1">%1$s</xliff:g> ခုကို ရှာဖွေရန် စနစ်ထည့်သွင်းပါ</item>
+ <item quantity="one">ပုံနှိပ်စက် <xliff:g id="COUNT_0">%1$s</xliff:g> ခုကို ရှာဖွေရန် စနစ်ထည့်သွင်းပါ</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ကို စာထုတ်နေပါသည်"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ကို ပယ်ဖျက်နေပါသည်"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"စာထုတ်စက်မှ အမှား <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"အစက ပြန်စရန်"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"စာထုတ်စက်နဲ့ ဆက်သွယ်ထားမှု မရှိပါ"</string>
<string name="reason_unknown" msgid="5507940196503246139">"အကြောင်းအရာ မသိရှိ"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – မတွေ့ရှိပါ"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g>ကိုသုံးမလား။"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"သင်၏ စာရွက်စာတမ်းများသည် ပရင်တာထံသို့ သွားစဉ် ဆာဗာ တစ်ခု သို့မဟုတ် ပိုများပြီး ဖြတ်ကျော်နိုင်ရသည်။"</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"ဆော်ရီး၊ အဲဒါ အလုပ်မဖြစ်ခဲ့ပါ။ ထပ် စမ်းပါ။"</string>
<string name="print_error_retry" msgid="1426421728784259538">"ထပ်စမ်း"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"ဒီပရင်တာမှာ ယခုအချိန်မှာ မရနိုင်ပါ။"</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"အစမ်းကြည့်ခြင်းကို ပြသ၍မရပါ"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"အစမ်းကြည့်ရန် ပြင်ဆင်နေ…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-nb/strings.xml b/packages/PrintSpooler/res/values-nb/strings.xml
index 0a6f6c3..82282ba 100644
--- a/packages/PrintSpooler/res/values-nb/strings.xml
+++ b/packages/PrintSpooler/res/values-nb/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Anbefalte tjenester"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Tjenester som er slått av"</string>
<string name="all_services_title" msgid="5578662754874906455">"Alle tjenester"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other">Installer for å finne <xliff:g id="COUNT_1">%1$s</xliff:g> printere</item>
+ <item quantity="one">Installer for å finne <xliff:g id="COUNT_0">%1$s</xliff:g> printer</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Skriver ut <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Avbryter <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Skriverfeil <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Start på nytt"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Ingen forbindelse med skriveren"</string>
<string name="reason_unknown" msgid="5507940196503246139">"ukjent"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – utilgjengelig"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Vil du bruke <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Dokumentet ditt kan gå via flere tjenere før det når skriveren."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Beklager, det fungerte ikke. Prøv på nytt."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Prøv på nytt"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Denne skriveren er ikke tilgjengelig akkurat nå."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Kan ikke vise forhåndsvisningen"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Forbereder forhåndsvisningen …"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-ne-rNP/strings.xml b/packages/PrintSpooler/res/values-ne-rNP/strings.xml
index 5af3a04..4cf2f51 100644
--- a/packages/PrintSpooler/res/values-ne-rNP/strings.xml
+++ b/packages/PrintSpooler/res/values-ne-rNP/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"सिफारिस गरिएका सेवाहरू"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"असक्षम गरिएका सेवाहरू"</string>
<string name="all_services_title" msgid="5578662754874906455">"सबै सेवाहरू"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> प्रिन्टरहरू पत्ता लगाउनका लागि स्थापना गर्नुहोस्</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$s</xliff:g> प्रिन्टर पत्ता लगाउनका लागि स्थापना गर्नुहोस्</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"प्रिन्ट गरिँदै <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"रद्द गरिँदै <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"प्रिन्टर त्रुटि <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"पुनःस्टार्ट गर्नुहोस्"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"प्रिन्टरमा कुनै जडान छैन"</string>
<string name="reason_unknown" msgid="5507940196503246139">"अज्ञात"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> - अनुपलब्ध"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> प्रयोग गर्ने हो?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"तपाईँको कागजात प्रिन्टरमा जाँदा यसको मार्गमा एक वा धेरै सर्भरहरू पार हुनसक्छन्।"</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"माफ गर्नुहोस्, त्यसले काम गरेन। पुनः प्रयास गर्नुहोस्।"</string>
<string name="print_error_retry" msgid="1426421728784259538">"पुनःप्रयास गर्नुहोस्"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"यो प्रिन्टर अहिले उपलब्ध छैन।"</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"पूर्वावलोकनलाई प्रदर्शन गर्न सक्दैन"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"पूर्वावलोकन तयारी..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-nl/strings.xml b/packages/PrintSpooler/res/values-nl/strings.xml
index 4afdb86..83b9a22 100644
--- a/packages/PrintSpooler/res/values-nl/strings.xml
+++ b/packages/PrintSpooler/res/values-nl/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Aanbevolen services"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Uitgeschakelde services"</string>
<string name="all_services_title" msgid="5578662754874906455">"Alle services"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other">Installeren om <xliff:g id="COUNT_1">%1$s</xliff:g> printers te vinden</item>
+ <item quantity="one">Installeren om <xliff:g id="COUNT_0">%1$s</xliff:g> printer te vinden</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> afdrukken"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> annuleren"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Printerfout <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Opnieuw starten"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Geen verbinding met printer"</string>
<string name="reason_unknown" msgid="5507940196503246139">"onbekend"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – niet beschikbaar"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> gebruiken?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Je document kan via een of meer servers naar de printer worden verzonden."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Dat werkte niet. Probeer het opnieuw."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Opnieuw proberen"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Deze printer is momenteel niet beschikbaar."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Kan voorbeeld niet weergeven"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Voorbeeld voorbereiden…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-pa-rIN/strings.xml b/packages/PrintSpooler/res/values-pa-rIN/strings.xml
index 1886ef5..5f3366f 100644
--- a/packages/PrintSpooler/res/values-pa-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-pa-rIN/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"ਸਿਫ਼ਾਰਸ਼ ਕੀਤੀਆਂ ਸੇਵਾਵਾਂ"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"ਅਯੋਗ ਬਣਾਈਆਂ ਗਈਆਂ ਸੇਵਾਵਾਂ"</string>
<string name="all_services_title" msgid="5578662754874906455">"ਸਾਰੀਆਂ ਸੇਵਾਵਾਂ"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$s</xliff:g> ਪ੍ਰਿੰਟਰ ਖੋਜਣ ਲਈ ਸਥਾਪਤ ਕਰੋ</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> ਪ੍ਰਿੰਟਰ ਖੋਜਣ ਲਈ ਸਥਾਪਤ ਕਰੋ</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ਨੂੰ ਪ੍ਰਿੰਟ ਕਰ ਰਿਹਾ ਹੈ"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ਨੂੰ ਰੱਦ ਕਰ ਰਿਹਾ ਹੈ"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"ਪ੍ਰਿੰਟਰ ਅਸ਼ੁੱਧੀ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"ਰੀਸਟਾਰਟ ਕਰੋ"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"ਪ੍ਰਿੰਟਰ ਲਈ ਕੋਈ ਕਨੈਕਸ਼ਨ ਨਹੀਂ"</string>
<string name="reason_unknown" msgid="5507940196503246139">"ਅਗਿਆਤ"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – ਅਣਉਪਲਬਧ"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"ਕੀ <xliff:g id="SERVICE">%1$s</xliff:g> ਵਰਤਣੀ ਹੈ?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"ਤੁਹਾਡਾ ਦਸਤਾਵੇਜ਼ ਪ੍ਰਿੰਟਰ ਵਿੱਚ ਜਾਣ ਲਈ ਇੱਕ ਜਾਂ ਦੋ ਸਰਵਰਾਂ ਵਿੱਚੋਂ ਲੰਘਦਾ ਹੈ।"</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"ਮਾਫ਼ ਕਰਨਾ, ਉਸਨੇ ਲਾਭਕਾਰੀ ਨਹੀਂ ਹੋਇਆ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
<string name="print_error_retry" msgid="1426421728784259538">"ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"ਇਹ ਪ੍ਰਿੰਟਰ ਇਸ ਵੇਲੇ ਉਪਲਬਧ ਨਹੀਂ ਹੈ।"</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"ਝਲਕ ਨਹੀਂ ਵਿਖਾਈ ਜਾ ਸਕਦੀ"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"ਪ੍ਰੀਵਿਊ ਦੀ ਤਿਆਰੀ ਕਰ ਰਿਹਾ ਹੈ…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-pl/strings.xml b/packages/PrintSpooler/res/values-pl/strings.xml
index 45649bb..e7fb7b6 100644
--- a/packages/PrintSpooler/res/values-pl/strings.xml
+++ b/packages/PrintSpooler/res/values-pl/strings.xml
@@ -74,6 +74,12 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Polecane usługi"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Wyłączone usługi"</string>
<string name="all_services_title" msgid="5578662754874906455">"Wszystkie usługi"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="few">Zainstaluj, by wykryć <xliff:g id="COUNT_1">%1$s</xliff:g> drukarki</item>
+ <item quantity="many">Zainstaluj, by wykryć <xliff:g id="COUNT_1">%1$s</xliff:g> drukarek</item>
+ <item quantity="other">Zainstaluj, by wykryć <xliff:g id="COUNT_1">%1$s</xliff:g> drukarki</item>
+ <item quantity="one">Zainstaluj, by wykryć <xliff:g id="COUNT_0">%1$s</xliff:g> drukarkę</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Drukowanie: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Anulowanie: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Błąd drukarki: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -82,7 +88,6 @@
<string name="restart" msgid="2472034227037808749">"Od nowa"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Brak połączenia z drukarką"</string>
<string name="reason_unknown" msgid="5507940196503246139">"brak informacji"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – niedostępne"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Użyć usługi <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Zanim dokument dotrze do drukarki, może przejść przez jeden lub kilka serwerów."</string>
<string-array name="color_mode_labels">
@@ -102,5 +107,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"To nie zadziałało. Spróbuj jeszcze raz."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Ponów próbę"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Drukarka nie jest teraz dostępna."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Nie można wyświetlić podglądu"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Przygotowuję podgląd…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-pt-rBR/strings.xml b/packages/PrintSpooler/res/values-pt-rBR/strings.xml
index 58eb24f..dd8cdb2 100644
--- a/packages/PrintSpooler/res/values-pt-rBR/strings.xml
+++ b/packages/PrintSpooler/res/values-pt-rBR/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Serviços recomendados"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Serviços desativados"</string>
<string name="all_services_title" msgid="5578662754874906455">"Todos os serviços"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="one">Instale para encontrar <xliff:g id="COUNT_1">%1$s</xliff:g> impressoras</item>
+ <item quantity="other">Instale para encontrar <xliff:g id="COUNT_1">%1$s</xliff:g> impressoras</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Imprimindo <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelando <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Erro ao imprimir <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Reiniciar"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Sem conexão com a impressora"</string>
<string name="reason_unknown" msgid="5507940196503246139">"desconhecido"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – não disponível"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Usar <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Seu documento pode passar por um ou mais servidores até chegar à impressora."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Falhou. Tente novamente."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Tentar novamente"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Esta impressora não está disponível no momento."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Não é possível exibir a visualização"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Preparando visualização…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-pt-rPT/strings.xml b/packages/PrintSpooler/res/values-pt-rPT/strings.xml
index 370bbb9..c1fe7bf 100644
--- a/packages/PrintSpooler/res/values-pt-rPT/strings.xml
+++ b/packages/PrintSpooler/res/values-pt-rPT/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Serviços recomendados"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Serviços desativados"</string>
<string name="all_services_title" msgid="5578662754874906455">"Todos os serviços"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other">Instale para detetar <xliff:g id="COUNT_1">%1$s</xliff:g> impressoras</item>
+ <item quantity="one">Instale para detetar <xliff:g id="COUNT_0">%1$s</xliff:g> impressora</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"A imprimir <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"A cancelar <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Erro da impressora <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Reiniciar"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Sem ligação à impressora"</string>
<string name="reason_unknown" msgid="5507940196503246139">"desconhecido"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – indisponível"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Pretende utilizar o <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"O seu documento pode passar por um ou mais servidores no respetivo caminho para a impressora."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Lamentamos, mas isso não funcionou. Tente novam."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Tentar novamente"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Esta impressora não está atualmente disponível."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Não é possível apresentar a pré-visualização"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"A preparar a pré-visualização..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-pt/strings.xml b/packages/PrintSpooler/res/values-pt/strings.xml
index 58eb24f..dd8cdb2 100644
--- a/packages/PrintSpooler/res/values-pt/strings.xml
+++ b/packages/PrintSpooler/res/values-pt/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Serviços recomendados"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Serviços desativados"</string>
<string name="all_services_title" msgid="5578662754874906455">"Todos os serviços"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="one">Instale para encontrar <xliff:g id="COUNT_1">%1$s</xliff:g> impressoras</item>
+ <item quantity="other">Instale para encontrar <xliff:g id="COUNT_1">%1$s</xliff:g> impressoras</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Imprimindo <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelando <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Erro ao imprimir <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Reiniciar"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Sem conexão com a impressora"</string>
<string name="reason_unknown" msgid="5507940196503246139">"desconhecido"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – não disponível"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Usar <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Seu documento pode passar por um ou mais servidores até chegar à impressora."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Falhou. Tente novamente."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Tentar novamente"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Esta impressora não está disponível no momento."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Não é possível exibir a visualização"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Preparando visualização…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-ro/strings.xml b/packages/PrintSpooler/res/values-ro/strings.xml
index 1097d56..b326e09 100644
--- a/packages/PrintSpooler/res/values-ro/strings.xml
+++ b/packages/PrintSpooler/res/values-ro/strings.xml
@@ -73,6 +73,11 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Servicii recomandate"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Servicii dezactivate"</string>
<string name="all_services_title" msgid="5578662754874906455">"Toate serviciile"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="few">Instalați pentru a descoperi <xliff:g id="COUNT_1">%1$s</xliff:g> imprimante</item>
+ <item quantity="other">Instalați pentru a descoperi <xliff:g id="COUNT_1">%1$s</xliff:g> de imprimante</item>
+ <item quantity="one">Instalați pentru a descoperi <xliff:g id="COUNT_0">%1$s</xliff:g> imprimantă</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Se printează <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Se anulează <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Eroare de printare: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -81,7 +86,6 @@
<string name="restart" msgid="2472034227037808749">"Reporniți"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Nu există conexiune la o imprimantă"</string>
<string name="reason_unknown" msgid="5507940196503246139">"necunoscut"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> - indisponibil"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Folosiți <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Documentul poate trece prin unul sau mai multe servere pe calea spre imprimantă."</string>
<string-array name="color_mode_labels">
@@ -101,5 +105,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Ne pare rău, operațiunea nu a reușit. Încercați din nou."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Reîncercați"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Această imprimantă nu este disponibilă momentan."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Previzualizarea nu se poate afișa"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Se pregătește previzualizarea..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-ru/strings.xml b/packages/PrintSpooler/res/values-ru/strings.xml
index 24b1e0d..5acadbc 100644
--- a/packages/PrintSpooler/res/values-ru/strings.xml
+++ b/packages/PrintSpooler/res/values-ru/strings.xml
@@ -74,6 +74,12 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Рекомендуемые службы"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Отключенные службы"</string>
<string name="all_services_title" msgid="5578662754874906455">"Все службы печати"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="one">Установите, чтобы найти <xliff:g id="COUNT_1">%1$s</xliff:g> принтер</item>
+ <item quantity="few">Установите, чтобы найти <xliff:g id="COUNT_1">%1$s</xliff:g> принтера</item>
+ <item quantity="many">Установите, чтобы найти <xliff:g id="COUNT_1">%1$s</xliff:g> принтеров</item>
+ <item quantity="other">Установите, чтобы найти <xliff:g id="COUNT_1">%1$s</xliff:g> принтера</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Печать задания \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\"…"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Отмена задания <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>…"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Ошибка задания \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\""</string>
@@ -82,7 +88,6 @@
<string name="restart" msgid="2472034227037808749">"Повторить"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Нет связи с принтером"</string>
<string name="reason_unknown" msgid="5507940196503246139">"неизвестно"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – недоступен"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Использовать <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Документ может пересылаться на принтер через несколько серверов."</string>
<string-array name="color_mode_labels">
@@ -102,5 +107,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Ошибка. Повторите попытку."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Повторить"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Принтер не готов."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Сбой предварительного просмотра"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Подготовка изображения…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-si-rLK/strings.xml b/packages/PrintSpooler/res/values-si-rLK/strings.xml
index 707c151..db4c5fd 100644
--- a/packages/PrintSpooler/res/values-si-rLK/strings.xml
+++ b/packages/PrintSpooler/res/values-si-rLK/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"නිර්දේශිත සේවා"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"අබල කළ සේවා"</string>
<string name="all_services_title" msgid="5578662754874906455">"සියලු සේවා"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="one">මුද්රණ යන්ත්ර <xliff:g id="COUNT_1">%1$s</xliff:g>ක් සොයා ගැනීමට ස්ථාපනය කරන්න</item>
+ <item quantity="other">මුද්රණ යන්ත්ර <xliff:g id="COUNT_1">%1$s</xliff:g>ක් සොයා ගැනීමට ස්ථාපනය කරන්න</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> මුද්රණය වේ"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"අවලංගු කෙරේ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"මුද්රණ දෝෂය <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"යළි අරඹන්න"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"මුද්රණ යන්ත්රය වෙත සම්බන්ධය නැත"</string>
<string name="reason_unknown" msgid="5507940196503246139">"නොදනී"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – ලද නොහැක"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> භාවිත කරන්නද?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"ඔබගේ ලේඛනය මුද්රණ යන්ත්රයට යන අතරතුර සේවාදායක එකක් හෝ කිහිපයක් හරහා යා හැක."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"කණගාටුයි, එය වැඩ නොකරයි. නැවත උත්සහ කරන්න."</string>
<string name="print_error_retry" msgid="1426421728784259538">"නැවත උත්සාහ කරන්න"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"දැන් මෙම මුද්රණ යන්ත්රය නොපවතී."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"පෙරදසුන සංදර්ශනය කළ නොහැකිය"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"පෙරදසුන සූදානම් කරමින්…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-sk/strings.xml b/packages/PrintSpooler/res/values-sk/strings.xml
index 1f13b54..63ee5e2 100644
--- a/packages/PrintSpooler/res/values-sk/strings.xml
+++ b/packages/PrintSpooler/res/values-sk/strings.xml
@@ -74,6 +74,12 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Odporúčané služby"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Zakázané služby"</string>
<string name="all_services_title" msgid="5578662754874906455">"Všetky služby"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="few">Nainštalujte a objavte <xliff:g id="COUNT_1">%1$s</xliff:g> tlačiarne</item>
+ <item quantity="many">Nainštalujte a objavte <xliff:g id="COUNT_1">%1$s</xliff:g> tlačiarne</item>
+ <item quantity="other">Nainštalujte a objavte <xliff:g id="COUNT_1">%1$s</xliff:g> tlačiarní</item>
+ <item quantity="one">Nainštalujte a objavte <xliff:g id="COUNT_0">%1$s</xliff:g> tlačiareň</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Prebieha tlač úlohy <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Prebieha zrušenie úlohy <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Chyba tlačiarne – úloha <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -82,7 +88,6 @@
<string name="restart" msgid="2472034227037808749">"Spustiť znova"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Žiadne pripojenie k tlačiarni"</string>
<string name="reason_unknown" msgid="5507940196503246139">"neznáme"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – nie je k dispozícii"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Použiť službu <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Skôr ako sa váš dokument dostane do tlačiarne, môže prejsť jedným alebo viacerými servermi."</string>
<string-array name="color_mode_labels">
@@ -102,5 +107,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Je nám to ľúto, nefungovalo to. Skúste to znova."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Opakovať"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Táto tlačiareň nie je momentálne k dispozícii."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Ukážka sa nedá zobraziť"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Pripravuje sa ukážka..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-sl/strings.xml b/packages/PrintSpooler/res/values-sl/strings.xml
index 3f8a5e6..f7616db 100644
--- a/packages/PrintSpooler/res/values-sl/strings.xml
+++ b/packages/PrintSpooler/res/values-sl/strings.xml
@@ -74,6 +74,12 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Priporočene storitve"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Onemogočene storitve"</string>
<string name="all_services_title" msgid="5578662754874906455">"Vse storitve"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="one">Namestite za odkrivanje <xliff:g id="COUNT_1">%1$s</xliff:g> tiskalnika</item>
+ <item quantity="two">Namestite za odkrivanje <xliff:g id="COUNT_1">%1$s</xliff:g> tiskalnikov</item>
+ <item quantity="few">Namestite za odkrivanje <xliff:g id="COUNT_1">%1$s</xliff:g> tiskalnikov</item>
+ <item quantity="other">Namestite za odkrivanje <xliff:g id="COUNT_1">%1$s</xliff:g> tiskalnikov</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Tiskanje: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Preklic: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Napaka tiskalnika: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -82,7 +88,6 @@
<string name="restart" msgid="2472034227037808749">"Začni znova"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Ni povezave s tiskalnikom"</string>
<string name="reason_unknown" msgid="5507940196503246139">"neznano"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – ni na voljo"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Želite uporabiti storitev <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Dokument gre lahko na poti do tiskalnika skozi enega ali več strežnikov."</string>
<string-array name="color_mode_labels">
@@ -102,5 +107,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"To žal ni delovalo. Poskusite znova."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Poskusi znova"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Ta tiskalnik trenutno ni na voljo."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Predogleda ni mogoče prikazati"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Priprava predogleda …"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-sq-rAL/strings.xml b/packages/PrintSpooler/res/values-sq-rAL/strings.xml
index 0b843d7..f4d2817 100644
--- a/packages/PrintSpooler/res/values-sq-rAL/strings.xml
+++ b/packages/PrintSpooler/res/values-sq-rAL/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Shërbimet e rekomanduara"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Shërbimet e çaktivizuara"</string>
<string name="all_services_title" msgid="5578662754874906455">"Të gjitha shërbimet"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other">Instaloje për të zbuluar <xliff:g id="COUNT_1">%1$s</xliff:g> printera</item>
+ <item quantity="one">Instaloje për të zbuluar <xliff:g id="COUNT_0">%1$s</xliff:g> printer</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Po printon <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Po anulon <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Printeri ndeshi në gabim gjatë punës: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Rifillo"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Printeri nuk është i lidhur"</string>
<string name="reason_unknown" msgid="5507940196503246139">"e panjohur"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – nuk mundësohet"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Përdor <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Dokumenti mund të kalojë përmes një ose shumë serverëve deri te printeri."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Na vjen keq, nuk funksionoi! Provo përsëri."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Provo sërish"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Ky printer nuk mund të përdoret tani."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Nuk mund të shfaqet paraafishimi"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Po përgatit shikimin paraprak…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-sr/strings.xml b/packages/PrintSpooler/res/values-sr/strings.xml
index 8baa23c..b285044 100644
--- a/packages/PrintSpooler/res/values-sr/strings.xml
+++ b/packages/PrintSpooler/res/values-sr/strings.xml
@@ -73,6 +73,11 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Препоручене услуге"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Онемогућене услуге"</string>
<string name="all_services_title" msgid="5578662754874906455">"Све услуге"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="one">Инсталирајте да бисте открили <xliff:g id="COUNT_1">%1$s</xliff:g> штампач</item>
+ <item quantity="few">Инсталирајте да бисте открили <xliff:g id="COUNT_1">%1$s</xliff:g> штампача</item>
+ <item quantity="other">Инсталирајте да бисте открили <xliff:g id="COUNT_1">%1$s</xliff:g> штампача</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Штампа се <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Отказује се <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Грешка штампача <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -81,7 +86,6 @@
<string name="restart" msgid="2472034227037808749">"Поново покрени"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Нема везе са штампачем"</string>
<string name="reason_unknown" msgid="5507940196503246139">"непознато"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – недоступан"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Желите ли да користите <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Документ може да прође кроз један или више сервера на путу до штампача."</string>
<string-array name="color_mode_labels">
@@ -101,5 +105,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Жао нам је, ово није успело. Покушајте поново."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Покушајте поново"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Овај штампач тренутно није доступан."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Није успео приказ прегледа"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Припрема прегледа..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-sv/strings.xml b/packages/PrintSpooler/res/values-sv/strings.xml
index 64b6b20..4a72800 100644
--- a/packages/PrintSpooler/res/values-sv/strings.xml
+++ b/packages/PrintSpooler/res/values-sv/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Rekommenderade tjänster"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Inaktiverade tjänster"</string>
<string name="all_services_title" msgid="5578662754874906455">"Alla tjänster"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other">Installera och hitta <xliff:g id="COUNT_1">%1$s</xliff:g> skrivare</item>
+ <item quantity="one">Installera och hitta <xliff:g id="COUNT_0">%1$s</xliff:g> skrivare</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Skriver ut <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Avbryter <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Skrivarfel för <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Starta om"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Ingen anslutning till skrivaren"</string>
<string name="reason_unknown" msgid="5507940196503246139">"okänt"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – inte tillgänglig"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Vill du använda <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"På vägen till skrivaren kan dokumentet passera en eller flera servrar."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Det fungerade tyvärr inte. Försök igen."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Försök igen"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Den här skrivaren är inte tillgänglig just nu."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Det gick inte att visa förhandsgranskningen"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Förbereder förhandsvisning ..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-sw/strings.xml b/packages/PrintSpooler/res/values-sw/strings.xml
index b3ffa71..34b935d 100644
--- a/packages/PrintSpooler/res/values-sw/strings.xml
+++ b/packages/PrintSpooler/res/values-sw/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Huduma zinazopendekezwa"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Huduma ambazo haziruhusiwi"</string>
<string name="all_services_title" msgid="5578662754874906455">"Huduma zote"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other">Sakinisha ili ugundue printa <xliff:g id="COUNT_1">%1$s</xliff:g></item>
+ <item quantity="one">Sakinisha ili ugundue printa <xliff:g id="COUNT_0">%1$s</xliff:g></item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Inachapisha <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Inaghairi <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Hitilafu ya kuchapisha <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Anzisha upya"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Hakuna muunganisho kwa printa"</string>
<string name="reason_unknown" msgid="5507940196503246139">"haijulikani"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> - haipatikani"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Ungependa kutumia <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Huenda hati yako ikapitia seva moja au zaidi kabla ya kufika kwenye printa."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Samahani, hiyo haikufanya kazi. Jaribu tena."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Jaribu tena"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Printa hii haipatikani kwa sasa."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Haiwezi kupakia onyesho la kuchungulia"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Inaandaa onyesho la kuchungulia..."</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-ta-rIN/strings.xml b/packages/PrintSpooler/res/values-ta-rIN/strings.xml
index 7ae3cbc..22f41bf 100644
--- a/packages/PrintSpooler/res/values-ta-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-ta-rIN/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"பரிந்துரைக்கப்படும் அச்சுப் பொறிகள்"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"முடக்கப்பட்ட அச்சுப் பொறிகள்"</string>
<string name="all_services_title" msgid="5578662754874906455">"எல்லா அச்சுப் பொறிகளும்"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> பிரிண்டர்களைக் கண்டறிய, நிறுவவும்</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$s</xliff:g> பிரிண்டரைக் கண்டறிய, நிறுவவும்</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ஐ அச்சிடுகிறது"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ஐ ரத்துசெய்கிறது"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"பிரிண்டர் பிழை <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"மீண்டும் தொடங்கு"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"அச்சுப்பொறியுடன் இணைக்கப்படவில்லை"</string>
<string name="reason_unknown" msgid="5507940196503246139">"அறியப்படாதது"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – இல்லை"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g>ஐப் பயன்படுத்தவா?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"உங்கள் ஆவணம் பிரிண்டருக்குச் செல்லும் வழியில் ஒன்று அல்லது அதற்கு மேற்பட்ட சேவையகங்களைக் கடந்து செல்லக்கூடும்."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"செயல்படவில்லை. மீண்டும் முயலவும்."</string>
<string name="print_error_retry" msgid="1426421728784259538">"மீண்டும் முயலவும்"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"இப்போது பிரிண்டர் இல்லை."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"மாதிரிக்காட்சியைக் காட்ட முடியவில்லை"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"மாதிரிக்காட்சியைத் தயார்படுத்துகிறது…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-te-rIN/strings.xml b/packages/PrintSpooler/res/values-te-rIN/strings.xml
index 9e8dea2..1211cfd 100644
--- a/packages/PrintSpooler/res/values-te-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-te-rIN/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"సిఫార్సు చేయబడిన సేవలు"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"నిలిపివేసిన సేవలు"</string>
<string name="all_services_title" msgid="5578662754874906455">"అన్ని సేవలు"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> ప్రింటర్లను కనుగొనడానికి ఇన్స్టాల్ చేయండి</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$s</xliff:g> ప్రింటర్ను కనుగొనడానికి ఇన్స్టాల్ చేయండి</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>ను ముద్రిస్తోంది"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>ను రద్దు చేస్తోంది"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"ప్రింటర్ లోపం <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"పునఃప్రారంభించు"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"ప్రింటర్కు కనెక్షన్ లేదు"</string>
<string name="reason_unknown" msgid="5507940196503246139">"తెలియదు"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – అందుబాటులో లేదు"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g>ని ఉపయోగించాలా?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"మీ పత్రం ప్రింటర్కు వెళ్లే మార్గంలో ఒకటి లేదా అంతకంటే ఎక్కువ సర్వర్ల గుండా వెళ్లవచ్చు."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"క్షమించండి, అది పని చేయలేదు. మళ్లీ ప్రయత్నించండి."</string>
<string name="print_error_retry" msgid="1426421728784259538">"మళ్లీ ప్రయత్నించు"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"ఈ ప్రింటర్ ప్రస్తుతం అందుబాటులో లేదు."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"పరిదృశ్యాన్ని ప్రదర్శించడం సాధ్యపడలేదు"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"పరిదృశ్యం సిద్ధమవుతోంది…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-th/strings.xml b/packages/PrintSpooler/res/values-th/strings.xml
index c623dd7..7f99288 100644
--- a/packages/PrintSpooler/res/values-th/strings.xml
+++ b/packages/PrintSpooler/res/values-th/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"บริการที่แนะนำ"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"บริการที่ปิดใช้"</string>
<string name="all_services_title" msgid="5578662754874906455">"บริการทั้งหมด"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other">ติดตั้งเพื่อค้นหาเครื่องพิมพ์ <xliff:g id="COUNT_1">%1$s</xliff:g> เครื่อง</item>
+ <item quantity="one">ติดตั้งเพื่อค้นหาเครื่องพิมพ์ <xliff:g id="COUNT_0">%1$s</xliff:g> เครื่อง</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"กำลังพิมพ์ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"กำลังยกเลิก <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"ข้อผิดพลาดเครื่องพิมพ์ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"เริ่มต้นใหม่"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"ไม่มีการเชื่อมต่อไปยังเครื่องพิมพ์"</string>
<string name="reason_unknown" msgid="5507940196503246139">"ไม่ทราบ"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ไม่พร้อมใช้งาน"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"ใช้ <xliff:g id="SERVICE">%1$s</xliff:g> ไหม"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"เอกสารของคุณอาจต้องผ่านมากกว่าหนึ่งเซิร์ฟเวอร์ระหว่างส่งไปยังเครื่องพิมพ์"</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"ขออภัย ไม่สามารถใช้งานได้ ลองอีกครั้ง"</string>
<string name="print_error_retry" msgid="1426421728784259538">"ลองอีกครั้ง"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"เครื่องพิมพ์นี้ไม่พร้อมใช้งานในขณะนี้"</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"ไม่สามารถแสดงตัวอย่าง"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"กำลังเตรียมการแสดงตัวอย่าง…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-tl/strings.xml b/packages/PrintSpooler/res/values-tl/strings.xml
index 0494cf6..7b50815 100644
--- a/packages/PrintSpooler/res/values-tl/strings.xml
+++ b/packages/PrintSpooler/res/values-tl/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Mga inirerekomendang serbisyo"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Mga naka-disable na serbisyo"</string>
<string name="all_services_title" msgid="5578662754874906455">"Lahat ng serbisyo"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="one">I-install upang tumuklas ng <xliff:g id="COUNT_1">%1$s</xliff:g> printer</item>
+ <item quantity="other">I-install upang tumuklas ng <xliff:g id="COUNT_1">%1$s</xliff:g> na printer</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Pini-print ang <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Kinakansela ang <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Error sa printer <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"I-restart"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Hindi nakakonekta sa printer"</string>
<string name="reason_unknown" msgid="5507940196503246139">"hindi alam"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – hindi available"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Gusto mo bang gamitin ang <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Bago ma-print ang iyong dokumento, maaari itong dumaan sa isa o higit pang mga server."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Paumanhin, hindi iyon gumana. Subukang muli."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Subukang muli"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Hindi available ang printer na ito sa ngayon."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Hindi maipakita ang preview"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Inihahanda ang preview…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-tr/strings.xml b/packages/PrintSpooler/res/values-tr/strings.xml
index 2818f36..1ca722b 100644
--- a/packages/PrintSpooler/res/values-tr/strings.xml
+++ b/packages/PrintSpooler/res/values-tr/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Önerilen hizmetler"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Devre dışı bırakılmış hizmetler"</string>
<string name="all_services_title" msgid="5578662754874906455">"Tüm hizmetler"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> yazıcıyı keşfetmek için yükleyin</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$s</xliff:g> yazıcıyı keşfetmek için yükleyin</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> yazdırılıyor"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> iptal ediliyor"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Yazıcı hatası: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Yeniden başlat"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Yazıcı bağlantısı yok"</string>
<string name="reason_unknown" msgid="5507940196503246139">"bilinmiyor"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – kullanılamıyor"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> kullanılsın mı?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Dokümanınız yazıcıya giderken bir veya daha fazla sunucudan geçebilir."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Maalesef bu işe yaramadı. Tekrar deneyin."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Yeniden dene"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Bu yazı şu anda kullanılamıyor."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Önizleme gösterilemiyor"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Önizleme hazırlanıyor…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-uk/strings.xml b/packages/PrintSpooler/res/values-uk/strings.xml
index 41051af..8004639 100644
--- a/packages/PrintSpooler/res/values-uk/strings.xml
+++ b/packages/PrintSpooler/res/values-uk/strings.xml
@@ -74,6 +74,12 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Рекомендовані служби"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Вимкнені служби"</string>
<string name="all_services_title" msgid="5578662754874906455">"Усі служби"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="one">Установіть, щоб знайти <xliff:g id="COUNT_1">%1$s</xliff:g> принтер</item>
+ <item quantity="few">Установіть, щоб знайти <xliff:g id="COUNT_1">%1$s</xliff:g> принтери</item>
+ <item quantity="many">Установіть, щоб знайти <xliff:g id="COUNT_1">%1$s</xliff:g> принтерів</item>
+ <item quantity="other">Установіть, щоб знайти <xliff:g id="COUNT_1">%1$s</xliff:g> принтера</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Завдання \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\" друкується"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Завдання \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\" скасовується"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Помилка завдання \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\""</string>
@@ -82,7 +88,6 @@
<string name="restart" msgid="2472034227037808749">"Перезапустити"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Немає з’єднання з принтером"</string>
<string name="reason_unknown" msgid="5507940196503246139">"невідомо"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"Завдання \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\" не доступне"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Увімкнути службу <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Коли ви надсилаєте документ на принтер, він може проходити через декілька серверів."</string>
<string-array name="color_mode_labels">
@@ -102,5 +107,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"На жаль, сталася помилка. Повторіть спробу."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Повторити"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Цей принтер зараз недоступний."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Не вдалося відкрити попередній перегляд"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Підготовка до попереднього перегляду…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-ur-rPK/strings.xml b/packages/PrintSpooler/res/values-ur-rPK/strings.xml
index a94b16f..19e759cb 100644
--- a/packages/PrintSpooler/res/values-ur-rPK/strings.xml
+++ b/packages/PrintSpooler/res/values-ur-rPK/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"تجویز کردہ سروسز"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"غیر فعال کردہ سروسز"</string>
<string name="all_services_title" msgid="5578662754874906455">"تمام سروسز"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> پرنٹرز دریافت کرنے کیلئے انسٹال کریں</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$s</xliff:g> پرنٹر دریافت کرنے کیلئے انسٹال کریں</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> پرنٹ کررہا ہے"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> کو منسوخ کر رہا ہے"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"پرنٹر کی خرابی <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"دوبارہ شروع کریں"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"پرنٹر کے ساتھ کوئی کنکشن نہیں ہے"</string>
<string name="reason_unknown" msgid="5507940196503246139">"نامعلوم"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – دستیاب نہیں ہے"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> استعمال کریں؟"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"آپ کی دستاویز پرنٹر تک جاتے ہوئے ممکن ہے ایک یا زیادہ سرورز سے گزرے۔"</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"معذرت، اس نے کام نہیں کیا۔ دوبارہ کوشش کریں۔"</string>
<string name="print_error_retry" msgid="1426421728784259538">"دوبارہ کوشش کریں"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"یہ پرنٹر ابھی دستیاب نہیں ہے۔"</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"پیش منظر ڈسپلے نہیں ہو سکتا"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"پیش منظر کو تیار کیا جا رہا ہے…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-uz-rUZ/strings.xml b/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
index a6af5bd..cf87a74 100644
--- a/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
+++ b/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Tavsiya etilgan xizmatlar"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"O‘chirib qo‘yilgan xizmatlar"</string>
<string name="all_services_title" msgid="5578662754874906455">"Barcha xizmatlar"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> ta printerni topish uchun o‘rnating</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$s</xliff:g> ta printerni topish uchun o‘rnating</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Chop etilmoqda: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> bekor qilinmoqda"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Printerda xatolik: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Qayta boshlash"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Printer ulanmagan"</string>
<string name="reason_unknown" msgid="5507940196503246139">"noma’lum"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – mavjud emas"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"<xliff:g id="SERVICE">%1$s</xliff:g> xizmatidan foydalanilsinmi?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Hujjatingiz chop etilishidan oldin bir yoki bir necha serverlardan o‘tishi mumkin."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Kechirasiz, ishlamadi. Qayta urinib ko‘ring."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Qayta urinish"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Ushbu printer hozirda mavjud emas."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Oldindan ko‘rsatib bo‘lmaydi"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Dastlabki ko\'rishga tayyorlanmoqda…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-vi/strings.xml b/packages/PrintSpooler/res/values-vi/strings.xml
index df9e1a4..2c1fa27 100644
--- a/packages/PrintSpooler/res/values-vi/strings.xml
+++ b/packages/PrintSpooler/res/values-vi/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Dịch vụ được đề xuất"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Dịch vụ đã tắt"</string>
<string name="all_services_title" msgid="5578662754874906455">"Tất cả dịch vụ"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other">Cài đặt để phát hiện <xliff:g id="COUNT_1">%1$s</xliff:g> máy in</item>
+ <item quantity="one">Cài đặt để phát hiện <xliff:g id="COUNT_0">%1$s</xliff:g> máy in</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"In <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Hủy <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Lỗi máy in <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Bắt đầu lại"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Không có kết nối nào với máy in"</string>
<string name="reason_unknown" msgid="5507940196503246139">"không xác định"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – không khả dụng"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Sử dụng <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Tài liệu của bạn có thể đi qua một hoặc nhiều máy chủ trên đường đến máy in."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Rất tiếc, tính năng đó không hoạt động. Hãy thử lại."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Thử lại"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Máy in này hiện không khả dụng."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Không thể hiển thị bản xem trước"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Đang chuẩn bị xem trước…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-zh-rCN/strings.xml b/packages/PrintSpooler/res/values-zh-rCN/strings.xml
index fb30e44..b350051 100644
--- a/packages/PrintSpooler/res/values-zh-rCN/strings.xml
+++ b/packages/PrintSpooler/res/values-zh-rCN/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"推荐的服务"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"已停用的服务"</string>
<string name="all_services_title" msgid="5578662754874906455">"所有服务"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other">安装即可找到 <xliff:g id="COUNT_1">%1$s</xliff:g> 台打印机</item>
+ <item quantity="one">安装即可找到 <xliff:g id="COUNT_0">%1$s</xliff:g> 台打印机</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"正在打印“<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>”"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"正在取消打印“<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>”"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"打印机在打印“<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>”时出错"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"重新开始"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"未与打印机建立连接"</string>
<string name="reason_unknown" msgid="5507940196503246139">"未知"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> - 无法使用"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"要使用<xliff:g id="SERVICE">%1$s</xliff:g>吗?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"您的文档可能会通过一个或多个服务器发送至打印机。"</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"抱歉,操作失败。请重试。"</string>
<string name="print_error_retry" msgid="1426421728784259538">"重试"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"该打印机目前无法使用。"</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"无法显示预览"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"即将显示预览…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-zh-rHK/strings.xml b/packages/PrintSpooler/res/values-zh-rHK/strings.xml
index f7c8fc9..192b41b 100644
--- a/packages/PrintSpooler/res/values-zh-rHK/strings.xml
+++ b/packages/PrintSpooler/res/values-zh-rHK/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"推薦服務"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"已停用的服務"</string>
<string name="all_services_title" msgid="5578662754874906455">"所有服務"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other">安裝即可使用 <xliff:g id="COUNT_1">%1$s</xliff:g> 部印表機</item>
+ <item quantity="one">安裝即可使用 <xliff:g id="COUNT_0">%1$s</xliff:g> 部印表機</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"正在列印 <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"正在取消 <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"打印機錯誤:<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"重新開始"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"尚未與打印機連線"</string>
<string name="reason_unknown" msgid="5507940196503246139">"不明"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – 無法使用"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"要使用 <xliff:g id="SERVICE">%1$s</xliff:g> 嗎?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"您的文件可能會通過一部或多部伺服器才傳送至打印機。"</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"很抱歉,行不通。請再試一次。"</string>
<string name="print_error_retry" msgid="1426421728784259538">"重試"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"這部打印機目前無法使用。"</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"無法顯示預覽"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"正在準備預覽…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-zh-rTW/strings.xml b/packages/PrintSpooler/res/values-zh-rTW/strings.xml
index aa18f3c..4aa5681 100644
--- a/packages/PrintSpooler/res/values-zh-rTW/strings.xml
+++ b/packages/PrintSpooler/res/values-zh-rTW/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"建議的列印服務"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"已停用的列印服務"</string>
<string name="all_services_title" msgid="5578662754874906455">"所有列印服務"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="other">安裝即可使用 <xliff:g id="COUNT_1">%1$s</xliff:g> 個印表機</item>
+ <item quantity="one">安裝即可使用 <xliff:g id="COUNT_0">%1$s</xliff:g> 個印表機</item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"正在列印 <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"正在取消 <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"印表機發生錯誤:<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"重新開始"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"尚未與印表機建立連線"</string>
<string name="reason_unknown" msgid="5507940196503246139">"不明"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – 無法使用"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"要使用「<xliff:g id="SERVICE">%1$s</xliff:g>」嗎?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"您的文件可能會透過一或多個伺服器輾轉傳送至印表機。"</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"很抱歉,無法執行這項操作。請再試一次。"</string>
<string name="print_error_retry" msgid="1426421728784259538">"重試"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"這台印表機目前無法使用。"</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"無法顯示預覽畫面"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"正在準備預覽…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-zu/strings.xml b/packages/PrintSpooler/res/values-zu/strings.xml
index 9cfcb33..121022b 100644
--- a/packages/PrintSpooler/res/values-zu/strings.xml
+++ b/packages/PrintSpooler/res/values-zu/strings.xml
@@ -72,6 +72,10 @@
<string name="recommended_services_title" msgid="3799434882937956924">"Amasevisi anconyiwe"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Amasevisi akhutshaziwe"</string>
<string name="all_services_title" msgid="5578662754874906455">"Onke amasevisi"</string>
+ <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
+ <item quantity="one">Faka ukuze uthole amaphrinta we-<xliff:g id="COUNT_1">%1$s</xliff:g></item>
+ <item quantity="other">Faka ukuze uthole amaphrinta we-<xliff:g id="COUNT_1">%1$s</xliff:g></item>
+ </plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Iphrinta i-<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Ikhansela i-<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Iphutha lephrinta ye-<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -80,7 +84,6 @@
<string name="restart" msgid="2472034227037808749">"Qala kabusha"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Akukho ukuxhumana kuphrinta"</string>
<string name="reason_unknown" msgid="5507940196503246139">"akwaziwa"</string>
- <string name="printer_unavailable" msgid="2434170617003315690">"I-<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – ayitholakali"</string>
<string name="print_service_security_warning_title" msgid="2160752291246775320">"Sebenzisa i-<xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Idokhumenti yakho ingase idlule iseva eyodwa noma amaningi lapho iya kuphrinta."</string>
<string-array name="color_mode_labels">
@@ -100,5 +103,6 @@
<string name="print_error_default_message" msgid="8602678405502922346">"Uxolo, lokho akusebenzanga. Zama futhi."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Zama futhi"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Le phrinta ayitholakali khona manje."</string>
+ <string name="print_cannot_load_page" msgid="6179560924492912009">"Ayikwazi ukubonisa ukubuka kuqala"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Ilungiselela ukubuka kuqala…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml
index 2f24d2c..2836adb 100644
--- a/packages/PrintSpooler/res/values/strings.xml
+++ b/packages/PrintSpooler/res/values/strings.xml
@@ -185,6 +185,12 @@
<!-- Label for the list item that links to the list of all print services. [CHAR LIMIT=50] -->
<string name="all_services_title">All services</string>
+ <!-- Subtitle for a print service recommendation. [CHAR LIMIT=50] -->
+ <plurals name="print_services_recommendation_subtitle">
+ <item quantity="one">Install to discover <xliff:g id="count" example="1">%1$s</xliff:g> printer</item>
+ <item quantity="other">Install to discover <xliff:g id="count" example="2">%1$s</xliff:g> printers</item>
+ </plurals>
+
<!-- Notifications -->
<!-- Template for the notification label for a printing print job. [CHAR LIMIT=25] -->
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
index 0210693..cd1d540 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
@@ -208,7 +208,7 @@
}
}
- CharSequence status = printJob.getStatus();
+ CharSequence status = printJob.getStatus(mContext.getPackageManager());
if (status != null) {
builder.setContentText(status);
} else {
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
index 18160ff..9b1ab0e 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
@@ -19,6 +19,7 @@
import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.StringRes;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
@@ -575,19 +576,35 @@
}
}
- /**
- * Set the status for a print job.
- *
- * @param printJobId ID of the print job to update
- * @param status the new status
- */
- public void setStatus(@NonNull PrintJobId printJobId, @Nullable CharSequence status) {
- synchronized (mLock) {
- getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY).setStatus(status);
+ /**
+ * Set the status for a print job.
+ *
+ * @param printJobId ID of the print job to update
+ * @param status the new status
+ */
+ public void setStatus(@NonNull PrintJobId printJobId, @Nullable CharSequence status) {
+ synchronized (mLock) {
+ getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY).setStatus(status);
- mNotificationController.onUpdateNotifications(mPrintJobs);
- }
- }
+ mNotificationController.onUpdateNotifications(mPrintJobs);
+ }
+ }
+
+ /**
+ * Set the status for a print job.
+ *
+ * @param printJobId ID of the print job to update
+ * @param status the new status as a string resource
+ * @param appPackageName app package the resource belongs to
+ */
+ public void setStatus(@NonNull PrintJobId printJobId, @StringRes int status,
+ @Nullable CharSequence appPackageName) {
+ synchronized (mLock) {
+ getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY).setStatus(status, appPackageName);
+
+ mNotificationController.onUpdateNotifications(mPrintJobs);
+ }
+ }
public boolean hasActivePrintJobsLocked() {
final int printJobCount = mPrintJobs.size();
@@ -884,7 +901,7 @@
serializer.attribute(null, ATTR_PROGRESS, String.valueOf(progress));
}
- CharSequence status = printJob.getStatus();
+ CharSequence status = printJob.getStatus(getPackageManager());
if (!TextUtils.isEmpty(status)) {
serializer.attribute(null, ATTR_STATUS, status.toString());
}
@@ -1041,7 +1058,9 @@
try {
in = mStatePersistFile.openRead();
} catch (FileNotFoundException e) {
- Log.i(LOG_TAG, "No existing print spooler state.");
+ if (DEBUG_PERSISTENCE) {
+ Log.d(LOG_TAG, "No existing print spooler state.");
+ }
return;
}
try {
@@ -1433,6 +1452,13 @@
PrintSpoolerService.this.setStatus(printJobId, status);
}
+ @Override
+ public void setStatusRes(@NonNull PrintJobId printJobId, @StringRes int status,
+ @NonNull CharSequence appPackageName) throws RemoteException {
+ PrintSpoolerService.this.setStatus(printJobId, status, appPackageName);
+ }
+
+
public PrintSpoolerService getService() {
return PrintSpoolerService.this;
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
index 2d3935b..99145b7b 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
@@ -178,6 +178,8 @@
}
if (mState == STATE_FAILED) {
Log.w(LOG_TAG, "Failed before start.");
+ } else if (mState == STATE_DESTROYED) {
+ Log.w(LOG_TAG, "Destroyed before start.");
} else {
if (mState != STATE_INITIAL) {
throw new IllegalStateException("Cannot start in state:" + stateToString(mState));
@@ -267,7 +269,7 @@
}
if (mState != STATE_STARTED && mState != STATE_UPDATED
&& mState != STATE_FAILED && mState != STATE_CANCELING
- && mState != STATE_CANCELED) {
+ && mState != STATE_CANCELED && mState != STATE_DESTROYED) {
throw new IllegalStateException("Cannot finish in state:"
+ stateToString(mState));
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/renderer/PdfManipulationService.java b/packages/PrintSpooler/src/com/android/printspooler/renderer/PdfManipulationService.java
index af4c347..0feda92 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/renderer/PdfManipulationService.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/renderer/PdfManipulationService.java
@@ -244,9 +244,20 @@
ranges = PageRangeUtils.normalize(ranges);
+ int lastPageIdx = mEditor.getPageCount() - 1;
+
final int rangeCount = ranges.length;
for (int i = rangeCount - 1; i >= 0; i--) {
PageRange range = ranges[i];
+
+ // Ignore removal of pages that are outside the document
+ if (range.getEnd() > lastPageIdx) {
+ if (range.getStart() > lastPageIdx) {
+ continue;
+ }
+ range = new PageRange(range.getStart(), lastPageIdx);
+ }
+
for (int j = range.getEnd(); j >= range.getStart(); j--) {
mEditor.removePage(j);
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/AddPrinterActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/AddPrinterActivity.java
index f2b3e6e..42ef10e 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/AddPrinterActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/AddPrinterActivity.java
@@ -30,10 +30,13 @@
import android.net.Uri;
import android.os.Bundle;
import android.print.PrintManager;
+import android.printservice.recommendation.RecommendationInfo;
+import android.print.PrintServiceRecommendationsLoader;
import android.print.PrintServicesLoader;
import android.printservice.PrintServiceInfo;
import android.provider.Settings;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
import android.view.View;
@@ -45,8 +48,10 @@
import android.widget.TextView;
import com.android.printspooler.R;
+import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
/**
@@ -57,31 +62,38 @@
* when the item is clicked.</li>
* <li>{@link #mDisabledServicesAdapter} for all disabled services. Once clicked the settings page
* for this service is opened.</li>
- * <li>{@link RecommendedServicesAdapter} for a link to all services. If this item is clicked
+ * <li>{@link #mRecommendedServicesAdapter} for a link to all services. If this item is clicked
* the market app is opened to show all print services.</li>
* </ul>
*/
-public class AddPrinterActivity extends ListActivity implements
- LoaderManager.LoaderCallbacks<List<PrintServiceInfo>>,
- AdapterView.OnItemClickListener {
+public class AddPrinterActivity extends ListActivity implements AdapterView.OnItemClickListener {
private static final String LOG_TAG = "AddPrinterActivity";
/** Ids for the loaders */
private static final int LOADER_ID_ENABLED_SERVICES = 1;
private static final int LOADER_ID_DISABLED_SERVICES = 2;
+ private static final int LOADER_ID_RECOMMENDED_SERVICES = 3;
+ private static final int LOADER_ID_ALL_SERVICES = 4;
/**
* The enabled services list. This is filled from the {@link #LOADER_ID_ENABLED_SERVICES}
- * loader in {@link #onLoadFinished}.
+ * loader in {@link PrintServiceInfoLoaderCallbacks#onLoadFinished}.
*/
private EnabledServicesAdapter mEnabledServicesAdapter;
/**
* The disabled services list. This is filled from the {@link #LOADER_ID_DISABLED_SERVICES}
- * loader in {@link #onLoadFinished}.
+ * loader in {@link PrintServiceInfoLoaderCallbacks#onLoadFinished}.
*/
private DisabledServicesAdapter mDisabledServicesAdapter;
+ /**
+ * The recommended services list. This is filled from the
+ * {@link #LOADER_ID_RECOMMENDED_SERVICES} loader in
+ * {@link PrintServicePrintServiceRecommendationLoaderCallbacks#onLoadFinished}.
+ */
+ private RecommendedServicesAdapter mRecommendedServicesAdapter;
+
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -90,36 +102,116 @@
mEnabledServicesAdapter = new EnabledServicesAdapter();
mDisabledServicesAdapter = new DisabledServicesAdapter();
+ mRecommendedServicesAdapter = new RecommendedServicesAdapter();
ArrayList<ActionAdapter> adapterList = new ArrayList<>(3);
adapterList.add(mEnabledServicesAdapter);
- adapterList.add(new RecommendedServicesAdapter());
+ adapterList.add(mRecommendedServicesAdapter);
adapterList.add(mDisabledServicesAdapter);
setListAdapter(new CombinedAdapter(adapterList));
getListView().setOnItemClickListener(this);
- getLoaderManager().initLoader(LOADER_ID_ENABLED_SERVICES, null, this);
- getLoaderManager().initLoader(LOADER_ID_DISABLED_SERVICES, null, this);
- // TODO: Load recommended services
+ PrintServiceInfoLoaderCallbacks printServiceLoaderCallbacks =
+ new PrintServiceInfoLoaderCallbacks();
+
+ getLoaderManager().initLoader(LOADER_ID_ENABLED_SERVICES, null, printServiceLoaderCallbacks);
+ getLoaderManager().initLoader(LOADER_ID_DISABLED_SERVICES, null, printServiceLoaderCallbacks);
+ getLoaderManager().initLoader(LOADER_ID_RECOMMENDED_SERVICES, null,
+ new PrintServicePrintServiceRecommendationLoaderCallbacks());
+ getLoaderManager().initLoader(LOADER_ID_ALL_SERVICES, null, printServiceLoaderCallbacks);
}
- @Override
- public Loader<List<PrintServiceInfo>> onCreateLoader(int id, Bundle args) {
- switch (id) {
- case LOADER_ID_ENABLED_SERVICES:
- return new PrintServicesLoader(
- (PrintManager) getSystemService(Context.PRINT_SERVICE), this,
- PrintManager.ENABLED_SERVICES);
- case LOADER_ID_DISABLED_SERVICES:
- return new PrintServicesLoader(
- (PrintManager) getSystemService(Context.PRINT_SERVICE), this,
- PrintManager.DISABLED_SERVICES);
- // TODO: Load recommended services
- default:
- // not reached
- return null;
+ /**
+ * Callbacks for the loaders operating on list of {@link PrintServiceInfo print service infos}.
+ */
+ private class PrintServiceInfoLoaderCallbacks implements
+ LoaderManager.LoaderCallbacks<List<PrintServiceInfo>> {
+ @Override
+ public Loader<List<PrintServiceInfo>> onCreateLoader(int id, Bundle args) {
+ switch (id) {
+ case LOADER_ID_ENABLED_SERVICES:
+ return new PrintServicesLoader(
+ (PrintManager) getSystemService(Context.PRINT_SERVICE),
+ AddPrinterActivity.this, PrintManager.ENABLED_SERVICES);
+ case LOADER_ID_DISABLED_SERVICES:
+ return new PrintServicesLoader(
+ (PrintManager) getSystemService(Context.PRINT_SERVICE),
+ AddPrinterActivity.this, PrintManager.DISABLED_SERVICES);
+ case LOADER_ID_ALL_SERVICES:
+ return new PrintServicesLoader(
+ (PrintManager) getSystemService(Context.PRINT_SERVICE),
+ AddPrinterActivity.this, PrintManager.ALL_SERVICES);
+ default:
+ // not reached
+ return null;
+ }
+ }
+
+
+ @Override
+ public void onLoadFinished(Loader<List<PrintServiceInfo>> loader,
+ List<PrintServiceInfo> data) {
+ switch (loader.getId()) {
+ case LOADER_ID_ENABLED_SERVICES:
+ mEnabledServicesAdapter.updateData(data);
+ break;
+ case LOADER_ID_DISABLED_SERVICES:
+ mDisabledServicesAdapter.updateData(data);
+ break;
+ case LOADER_ID_ALL_SERVICES:
+ mRecommendedServicesAdapter.updateInstalledServices(data);
+ default:
+ // not reached
+ }
+ }
+
+ @Override
+ public void onLoaderReset(Loader<List<PrintServiceInfo>> loader) {
+ if (!isFinishing()) {
+ switch (loader.getId()) {
+ case LOADER_ID_ENABLED_SERVICES:
+ mEnabledServicesAdapter.updateData(null);
+ break;
+ case LOADER_ID_DISABLED_SERVICES:
+ mDisabledServicesAdapter.updateData(null);
+ break;
+ case LOADER_ID_ALL_SERVICES:
+ mRecommendedServicesAdapter.updateInstalledServices(null);
+ break;
+ default:
+ // not reached
+ }
+ }
+ }
+ }
+
+ /**
+ * Callbacks for the loaders operating on list of {@link RecommendationInfo print service
+ * recommendations}.
+ */
+ private class PrintServicePrintServiceRecommendationLoaderCallbacks implements
+ LoaderManager.LoaderCallbacks<List<RecommendationInfo>> {
+ @Override
+ public Loader<List<RecommendationInfo>> onCreateLoader(int id, Bundle args) {
+ return new PrintServiceRecommendationsLoader(
+ (PrintManager) getSystemService(Context.PRINT_SERVICE),
+ AddPrinterActivity.this);
+ }
+
+
+ @Override
+ public void onLoadFinished(Loader<List<RecommendationInfo>> loader,
+ List<RecommendationInfo> data) {
+ mRecommendedServicesAdapter.updateRecommendations(data);
+ }
+
+ @Override
+ public void onLoaderReset(Loader<List<RecommendationInfo>> loader) {
+ if (!isFinishing()) {
+ mRecommendedServicesAdapter.updateRecommendations(null);
+ }
}
}
@@ -128,39 +220,6 @@
((ActionAdapter) getListAdapter()).performAction(position);
}
- @Override
- public void onLoadFinished(Loader<List<PrintServiceInfo>> loader,
- List<PrintServiceInfo> data) {
- switch (loader.getId()) {
- case LOADER_ID_ENABLED_SERVICES:
- mEnabledServicesAdapter.updateData(data);
- break;
- case LOADER_ID_DISABLED_SERVICES:
- mDisabledServicesAdapter.updateData(data);
- break;
- // TODO: Load recommended services
- default:
- // not reached
- }
- }
-
- @Override
- public void onLoaderReset(Loader<List<PrintServiceInfo>> loader) {
- if (!isFinishing()) {
- switch (loader.getId()) {
- case LOADER_ID_ENABLED_SERVICES:
- mEnabledServicesAdapter.updateData(null);
- break;
- case LOADER_ID_DISABLED_SERVICES:
- mDisabledServicesAdapter.updateData(null);
- break;
- // TODO: Reset recommended services
- default:
- // not reached
- }
- }
- }
-
/**
* Marks an adapter that can can perform an action for a position in it's list.
*/
@@ -490,28 +549,65 @@
* Adapter for the recommended services.
*/
private class RecommendedServicesAdapter extends ActionAdapter {
+ /** Package names of all installed print services */
+ private @NonNull final ArraySet<String> mInstalledServices;
+
+ /** All print service recommendations */
+ private @Nullable List<RecommendationInfo> mRecommendations;
+
+ /**
+ * Sorted print service recommendations for services that are not installed
+ *
+ * @see #filterRecommendations
+ */
+ private @Nullable List<RecommendationInfo> mFilteredRecommendations;
+
+ /**
+ * Create a new adapter.
+ */
+ private RecommendedServicesAdapter() {
+ mInstalledServices = new ArraySet<>();
+ }
+
@Override
public int getCount() {
- return 2;
+ if (mFilteredRecommendations == null) {
+ return 2;
+ } else {
+ return mFilteredRecommendations.size() + 2;
+ }
}
@Override
public int getViewTypeCount() {
- return 2;
+ return 3;
+ }
+
+ /**
+ * @return The position the all services link is at.
+ */
+ private int getAllServicesPos() {
+ return getCount() - 1;
}
@Override
public int getItemViewType(int position) {
if (position == 0) {
return 0;
- } else {
+ } else if (getAllServicesPos() == position) {
return 1;
+ } else {
+ return 2;
}
}
@Override
public Object getItem(int position) {
- return null;
+ if (position == 0 || position == getAllServicesPos()) {
+ return null;
+ } else {
+ return mFilteredRecommendations.get(position - 1);
+ }
}
@Override
@@ -531,11 +627,27 @@
.setText(R.string.recommended_services_title);
return convertView;
- }
+ } else if (position == getAllServicesPos()) {
+ if (convertView == null) {
+ convertView = getLayoutInflater().inflate(R.layout.all_print_services_list_item,
+ parent, false);
+ }
+ } else {
+ RecommendationInfo recommendation = (RecommendationInfo) getItem(position);
- if (convertView == null) {
- convertView = getLayoutInflater().inflate(R.layout.all_print_services_list_item,
- parent, false);
+ if (convertView == null) {
+ convertView = getLayoutInflater().inflate(
+ R.layout.print_service_recommendations_list_item, parent, false);
+ }
+
+ ((TextView) convertView.findViewById(R.id.title)).setText(recommendation.getName());
+
+ ((TextView) convertView.findViewById(R.id.subtitle)).setText(getResources()
+ .getQuantityString(R.plurals.print_services_recommendation_subtitle,
+ recommendation.getNumDiscoveredPrinters(),
+ recommendation.getNumDiscoveredPrinters()));
+
+ return convertView;
}
return convertView;
@@ -548,16 +660,107 @@
@Override
public void performAction(@IntRange(from = 0) int position) {
- String searchUri = Settings.Secure
- .getString(getContentResolver(), Settings.Secure.PRINT_SERVICE_SEARCH_URI);
+ if (position == getAllServicesPos()) {
+ String searchUri = Settings.Secure
+ .getString(getContentResolver(), Settings.Secure.PRINT_SERVICE_SEARCH_URI);
- if (searchUri != null) {
+ if (searchUri != null) {
+ try {
+ startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(searchUri)));
+ } catch (ActivityNotFoundException e) {
+ Log.e(LOG_TAG, "Cannot start market", e);
+ }
+ }
+ } else {
+ RecommendationInfo recommendation = (RecommendationInfo) getItem(position);
+
try {
- startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(searchUri)));
+ startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(getString(
+ R.string.uri_package_details, recommendation.getPackageName()))));
} catch (ActivityNotFoundException e) {
Log.e(LOG_TAG, "Cannot start market", e);
}
}
}
+
+ /**
+ * Filter recommended services.
+ */
+ private void filterRecommendations() {
+ if (mRecommendations == null) {
+ mFilteredRecommendations = null;
+ } else {
+ mFilteredRecommendations = new ArrayList<>();
+
+ // Filter out recommendations for already installed services
+ final int numRecommendations = mRecommendations.size();
+ for (int i = 0; i < numRecommendations; i++) {
+ RecommendationInfo recommendation = mRecommendations.get(i);
+
+ if (!mInstalledServices.contains(recommendation.getPackageName())) {
+ mFilteredRecommendations.add(recommendation);
+ }
+ }
+ }
+
+ notifyDataSetChanged();
+ }
+
+ /**
+ * Update the installed print services.
+ *
+ * @param services The new set of services
+ */
+ public void updateInstalledServices(List<PrintServiceInfo> services) {
+ mInstalledServices.clear();
+
+ final int numServices = services.size();
+ for (int i = 0; i < numServices; i++) {
+ mInstalledServices.add(services.get(i).getComponentName().getPackageName());
+ }
+
+ filterRecommendations();
+ }
+
+ /**
+ * Update the recommended print services.
+ *
+ * @param recommendations The new set of recommendations
+ */
+ public void updateRecommendations(List<RecommendationInfo> recommendations) {
+ if (recommendations != null) {
+ final Collator collator = Collator.getInstance();
+
+ // Sort recommendations (early conditions are more important)
+ // - higher number of discovered printers first
+ // - single vendor services first
+ // - alphabetically
+ Collections.sort(recommendations,
+ new Comparator<RecommendationInfo>() {
+ @Override public int compare(RecommendationInfo o1,
+ RecommendationInfo o2) {
+ if (o1.getNumDiscoveredPrinters() !=
+ o2.getNumDiscoveredPrinters()) {
+ return o2.getNumDiscoveredPrinters() -
+ o1.getNumDiscoveredPrinters();
+ } else if (o1.recommendsMultiVendorService()
+ != o2.recommendsMultiVendorService()) {
+ if (o1.recommendsMultiVendorService()) {
+ return 1;
+ } else {
+ return -1;
+ }
+ } else {
+ return collator.compare(o1.getName().toString(),
+ o2.getName().toString());
+ }
+ }
+ });
+ }
+
+ mRecommendations = recommendations;
+
+ filterRecommendations();
+ }
}
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
index 3b5513a..7935440 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
@@ -267,9 +267,12 @@
// The contract is that if we already have a valid,
// result the we have to deliver it immediately.
- if (!mPrinters.isEmpty()) {
- deliverResult(new ArrayList<>(mPrinters));
- }
+ (new Handler(Looper.getMainLooper())).post(new Runnable() {
+ @Override public void run() {
+ deliverResult(new ArrayList<>(mPrinters));
+ }
+ });
+
// Always load the data to ensure discovery period is
// started and to make sure obsolete printers are updated.
onForceLoad();
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
index eebb60c..c1a3f86 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
@@ -206,6 +206,7 @@
int documentPageCount, MediaSize mediaSize, Margins minMargins) {
boolean documentChanged = false;
boolean updatePreviewAreaAndPageSize = false;
+ boolean clearSelectedPages = false;
// If the app does not tell how many pages are in the document we cannot
// optimize and ask for all pages whose count we get from the renderer.
@@ -225,30 +226,41 @@
}
}
- if (!Arrays.equals(mSelectedPages, selectedPages)) {
- mSelectedPages = selectedPages;
- mSelectedPageCount = PageRangeUtils.getNormalizedPageCount(
- mSelectedPages, documentPageCount);
- setConfirmedPages(mSelectedPages, documentPageCount);
- updatePreviewAreaAndPageSize = true;
- documentChanged = true;
- }
-
if (mDocumentPageCount != documentPageCount) {
mDocumentPageCount = documentPageCount;
documentChanged = true;
+ clearSelectedPages = true;
}
if (mMediaSize == null || !mMediaSize.equals(mediaSize)) {
mMediaSize = mediaSize;
updatePreviewAreaAndPageSize = true;
documentChanged = true;
+
+ clearSelectedPages = true;
}
if (mMinMargins == null || !mMinMargins.equals(minMargins)) {
mMinMargins = minMargins;
updatePreviewAreaAndPageSize = true;
documentChanged = true;
+
+ clearSelectedPages = true;
+ }
+
+ if (clearSelectedPages) {
+ mSelectedPages = PageRange.ALL_PAGES_ARRAY;
+ mSelectedPageCount = documentPageCount;
+ setConfirmedPages(mSelectedPages, documentPageCount);
+ updatePreviewAreaAndPageSize = true;
+ documentChanged = true;
+ } else if (!Arrays.equals(mSelectedPages, selectedPages)) {
+ mSelectedPages = selectedPages;
+ mSelectedPageCount = PageRangeUtils.getNormalizedPageCount(
+ mSelectedPages, documentPageCount);
+ setConfirmedPages(mSelectedPages, documentPageCount);
+ updatePreviewAreaAndPageSize = true;
+ documentChanged = true;
}
// If *all pages* is selected we need to convert that to absolute
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index bb34fcf..e7aebdd 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -62,7 +62,6 @@
import android.provider.DocumentsContract;
import android.text.Editable;
import android.text.TextUtils;
-import android.text.TextUtils.SimpleStringSplitter;
import android.text.TextWatcher;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -117,8 +116,6 @@
import java.util.Collections;
import java.util.List;
import java.util.Objects;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
public class PrintActivity extends Activity implements RemotePrintDocument.UpdateResultCallbacks,
PrintErrorFragment.OnActionListener, PageAdapter.ContentCallbacks,
@@ -128,8 +125,6 @@
private static final boolean DEBUG = false;
- public static final String INTENT_EXTRA_PRINTER_ID = "INTENT_EXTRA_PRINTER_ID";
-
private static final String FRAGMENT_TAG = "FRAGMENT_TAG";
private static final String HAS_PRINTED_PREF = "has_printed";
@@ -167,24 +162,11 @@
private static final int MIN_COPIES = 1;
private static final String MIN_COPIES_STRING = String.valueOf(MIN_COPIES);
- private static final Pattern PATTERN_DIGITS = Pattern.compile("[\\d]+");
-
- private static final Pattern PATTERN_ESCAPE_SPECIAL_CHARS = Pattern.compile(
- "(?=[]\\[+&|!(){}^\"~*?:\\\\])");
-
- private static final Pattern PATTERN_PAGE_RANGE = Pattern.compile(
- "[\\s]*[0-9]+[\\-]?[\\s]*[0-9]*[\\s]*?(([,])"
- + "[\\s]*[0-9]+[\\s]*[\\-]?[\\s]*[0-9]*[\\s]*|[\\s]*)+");
-
- public static final PageRange[] ALL_PAGES_ARRAY = new PageRange[]{PageRange.ALL_PAGES};
-
private boolean mIsOptionsUiBound = false;
private final PrinterAvailabilityDetector mPrinterAvailabilityDetector =
new PrinterAvailabilityDetector();
- private final SimpleStringSplitter mStringCommaSplitter = new SimpleStringSplitter(',');
-
private final OnFocusChangeListener mSelectAllOnFocusListener = new SelectAllOnFocusListener();
private PrintSpoolerProvider mSpoolerProvider;
@@ -480,7 +462,7 @@
}
private void onPrintDocumentError(String message) {
- mProgressMessageController.cancel();
+ setState(mProgressMessageController.cancel());
ensureErrorUiShown(null, PrintErrorFragment.ACTION_RETRY);
setState(STATE_UPDATE_FAILED);
@@ -506,7 +488,7 @@
Log.i(LOG_TAG, "onUpdateCanceled()");
}
- mProgressMessageController.cancel();
+ setState(mProgressMessageController.cancel());
ensurePreviewUiShown();
switch (mState) {
@@ -528,7 +510,7 @@
Log.i(LOG_TAG, "onUpdateCompleted()");
}
- mProgressMessageController.cancel();
+ setState(mProgressMessageController.cancel());
ensurePreviewUiShown();
// Update the print job with the info for the written document. The page
@@ -576,7 +558,7 @@
Log.i(LOG_TAG, "onUpdateFailed()");
}
- mProgressMessageController.cancel();
+ setState(mProgressMessageController.cancel());
ensureErrorUiShown(error, PrintErrorFragment.ACTION_RETRY);
if (mState == STATE_CREATE_FILE_FAILED
@@ -597,14 +579,6 @@
@Override
public void onOptionsClosed() {
- PageRange[] selectedPages = computeSelectedPages();
- if (!Arrays.equals(mSelectedPages, selectedPages)) {
- mSelectedPages = selectedPages;
-
- // Update preview.
- updatePrintPreviewController(false);
- }
-
// Make sure the IME is not on the way of preview as
// the user may have used it to type copies or range.
InputMethodManager imm = getSystemService(InputMethodManager.class);
@@ -717,21 +691,17 @@
private void onSelectPrinterActivityResult(int resultCode, Intent data) {
if (resultCode == RESULT_OK && data != null) {
- PrinterId printerId = data.getParcelableExtra(INTENT_EXTRA_PRINTER_ID);
- if (printerId != null) {
- mDestinationSpinnerAdapter.ensurePrinterInVisibleAdapterPosition(printerId);
- final int index = mDestinationSpinnerAdapter.getPrinterIndex(printerId);
- if (index != AdapterView.INVALID_POSITION) {
- mDestinationSpinner.setSelection(index);
- return;
- }
+ PrinterInfo printerInfo = data.getParcelableExtra(
+ SelectPrinterActivity.INTENT_EXTRA_PRINTER);
+ if (printerInfo != null) {
+ mCurrentPrinter = printerInfo;
+ mDestinationSpinnerAdapter.ensurePrinterInVisibleAdapterPosition(printerInfo);
}
}
if (mCurrentPrinter != null) {
- PrinterId printerId = mCurrentPrinter.getId();
- final int index = mDestinationSpinnerAdapter.getPrinterIndex(printerId);
- mDestinationSpinner.setSelection(index);
+ // Trigger PrintersObserver.onChanged() to adjust selection back to current printer
+ mDestinationSpinnerAdapter.notifyDataSetChanged();
}
}
@@ -950,7 +920,7 @@
mSelectedPages = selectedPages;
mPrintJob.setPages(selectedPages);
- if (Arrays.equals(selectedPages, ALL_PAGES_ARRAY)) {
+ if (Arrays.equals(selectedPages, PageRange.ALL_PAGES_ARRAY)) {
if (mRangeOptionsSpinner.getSelectedItemPosition() != 0) {
mRangeOptionsSpinner.setSelection(0);
mPageRangeEditText.setText("");
@@ -1049,7 +1019,22 @@
}
}
+ /**
+ * Clear the selected page range and update the preview if needed.
+ */
+ private void clearPageRanges() {
+ mRangeOptionsSpinner.setSelection(0);
+ mPageRangeEditText.setError(null);
+ mPageRangeEditText.setText("");
+ mSelectedPages = PageRange.ALL_PAGES_ARRAY;
+
+ if (!Arrays.equals(mSelectedPages, mPrintPreviewController.getSelectedPages())) {
+ updatePrintPreviewController(false);
+ }
+ }
+
private void updatePrintAttributesFromCapabilities(PrinterCapabilitiesInfo capabilities) {
+ boolean clearRanges = false;
PrintAttributes defaults = capabilities.getDefaults();
// Sort the media sizes based on the current locale.
@@ -1061,6 +1046,7 @@
// Media size.
MediaSize currMediaSize = attributes.getMediaSize();
if (currMediaSize == null) {
+ clearRanges = true;
attributes.setMediaSize(defaults.getMediaSize());
} else {
MediaSize newMediaSize = null;
@@ -1079,6 +1065,7 @@
}
// If we did not find the current media size fall back to default.
if (newMediaSize == null) {
+ clearRanges = true;
newMediaSize = defaults.getMediaSize();
}
@@ -1110,7 +1097,14 @@
}
// Margins.
+ if (!Objects.equals(attributes.getMinMargins(), defaults.getMinMargins())) {
+ clearRanges = true;
+ }
attributes.setMinMargins(defaults.getMinMargins());
+
+ if (clearRanges) {
+ clearPageRanges();
+ }
}
private boolean updateDocument(boolean clearLastError) {
@@ -1161,6 +1155,18 @@
doFinish();
}
+ /**
+ * Update the selected pages from the text field.
+ */
+ private void updateSelectedPagesFromTextField() {
+ PageRange[] selectedPages = computeSelectedPages();
+ if (!Arrays.equals(mSelectedPages, selectedPages)) {
+ mSelectedPages = selectedPages;
+ // Update preview.
+ updatePrintPreviewController(false);
+ }
+ }
+
private void confirmPrint() {
setState(STATE_PRINT_CONFIRMED);
@@ -1170,14 +1176,10 @@
addCurrentPrinterToHistory();
setUserPrinted();
- PageRange[] selectedPages = computeSelectedPages();
- if (!Arrays.equals(mSelectedPages, selectedPages)) {
- mSelectedPages = selectedPages;
- // Update preview.
- updatePrintPreviewController(false);
- }
-
+ // updateSelectedPagesFromTextField migth update the preview, hence apply the preview first
updateSelectedPagesFromPreview();
+ updateSelectedPagesFromTextField();
+
mPrintPreviewController.closeOptions();
if (canUpdateDocument()) {
@@ -1262,6 +1264,7 @@
// Page range
mPageRangeTitle = (TextView) findViewById(R.id.page_range_title);
mPageRangeEditText = (EditText) findViewById(R.id.page_range_edittext);
+ mPageRangeEditText.setVisibility(View.INVISIBLE);
mPageRangeEditText.setOnFocusChangeListener(mSelectAllOnFocusListener);
mPageRangeEditText.addTextChangedListener(new RangeTextWatcher());
@@ -1476,6 +1479,12 @@
cancelPrint();
}
} else if (view == mMoreOptionsButton) {
+ if (mPageRangeEditText.getError() == null) {
+ // The selected pages is only applied once the user leaves the text field. A click
+ // on this button, does not count as leaving.
+ updateSelectedPagesFromTextField();
+ }
+
if (mCurrentPrinter != null) {
startAdvancedPrintOptionsActivity(mCurrentPrinter);
}
@@ -1753,36 +1762,38 @@
// Range options
PrintDocumentInfo info = mPrintedDocument.getDocumentInfo().info;
final int pageCount = getAdjustedPageCount(info);
- if (info != null && pageCount > 0) {
- if (pageCount == 1) {
- mRangeOptionsSpinner.setEnabled(false);
- } else {
- mRangeOptionsSpinner.setEnabled(true);
- if (mRangeOptionsSpinner.getSelectedItemPosition() > 0) {
- if (!mPageRangeEditText.isEnabled()) {
- mPageRangeEditText.setEnabled(true);
- mPageRangeEditText.setVisibility(View.VISIBLE);
- mPageRangeTitle.setVisibility(View.VISIBLE);
- mPageRangeEditText.requestFocus();
- InputMethodManager imm = (InputMethodManager)
- getSystemService(Context.INPUT_METHOD_SERVICE);
- imm.showSoftInput(mPageRangeEditText, 0);
- }
+ if (pageCount > 0) {
+ if (info != null) {
+ if (pageCount == 1) {
+ mRangeOptionsSpinner.setEnabled(false);
} else {
- mPageRangeEditText.setEnabled(false);
- mPageRangeEditText.setVisibility(View.INVISIBLE);
- mPageRangeTitle.setVisibility(View.INVISIBLE);
+ mRangeOptionsSpinner.setEnabled(true);
+ if (mRangeOptionsSpinner.getSelectedItemPosition() > 0) {
+ if (!mPageRangeEditText.isEnabled()) {
+ mPageRangeEditText.setEnabled(true);
+ mPageRangeEditText.setVisibility(View.VISIBLE);
+ mPageRangeTitle.setVisibility(View.VISIBLE);
+ mPageRangeEditText.requestFocus();
+ InputMethodManager imm = (InputMethodManager)
+ getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.showSoftInput(mPageRangeEditText, 0);
+ }
+ } else {
+ mPageRangeEditText.setEnabled(false);
+ mPageRangeEditText.setVisibility(View.INVISIBLE);
+ mPageRangeTitle.setVisibility(View.INVISIBLE);
+ }
}
+ } else {
+ if (mRangeOptionsSpinner.getSelectedItemPosition() != 0) {
+ mRangeOptionsSpinner.setSelection(0);
+ mPageRangeEditText.setText("");
+ }
+ mRangeOptionsSpinner.setEnabled(false);
+ mPageRangeEditText.setEnabled(false);
+ mPageRangeEditText.setVisibility(View.INVISIBLE);
+ mPageRangeTitle.setVisibility(View.INVISIBLE);
}
- } else {
- if (mRangeOptionsSpinner.getSelectedItemPosition() != 0) {
- mRangeOptionsSpinner.setSelection(0);
- mPageRangeEditText.setText("");
- }
- mRangeOptionsSpinner.setEnabled(false);
- mPageRangeEditText.setEnabled(false);
- mPageRangeEditText.setVisibility(View.INVISIBLE);
- mPageRangeTitle.setVisibility(View.INVISIBLE);
}
final int newPageCount = getAdjustedPageCount(info);
@@ -1895,45 +1906,13 @@
}
if (mRangeOptionsSpinner.getSelectedItemPosition() > 0) {
- List<PageRange> pageRanges = new ArrayList<>();
- mStringCommaSplitter.setString(mPageRangeEditText.getText().toString());
+ PrintDocumentInfo info = mPrintedDocument.getDocumentInfo().info;
+ final int pageCount = (info != null) ? getAdjustedPageCount(info) : 0;
- while (mStringCommaSplitter.hasNext()) {
- String range = mStringCommaSplitter.next().trim();
- if (TextUtils.isEmpty(range)) {
- continue;
- }
- final int dashIndex = range.indexOf('-');
- final int fromIndex;
- final int toIndex;
-
- if (dashIndex > 0) {
- fromIndex = Integer.parseInt(range.substring(0, dashIndex).trim()) - 1;
- // It is possible that the dash is at the end since the input
- // verification can has to allow the user to keep entering if
- // this would lead to a valid input. So we handle this.
- if (dashIndex < range.length() - 1) {
- String fromString = range.substring(dashIndex + 1, range.length()).trim();
- toIndex = Integer.parseInt(fromString) - 1;
- } else {
- toIndex = fromIndex;
- }
- } else {
- fromIndex = toIndex = Integer.parseInt(range) - 1;
- }
-
- PageRange pageRange = new PageRange(Math.min(fromIndex, toIndex),
- Math.max(fromIndex, toIndex));
- pageRanges.add(pageRange);
- }
-
- PageRange[] pageRangesArray = new PageRange[pageRanges.size()];
- pageRanges.toArray(pageRangesArray);
-
- return PageRangeUtils.normalize(pageRangesArray);
+ return PageRangeUtils.parsePageRanges(mPageRangeEditText.getText(), pageCount);
}
- return ALL_PAGES_ARRAY;
+ return PageRange.ALL_PAGES_ARRAY;
}
private int getAdjustedPageCount(PrintDocumentInfo info) {
@@ -2051,9 +2030,13 @@
mDestinationSpinnerAdapter.unregisterDataSetObserver(mPrintersObserver);
}
- if (mState != STATE_INITIALIZING) {
- mProgressMessageController.cancel();
+ if (mSpoolerProvider != null) {
mSpoolerProvider.destroy();
+ }
+
+ setState(mProgressMessageController.cancel());
+
+ if (mState != STATE_INITIALIZING) {
mPrintedDocument.finish();
mPrintedDocument.destroy();
mPrintPreviewController.destroy(new Runnable() {
@@ -2226,23 +2209,36 @@
return AdapterView.INVALID_POSITION;
}
- public void ensurePrinterInVisibleAdapterPosition(PrinterId printerId) {
+ public void ensurePrinterInVisibleAdapterPosition(PrinterInfo printer) {
final int printerCount = mPrinterHolders.size();
+ boolean isKnownPrinter = false;
for (int i = 0; i < printerCount; i++) {
PrinterHolder printerHolder = mPrinterHolders.get(i);
- if (printerHolder.printer.getId().equals(printerId)) {
+
+ if (printerHolder.printer.getId().equals(printer.getId())) {
+ isKnownPrinter = true;
+
// If already in the list - do nothing.
if (i < getCount() - 2) {
- return;
+ break;
}
// Else replace the last one (two items are not printers).
final int lastPrinterIndex = getCount() - 3;
mPrinterHolders.set(i, mPrinterHolders.get(lastPrinterIndex));
mPrinterHolders.set(lastPrinterIndex, printerHolder);
- notifyDataSetChanged();
- return;
+ break;
}
}
+
+ if (!isKnownPrinter) {
+ PrinterHolder printerHolder = new PrinterHolder(printer);
+ printerHolder.removed = true;
+
+ mPrinterHolders.add(Math.max(0, getCount() - 3), printerHolder);
+ }
+
+ // Force reload to adjust selection in PrintersObserver.onChanged()
+ notifyDataSetChanged();
}
@Override
@@ -2425,8 +2421,7 @@
List<PrinterHolder> newPrinterHolders = new ArrayList<>();
// Update printers we already have which are either updated or removed.
- // We do not remove printers if the currently selected printer is removed
- // to prevent the user printing to a wrong printer.
+ // We do not remove the currently selected printer.
final int oldPrinterCount = mPrinterHolders.size();
for (int i = 0; i < oldPrinterCount; i++) {
PrinterHolder printerHolder = mPrinterHolders.get(i);
@@ -2435,10 +2430,11 @@
if (updatedPrinter != null) {
printerHolder.printer = updatedPrinter;
printerHolder.removed = false;
- } else {
+ newPrinterHolders.add(printerHolder);
+ } else if (mCurrentPrinter != null && mCurrentPrinter.getId().equals(oldPrinterId)){
printerHolder.removed = true;
+ newPrinterHolders.add(printerHolder);
}
- newPrinterHolders.add(printerHolder);
}
// Add the rest of the new printers, i.e. what is left.
@@ -2470,14 +2466,25 @@
return null;
}
- public void pruneRemovedPrinters() {
+ /**
+ * Remove a printer from the holders if it is marked as removed.
+ *
+ * @param printerId the id of the printer to remove.
+ *
+ * @return true iff the printer was removed.
+ */
+ public boolean pruneRemovedPrinter(PrinterId printerId) {
final int holderCounts = mPrinterHolders.size();
for (int i = holderCounts - 1; i >= 0; i--) {
PrinterHolder printerHolder = mPrinterHolders.get(i);
- if (printerHolder.removed) {
+
+ if (printerHolder.printer.getId().equals(printerId) && printerHolder.removed) {
mPrinterHolders.remove(i);
+ return true;
}
}
+
+ return false;
}
private void addPrinters(List<PrinterHolder> list, Collection<PrinterInfo> printers) {
@@ -2522,17 +2529,17 @@
PrinterHolder printerHolder = mDestinationSpinnerAdapter.getPrinterHolder(
oldPrinterState.getId());
- if (printerHolder == null) {
- return;
- }
PrinterInfo newPrinterState = printerHolder.printer;
- if (!printerHolder.removed) {
- mDestinationSpinnerAdapter.pruneRemovedPrinters();
- } else {
+ if (printerHolder.removed) {
onPrinterUnavailable(newPrinterState);
}
+ if (mDestinationSpinner.getSelectedItem() != printerHolder) {
+ mDestinationSpinner.setSelection(
+ mDestinationSpinnerAdapter.getPrinterIndex(newPrinterState.getId()));
+ }
+
if (oldPrinterState.equals(newPrinterState)) {
return;
}
@@ -2604,6 +2611,8 @@
private final class MyOnItemSelectedListener implements AdapterView.OnItemSelectedListener {
@Override
public void onItemSelected(AdapterView<?> spinner, View view, int position, long id) {
+ boolean clearRanges = false;
+
if (spinner == mDestinationSpinner) {
if (position == AdapterView.INVALID_POSITION) {
return;
@@ -2622,13 +2631,28 @@
return;
}
+ PrinterId oldId = null;
+ if (mCurrentPrinter != null) {
+ oldId = mCurrentPrinter.getId();
+ }
+
mCurrentPrinter = currentPrinter;
+ if (oldId != null) {
+ boolean printerRemoved = mDestinationSpinnerAdapter.pruneRemovedPrinter(oldId);
+
+ if (printerRemoved) {
+ // Trigger PrinterObserver.onChanged to adjust selection. This will call
+ // this function again.
+ mDestinationSpinnerAdapter.notifyDataSetChanged();
+ return;
+ }
+ }
+
PrinterHolder printerHolder = mDestinationSpinnerAdapter.getPrinterHolder(
currentPrinter.getId());
if (!printerHolder.removed) {
setState(STATE_CONFIGURING);
- mDestinationSpinnerAdapter.pruneRemovedPrinters();
ensurePreviewUiShown();
}
@@ -2650,10 +2674,17 @@
} else if (spinner == mMediaSizeSpinner) {
SpinnerItem<MediaSize> mediaItem = mMediaSizeSpinnerAdapter.getItem(position);
PrintAttributes attributes = mPrintJob.getAttributes();
+
+ MediaSize newMediaSize;
if (mOrientationSpinner.getSelectedItemPosition() == 0) {
- attributes.setMediaSize(mediaItem.value.asPortrait());
+ newMediaSize = mediaItem.value.asPortrait();
} else {
- attributes.setMediaSize(mediaItem.value.asLandscape());
+ newMediaSize = mediaItem.value.asLandscape();
+ }
+
+ if (newMediaSize != attributes.getMediaSize()) {
+ clearRanges = true;
+ attributes.setMediaSize(newMediaSize);
}
} else if (spinner == mColorModeSpinner) {
SpinnerItem<Integer> colorModeItem = mColorModeSpinnerAdapter.getItem(position);
@@ -2665,25 +2696,35 @@
SpinnerItem<Integer> orientationItem = mOrientationSpinnerAdapter.getItem(position);
PrintAttributes attributes = mPrintJob.getAttributes();
if (mMediaSizeSpinner.getSelectedItem() != null) {
- if (orientationItem.value == ORIENTATION_PORTRAIT) {
- attributes.copyFrom(attributes.asPortrait());
- } else {
- attributes.copyFrom(attributes.asLandscape());
+ boolean isPortrait = attributes.isPortrait();
+
+ if (isPortrait != (orientationItem.value == ORIENTATION_PORTRAIT)) {
+ clearRanges = true;
+ if (orientationItem.value == ORIENTATION_PORTRAIT) {
+ attributes.copyFrom(attributes.asPortrait());
+ } else {
+ attributes.copyFrom(attributes.asLandscape());
+ }
}
}
} else if (spinner == mRangeOptionsSpinner) {
if (mRangeOptionsSpinner.getSelectedItemPosition() == 0) {
+ clearRanges = true;
mPageRangeEditText.setText("");
} else if (TextUtils.isEmpty(mPageRangeEditText.getText())) {
mPageRangeEditText.setError("");
}
}
- if (canUpdateDocument()) {
- updateDocument(false);
+ if (clearRanges) {
+ clearPageRanges();
}
updateOptionsUi();
+
+ if (canUpdateDocument()) {
+ updateDocument(false);
+ }
}
@Override
@@ -2699,6 +2740,10 @@
if (!TextUtils.isEmpty(editText.getText())) {
editText.setSelection(editText.getText().length());
}
+
+ if (view == mPageRangeEditText && !hasFocus && mPageRangeEditText.getError() == null) {
+ updateSelectedPagesFromTextField();
+ }
}
}
@@ -2717,50 +2762,26 @@
public void afterTextChanged(Editable editable) {
final boolean hadErrors = hasErrors();
- String text = editable.toString();
-
- if (TextUtils.isEmpty(text)) {
- mPageRangeEditText.setError("");
- updateOptionsUi();
- return;
- }
-
- String escapedText = PATTERN_ESCAPE_SPECIAL_CHARS.matcher(text).replaceAll("////");
- if (!PATTERN_PAGE_RANGE.matcher(escapedText).matches()) {
- mPageRangeEditText.setError("");
- updateOptionsUi();
- return;
- }
-
PrintDocumentInfo info = mPrintedDocument.getDocumentInfo().info;
final int pageCount = (info != null) ? getAdjustedPageCount(info) : 0;
+ PageRange[] ranges = PageRangeUtils.parsePageRanges(editable, pageCount);
- // The range
- Matcher matcher = PATTERN_DIGITS.matcher(text);
- while (matcher.find()) {
- String numericString = text.substring(matcher.start(), matcher.end()).trim();
- if (TextUtils.isEmpty(numericString)) {
- continue;
- }
- final int pageIndex = Integer.parseInt(numericString);
- if (pageIndex < 1 || pageIndex > pageCount) {
+ if (ranges.length == 0) {
+ if (mPageRangeEditText.getError() == null) {
mPageRangeEditText.setError("");
updateOptionsUi();
- return;
}
+ return;
}
- // We intentionally do not catch the case of the from page being
- // greater than the to page. When computing the requested pages
- // we just swap them if necessary.
-
- mPageRangeEditText.setError(null);
- mPrintButton.setEnabled(true);
- updateOptionsUi();
-
- if (hadErrors && !hasErrors()) {
+ if (mPageRangeEditText.getError() != null) {
+ mPageRangeEditText.setError(null);
updateOptionsUi();
}
+
+ if (hadErrors && canUpdateDocument()) {
+ updateDocument(false);
+ }
}
}
@@ -2780,8 +2801,10 @@
final boolean hadErrors = hasErrors();
if (editable.length() == 0) {
- mCopiesEditText.setError("");
- updateOptionsUi();
+ if (mCopiesEditText.getError() == null) {
+ mCopiesEditText.setError("");
+ updateOptionsUi();
+ }
return;
}
@@ -2793,16 +2816,19 @@
}
if (copies < MIN_COPIES) {
- mCopiesEditText.setError("");
- updateOptionsUi();
+ if (mCopiesEditText.getError() == null) {
+ mCopiesEditText.setError("");
+ updateOptionsUi();
+ }
return;
}
mPrintJob.setCopies(copies);
- mCopiesEditText.setError(null);
-
- updateOptionsUi();
+ if (mCopiesEditText.getError() != null) {
+ mCopiesEditText.setError(null);
+ updateOptionsUi();
+ }
if (hadErrors && canUpdateDocument()) {
updateDocument(false);
@@ -2817,29 +2843,50 @@
private boolean mPosted;
+ /** State before run was executed */
+ private int mPreviousState = -1;
+
public ProgressMessageController(Context context) {
mHandler = new Handler(context.getMainLooper(), null, false);
}
public void post() {
- if (mPosted) {
+ if (mState == STATE_UPDATE_SLOW) {
+ setState(STATE_UPDATE_SLOW);
+ ensureProgressUiShown();
+ updateOptionsUi();
+
+ return;
+ } else if (mPosted) {
return;
}
+ mPreviousState = -1;
mPosted = true;
mHandler.postDelayed(this, PROGRESS_TIMEOUT_MILLIS);
}
- public void cancel() {
+ private int getStateAfterCancel() {
+ if (mPreviousState == -1) {
+ return mState;
+ } else {
+ return mPreviousState;
+ }
+ }
+
+ public int cancel() {
if (!mPosted) {
- return;
+ return getStateAfterCancel();
}
mPosted = false;
mHandler.removeCallbacks(this);
+
+ return getStateAfterCancel();
}
@Override
public void run() {
mPosted = false;
+ mPreviousState = mState;
setState(STATE_UPDATE_SLOW);
ensureProgressUiShown();
updateOptionsUi();
@@ -2988,12 +3035,10 @@
List<PageRange> rangesToShred = new ArrayList<>();
PageRange previousRange = null;
- final int pageCount = printJob.getDocumentInfo().getPageCount();
-
PageRange[] printedPages = printJob.getPages();
final int rangeCount = printedPages.length;
for (int i = 0; i < rangeCount; i++) {
- PageRange range = PageRangeUtils.asAbsoluteRange(printedPages[i], pageCount);
+ PageRange range = printedPages[i];
if (previousRange == null) {
final int startPageIdx = 0;
@@ -3012,11 +3057,8 @@
}
if (i == rangeCount - 1) {
- final int startPageIdx = range.getEnd() + 1;
- final int endPageIdx = printJob.getDocumentInfo().getPageCount() - 1;
- if (startPageIdx <= endPageIdx) {
- PageRange removedRange = new PageRange(startPageIdx, endPageIdx);
- rangesToShred.add(removedRange);
+ if (range.getEnd() != Integer.MAX_VALUE) {
+ rangesToShred.add(new PageRange(range.getEnd() + 1, Integer.MAX_VALUE));
}
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
index e53a522..fcc9f6a 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
@@ -18,12 +18,11 @@
import android.app.Activity;
import android.app.LoaderManager;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender.SendIntentException;
import android.content.Loader;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.database.DataSetObserver;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
@@ -34,6 +33,7 @@
import android.printservice.PrintServiceInfo;
import android.provider.Settings;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.TypedValue;
import android.view.ContextMenu;
@@ -73,14 +73,15 @@
private static final int LOADER_ID_PRINT_REGISTRY_INT = 2;
private static final int LOADER_ID_ENABLED_PRINT_SERVICES = 3;
- public static final String INTENT_EXTRA_PRINTER_ID = "INTENT_EXTRA_PRINTER_ID";
+ public static final String INTENT_EXTRA_PRINTER = "INTENT_EXTRA_PRINTER";
+ private static final String EXTRA_PRINTER = "EXTRA_PRINTER";
private static final String EXTRA_PRINTER_ID = "EXTRA_PRINTER_ID";
private static final String KEY_NOT_FIRST_CREATE = "KEY_NOT_FIRST_CREATE";
- /** If there are any enabled print services */
- private boolean mHasEnabledPrintServices;
+ /** The currently enabled print services by their ComponentName */
+ private ArrayMap<ComponentName, PrintServiceInfo> mEnabledPrintServices;
private PrinterRegistry mPrinterRegistry;
@@ -99,6 +100,8 @@
setContentView(R.layout.select_printer_activity);
+ mEnabledPrintServices = new ArrayMap<>();
+
mPrinterRegistry = new PrinterRegistry(this, null, LOADER_ID_PRINT_REGISTRY,
LOADER_ID_PRINT_REGISTRY_INT);
@@ -134,7 +137,7 @@
if (printer == null) {
startAddPrinterActivity();
} else {
- onPrinterSelected(printer.getId());
+ onPrinterSelected(printer);
}
}
});
@@ -244,7 +247,7 @@
MenuItem selectItem = menu.add(Menu.NONE, R.string.print_select_printer,
Menu.NONE, R.string.print_select_printer);
Intent intent = new Intent();
- intent.putExtra(EXTRA_PRINTER_ID, printer.getId());
+ intent.putExtra(EXTRA_PRINTER, printer);
selectItem.setIntent(intent);
}
@@ -263,8 +266,8 @@
public boolean onContextItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.string.print_select_printer: {
- PrinterId printerId = item.getIntent().getParcelableExtra(EXTRA_PRINTER_ID);
- onPrinterSelected(printerId);
+ PrinterInfo printer = item.getIntent().getParcelableExtra(EXTRA_PRINTER);
+ onPrinterSelected(printer);
} return true;
case R.string.print_forget_printer: {
@@ -302,9 +305,9 @@
super.onStop();
}
- private void onPrinterSelected(PrinterId printerId) {
+ private void onPrinterSelected(PrinterInfo printer) {
Intent intent = new Intent();
- intent.putExtra(INTENT_EXTRA_PRINTER_ID, printerId);
+ intent.putExtra(INTENT_EXTRA_PRINTER, printer);
setResult(RESULT_OK, intent);
finish();
}
@@ -316,7 +319,7 @@
}
TextView titleView = (TextView) findViewById(R.id.title);
View progressBar = findViewById(R.id.progress_bar);
- if (!mHasEnabledPrintServices) {
+ if (mEnabledPrintServices.size() > 0) {
titleView.setText(R.string.print_no_print_services);
progressBar.setVisibility(View.GONE);
} else if (adapter.getUnfilteredCount() <= 0) {
@@ -345,11 +348,16 @@
@Override
public void onLoadFinished(Loader<List<PrintServiceInfo>> loader,
- List<PrintServiceInfo> data) {
- if (data == null || data.isEmpty()) {
- mHasEnabledPrintServices = false;
- } else {
- mHasEnabledPrintServices = true;
+ List<PrintServiceInfo> services) {
+ mEnabledPrintServices.clear();
+
+ if (services != null && !services.isEmpty()) {
+ final int numServices = services.size();
+ for (int i = 0; i < numServices; i++) {
+ PrintServiceInfo service = services.get(i);
+
+ mEnabledPrintServices.put(service.getComponentName(), service);
+ }
}
onPrintServicesUpdate();
@@ -532,14 +540,12 @@
CharSequence title = printer.getName();
Drawable icon = printer.loadIcon(SelectPrinterActivity.this);
- CharSequence printServiceLabel;
- try {
- PackageInfo packageInfo = getPackageManager().getPackageInfo(
- printer.getId().getServiceName().getPackageName(), 0);
+ PrintServiceInfo service = mEnabledPrintServices.get(printer.getId().getServiceName());
- printServiceLabel = packageInfo.applicationInfo.loadLabel(getPackageManager());
- } catch (NameNotFoundException e) {
- printServiceLabel = null;
+ CharSequence printServiceLabel = null;
+ if (service != null) {
+ printServiceLabel = service.getResolveInfo().loadLabel(getPackageManager())
+ .toString();
}
CharSequence description = printer.getDescription();
diff --git a/packages/PrintSpooler/src/com/android/printspooler/util/PageRangeUtils.java b/packages/PrintSpooler/src/com/android/printspooler/util/PageRangeUtils.java
index 2b317b3..7425c03 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/util/PageRangeUtils.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/util/PageRangeUtils.java
@@ -18,7 +18,9 @@
import android.print.PageRange;
import android.print.PrintDocumentInfo;
+import android.util.Pair;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
@@ -155,6 +157,167 @@
}
/**
+ * Return the next position after {@code pos} that is not a space character.
+ *
+ * @param s The string to parse
+ * @param pos The starting position
+ *
+ * @return The position of the first space character
+ */
+ private static int readWhiteSpace(CharSequence s, int pos) {
+ while (pos < s.length() && s.charAt(pos) == ' ') {
+ pos++;
+ }
+
+ return pos;
+ }
+
+ /**
+ * Read a number from a string at a certain position.
+ *
+ * @param s The string to parse
+ * @param pos The starting position
+ *
+ * @return The position after the number + the number read or null if the number was not found
+ */
+ private static Pair<Integer, Integer> readNumber(CharSequence s, int pos) {
+ Integer result = 0;
+ while (pos < s.length() && s.charAt(pos) >= '0' && s.charAt(pos) <= '9') {
+ // Number cannot start with 0
+ if (result == 0 && s.charAt(pos) == '0') {
+ break;
+ }
+ result = result * 10 + (s.charAt(pos) - '0');
+ // Abort on overflow
+ if (result < 0) {
+ break;
+ }
+ pos++;
+ }
+
+ // 0 is not a valid page number
+ if (result == 0) {
+ return new Pair<>(pos, null);
+ } else {
+ return new Pair<>(pos, result);
+ }
+ }
+
+ /**
+ * Read a single character from a string at a certain position.
+ *
+ * @param s The string to parse
+ * @param pos The starting position
+ * @param expectedChar The character to read
+ *
+ * @return The position after the character + the character read or null if the character was
+ * not found
+ */
+ private static Pair<Integer, Character> readChar(CharSequence s, int pos, char expectedChar) {
+ if (pos < s.length() && s.charAt(pos) == expectedChar) {
+ return new Pair<>(pos + 1, expectedChar);
+ } else {
+ return new Pair<>(pos, null);
+ }
+ }
+
+ /**
+ * Read a page range character from a string at a certain position.
+ *
+ * @param s The string to parse
+ * @param pos The starting position
+ * @param maxPageNumber The highest page number to accept.
+ *
+ * @return The position after the page range + the page range read or null if the page range was
+ * not found
+ */
+ private static Pair<Integer, PageRange> readRange(CharSequence s, int pos, int maxPageNumber) {
+ Pair<Integer, Integer> retInt;
+ Pair<Integer, Character> retChar;
+
+ Character comma;
+ if (pos == 0) {
+ // When we reading the first range, we do not want to have a comma
+ comma = ',';
+ } else {
+ retChar = readChar(s, pos, ',');
+ pos = retChar.first;
+ comma = retChar.second;
+ }
+
+ pos = readWhiteSpace(s, pos);
+
+ retInt = readNumber(s, pos);
+ pos = retInt.first;
+ Integer start = retInt.second;
+
+ pos = readWhiteSpace(s, pos);
+
+ retChar = readChar(s, pos, '-');
+ pos = retChar.first;
+ Character separator = retChar.second;
+
+ pos = readWhiteSpace(s, pos);
+
+ retInt = readNumber(s, pos);
+ pos = retInt.first;
+ Integer end = retInt.second;
+
+ pos = readWhiteSpace(s, pos);
+
+ if (comma != null &&
+ // range, maybe unbounded
+ ((separator != null && (start != null || end != null)) ||
+ // single page
+ (separator == null && start != null && end == null))) {
+ if (start == null) {
+ start = 1;
+ }
+
+ if (end == null) {
+ if (separator == null) {
+ end = start;
+ } else {
+ end = maxPageNumber;
+ }
+ }
+
+ if (start <= end && start >= 1 && end <= maxPageNumber) {
+ return new Pair<>(pos, new PageRange(start - 1, end - 1));
+ }
+ }
+
+ return new Pair<>(pos, null);
+ }
+
+ /**
+ * Parse a string into an array of page ranges.
+ *
+ * @param s The string to parse
+ * @param maxPageNumber The highest page number to accept.
+ *
+ * @return The parsed ranges or null if the string could not be parsed.
+ */
+ public static PageRange[] parsePageRanges(CharSequence s, int maxPageNumber) {
+ ArrayList<PageRange> ranges = new ArrayList<>();
+
+ int pos = 0;
+ while (pos < s.length()) {
+ Pair<Integer, PageRange> retRange = readRange(s, pos, maxPageNumber);
+
+ if (retRange.second == null) {
+ ranges.clear();
+ break;
+ }
+
+ ranges.add(retRange.second);
+ pos = retRange.first;
+ }
+
+ return PageRangeUtils.normalize(ranges.toArray(new PageRange[ranges.size()]));
+ }
+
+ /**
* Offsets a the start and end of page ranges with the given value.
*
* @param pageRanges The page ranges to offset.
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 406c9b7..c44f0d0 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -278,8 +278,7 @@
<string name="enable_webview_multiprocess_desc" msgid="852226124223847283">"Laat loop WebView-leweraars in \'n geïsoleerde proses."</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView-implementering"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Stel WebView-implementering"</string>
- <!-- no translation found for select_webview_provider_toast_text (5466970498308266359) -->
- <skip />
+ <string name="select_webview_provider_toast_text" msgid="5466970498308266359">"Hierdie keuse is nie meer geldig nie. Probeer weer."</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Skakel om na lêerenkripsie"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Skakel om …"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Lêerenkripsie is reeds uitgevoer"</string>
@@ -297,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Hierdie kenmerk is eksperimenteel en kan werkverrigting beïnvloed."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Geneutraliseer deur <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Ongeveer <xliff:g id="TIME">%1$s</xliff:g> oor"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – sowat <xliff:g id="TIME">%2$s</xliff:g> oor"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> tot vol"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> tot vol op WS"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> tot vol oor USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> tot vol vanaf draadloos"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Onbekend"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Laai"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Laai tans op WS"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Laai tans oor USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Laai tans draadloos"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Laai nie"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Laai nie"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Vol"</string>
@@ -317,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Tuis"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> gelede"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> oor"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Klein"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Verstek"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Groot"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Groter"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Grootste"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Gepasmaak (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index e4cd8b9..cd05135 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -278,8 +278,7 @@
<string name="enable_webview_multiprocess_desc" msgid="852226124223847283">"የWebView ምስል ሰሪዎችን በተገለለ ሂደት ውስጥ አሂድ።"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"የWebView ትግበራ"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"የWebView ትግበራን ያዘጋጁ"</string>
- <!-- no translation found for select_webview_provider_toast_text (5466970498308266359) -->
- <skip />
+ <string name="select_webview_provider_toast_text" msgid="5466970498308266359">"ይህ ምርጫ ከአሁን በኋላ የሚሰራ አይደለም። እንደገና ይሞክሩ።"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"ወደ ፋይል ምሥጠራ ቀይር"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"ለውጥ…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"ፋይል አስቀድሞ ተመስጥሯል"</string>
@@ -297,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ይህ ባህሪ የሙከራ ነውና አፈጻጸም ላይ ተጽዕኖ ሊኖረው ይችላል።"</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"በ<xliff:g id="TITLE">%1$s</xliff:g> ተሽሯል"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"<xliff:g id="TIME">%1$s</xliff:g> ገደማ ቀርቷል"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - ገደማ <xliff:g id="TIME">%2$s</xliff:g> ይቀራል"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> እስከሚሞላ ድረስ"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> በኤሲ ላይ እስከሚሞላ ድረስ"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> በዩኤስቢ ላይ እስከሚሞላ ድረስ"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> በገመድ አልባ ላይ እስከሚሞላ ድረስ"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"ያልታወቀ"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"ኃይል በመሙላት ላይ"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"በኤሲ ሃይል በመሙላት ላይ"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"በዩኤስቢ ሃይል በመሙላት ላይ"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"በገመድ አልባ ሃይል በመሙላት ላይ"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"ባትሪ እየሞላ አይደለም"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"ኃይል እየሞላ አይደለም"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"ሙሉነው"</string>
@@ -317,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"መነሻ"</string>
<string name="charge_length_format" msgid="8978516217024434156">"ከ<xliff:g id="ID_1">%1$s</xliff:g> በፊት"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> ቀርቷል"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"ትንሽ"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"ነባሪ"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"ትልቅ"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"ተለቅ ያለ"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"በጣም ተለቅ ያለ"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"ብጁ (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index ad87a89..b3f55eb 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"هذه الميزة تجريبية وقد تؤثر في الأداء."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"تم الاستبدال بـ <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"يتبقى <xliff:g id="TIME">%1$s</xliff:g> تقريبًا"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - تبقى <xliff:g id="TIME">%2$s</xliff:g> تقريبًا"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> حتى الاكتمال"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> حتى الاكتمال باستخدام التيار المتردد"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> حتى الاكتمال عبر USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> حتى الاكتمال بالشحن اللاسلكي"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"غير معروف"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"شحن"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"جارٍ الشحن بتيار متردد"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"جارٍ الشحن عبر USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"جارٍ الشحن لاسلكيًا"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"لا يتم الشحن"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"لا يتم الشحن"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"ممتلئة"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"الشاشة الرئيسية"</string>
<string name="charge_length_format" msgid="8978516217024434156">"قبل <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"يتبقى <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"صغير"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"افتراضي"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"كبير"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"أكبر"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"أكبر مستوى"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"مخصص (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-az-rAZ/strings.xml b/packages/SettingsLib/res/values-az-rAZ/strings.xml
index c141445..9a099af 100644
--- a/packages/SettingsLib/res/values-az-rAZ/strings.xml
+++ b/packages/SettingsLib/res/values-az-rAZ/strings.xml
@@ -278,8 +278,7 @@
<string name="enable_webview_multiprocess_desc" msgid="852226124223847283">"WebView rendererləri təcrid olunmuş prosesdə işlədin."</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView icrası"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView icrasını ayarlayın"</string>
- <!-- no translation found for select_webview_provider_toast_text (5466970498308266359) -->
- <skip />
+ <string name="select_webview_provider_toast_text" msgid="5466970498308266359">"Bu seçim artıq etibarlı deyil. Yenidən cəhd edin."</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Fayl şifrələnməsinə çevirin"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Çevirin..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Fayl artıq şifrələnib"</string>
@@ -297,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Bu funksiya eksperimentaldır və performansa təsir edə bilər."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> tərəfindən qəbul edilmir"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Təxminən <xliff:g id="TIME">%1$s</xliff:g> qalıb"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - təxminən <xliff:g id="TIME">%2$s</xliff:g> qalıb"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> dolana qədər"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> AC üzərindən dolana qədər"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB üzərindən dolana qədər"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> naqilsiz üzərindən dolana qədər"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Naməlum"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Enerji doldurma"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Dəyişən cərəyanda qidalanır"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB üzərindən qidalanır"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Naqilsiz qidalanır"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Doldurulmur"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Enerji doldurulmur"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Tam"</string>
@@ -317,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Əsas səhifə"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> əvvəl"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> qalıb"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Kiçik"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Defolt"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Böyük"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Daha böyük"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Ən böyük"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Fərdi (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 3e3cce0..85b6ac2 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ova funkcija je eksperimentalna i može da utiče na performanse."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Zamenjuje ga <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Još otprilike <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – preostalo oko <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> dok se ne napuni"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> dok se ne napuni punjačem"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> dok se ne napuni preko USB-a"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> dok se ne napuni bežično"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Nepoznato"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Punjenje"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Punjenje preko punjača"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Punjenje preko USB-a"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Bežično punjenje"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Ne puni se"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ne puni se"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Puno"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Početni"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Pre <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Još <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Mali"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Podrazumevano"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Veliki"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Veći"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Najveći"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Prilagođeni (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-be-rBY/strings.xml b/packages/SettingsLib/res/values-be-rBY/strings.xml
index 08aa3a0..c8098ec 100644
--- a/packages/SettingsLib/res/values-be-rBY/strings.xml
+++ b/packages/SettingsLib/res/values-be-rBY/strings.xml
@@ -278,8 +278,7 @@
<string name="enable_webview_multiprocess_desc" msgid="852226124223847283">"Запусціць апрацоўшчыкі WebView у ізаляваным працэсе."</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"Рэалізацыя WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Наладзіць рэалізацыю WebView"</string>
- <!-- no translation found for select_webview_provider_toast_text (5466970498308266359) -->
- <skip />
+ <string name="select_webview_provider_toast_text" msgid="5466970498308266359">"Гэты варыянт больш не даступны. Паспрабуйце яшчэ раз."</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Перайсці на шыфраванне файлаў"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Пераход..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Шыфраванне файлаў ужо дзейнічае"</string>
@@ -297,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Гэтая функцыя з\'яўляецца эксперыментальнай і можа паўплываць на прадукцыйнасць."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Перавызначаны <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Засталося прыблізна <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – засталося прыблізна <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> да поўнай зарадкі"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> да поўнай зарадкі ад сеткі пер. току"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> да поўнай зарадкі па USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> да поўн. зарадкі бесправадным шляхам"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Невядома"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Зарадка"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Зар. ад сеткі пер. току"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Зарадка па USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Бесправадная зарадка"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Не зараджаецца"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Не зараджаецца"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Поўная"</string>
@@ -317,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Галоўная"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> таму назад"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Засталося <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Маленькі"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Стандартны"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Вялікі"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Большы"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Найвялікшы"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Карыстальніцкі (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 2dbeeac..35b24bf 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Тази функция е експериментална и може да се отрази на ефективността."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Заменено от „<xliff:g id="TITLE">%1$s</xliff:g>“"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Прибл. оставащо време: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – приблизително оставащо време: <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до пълно зареждане"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до пълно зареждане при променлив ток"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до пълно зареждане през USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до пълно безжично зареждане"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Неизвестно"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Зарежда се"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Зареждане при AC"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Зареждане през USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Безжично зареждане"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Не се зарежда"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Не се зарежда"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Пълна"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Начало"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Преди <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Оставащо време: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Малко"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"По подразбиране"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Голямо"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"По-голямо"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Най-голямо"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Персонализирано (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-bn-rBD/strings.xml b/packages/SettingsLib/res/values-bn-rBD/strings.xml
index eea4f06..14c2c95 100644
--- a/packages/SettingsLib/res/values-bn-rBD/strings.xml
+++ b/packages/SettingsLib/res/values-bn-rBD/strings.xml
@@ -296,17 +296,26 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"এই বৈশিষ্ট্যটি পরীক্ষামূলক এবং এটি কার্য-সম্পাদনা প্রভাবিত করতে পারে।"</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> এর দ্বারা ওভাররাইড করা হয়েছে"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"প্রায় <xliff:g id="TIME">%1$s</xliff:g> বাকী আছে"</string>
+ <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> বাকী আছে"</string>
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - আনুমানিক <xliff:g id="TIME">%2$s</xliff:g> বাকি আছে"</string>
+ <string name="power_discharging_duration_short" msgid="4192244429001842403">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> বাকী আছে"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - সম্পূর্ণ হতে <xliff:g id="TIME">%2$s</xliff:g> বাকি"</string>
+ <string name="power_charging_duration_short" msgid="1098603958472207920">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - ACতে সম্পূর্ণ হতে <xliff:g id="TIME">%2$s</xliff:g> বাকি"</string>
+ <string name="power_charging_duration_ac_short" msgid="7895864687218765582">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - USB এর মাধ্যমে সম্পূর্ণ হতে <xliff:g id="TIME">%2$s</xliff:g> বাকি"</string>
+ <string name="power_charging_duration_usb_short" msgid="941854728040426399">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - বেতার যোগে সম্পূর্ণ হতে <xliff:g id="TIME">%2$s</xliff:g> বাকি"</string>
+ <string name="power_charging_duration_wireless_short" msgid="1642664799869599476">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"অজানা"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"চার্জ হচ্ছে"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC তে চার্জ হচ্ছে"</string>
+ <string name="battery_info_status_charging_ac_short" msgid="7431401092096415502">"চার্জ হচ্ছে"</string>
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB এর মাধ্যমে চার্জ হচ্ছে"</string>
+ <string name="battery_info_status_charging_usb_short" msgid="6733371990319101366">"চার্জ হচ্ছে"</string>
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"তারবিহীনভাবে চার্জ হচ্ছে"</string>
+ <string name="battery_info_status_charging_wireless_short" msgid="752569941028903610">"চার্জ হচ্ছে"</string>
<string name="battery_info_status_discharging" msgid="310932812698268588">"চার্জ হচ্ছে না"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"চার্জ হচ্ছে না"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"পূর্ণ"</string>
@@ -316,4 +325,10 @@
<string name="home" msgid="8263346537524314127">"হোম"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> আগে"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> বাকী আছে"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"ক্ষুদ্র"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"ডিফল্ট"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"বড়"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"খুব বড়"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"বৃহত্তম"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"কাস্টম (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-bs-rBA/strings.xml b/packages/SettingsLib/res/values-bs-rBA/strings.xml
index cf9e4a4..f9c7fcad 100644
--- a/packages/SettingsLib/res/values-bs-rBA/strings.xml
+++ b/packages/SettingsLib/res/values-bs-rBA/strings.xml
@@ -278,8 +278,7 @@
<string name="enable_webview_multiprocess_desc" msgid="852226124223847283">"Pokrenite WebView operatera u izolovanom procesu."</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"Postavljanje WebViewa"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Podesi WebView"</string>
- <!-- no translation found for select_webview_provider_toast_text (5466970498308266359) -->
- <skip />
+ <string name="select_webview_provider_toast_text" msgid="5466970498308266359">"Ovaj izbor više ne vrijedi. Pokušajte ponovo."</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Pretvori u šifrirani fajl"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Pretvaranje…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Fajl je već šifriran"</string>
@@ -297,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ova funkcija je eksperimentalna te može utjecati na performanse."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Zamjenjuje <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Još otprilike <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – preostalo vreme je otprilike <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do pune baterije"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do kraja punjenja na el. napajanju"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do pune baterije preko USB-a"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do pune baterije bežičnim punjenjem"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Nepoznato"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Puni se"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Puni se na punjaču"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Punjenje preko USB-a"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Bežično punjenje"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Ne puni se"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ne puni se"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Puna"</string>
@@ -317,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Početna stranica"</string>
<string name="charge_length_format" msgid="8978516217024434156">"prije <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Još otprilike <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Malo"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Zadano"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Veliko"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Veće"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Najveće"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Prilagodi (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 8d158ac..a070740 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -296,17 +296,26 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Aquesta funció és experimental i pot afectar el rendiment."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"S\'ha substituït per <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Temps restant aproximat: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Temps restant: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g>: falten aproximadament <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_discharging_duration_short" msgid="4192244429001842403">"<xliff:g id="LEVEL">%1$s</xliff:g>; temps restant: <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> per completar la càrrega"</string>
+ <string name="power_charging_duration_short" msgid="1098603958472207920">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> per completar la càrrega per CA"</string>
+ <string name="power_charging_duration_ac_short" msgid="7895864687218765582">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> per completar la càrrega per USB"</string>
+ <string name="power_charging_duration_usb_short" msgid="941854728040426399">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> per completar la càrrega sense fil"</string>
+ <string name="power_charging_duration_wireless_short" msgid="1642664799869599476">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Desconegut"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"S\'està carregant"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Càrrega: corr. alt."</string>
+ <string name="battery_info_status_charging_ac_short" msgid="7431401092096415502">"S\'està carregant"</string>
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Càrrega per USB"</string>
+ <string name="battery_info_status_charging_usb_short" msgid="6733371990319101366">"S\'està carregant"</string>
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Càrrega sense fils"</string>
+ <string name="battery_info_status_charging_wireless_short" msgid="752569941028903610">"S\'està carregant"</string>
<string name="battery_info_status_discharging" msgid="310932812698268588">"No s\'està carregant"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"No s\'està carregant"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Plena"</string>
@@ -316,4 +325,10 @@
<string name="home" msgid="8263346537524314127">"Inici"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Fa <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Temps restant: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Petit"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Predeterminat"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Gran"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Més gran"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Màxim"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Personalitzat (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 9e9abad..040c9f0 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Funkce je experimentální a může mít vliv na výkon."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Přepsáno nastavením <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Zbývající čas: <xliff:g id="TIME">%1$s</xliff:g> (přibližně)"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – zbývá přibližně <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabití"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabití ze zásuvky"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabití přes USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabití bezdrátově"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Neznámé"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Nabíjí se"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Nabíjení z adaptéru"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Nabíjení přes USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Bezdrátové nabíjení"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Nenabíjí se"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nenabíjí se"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Nabitá"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Plocha"</string>
<string name="charge_length_format" msgid="8978516217024434156">"před <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Zbývající čas: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Malé"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Výchozí"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Velké"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Větší"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Největší"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Vlastní (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 659bd32..ddad673 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -296,17 +296,26 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Denne funktion er eksperimentel og kan påvirke ydeevnen."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Tilsidesat af <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Ca. <xliff:g id="TIME">%1$s</xliff:g> tilbage"</string>
+ <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> tilbage"</string>
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – ca. <xliff:g id="TIME">%2$s</xliff:g> tilbage"</string>
+ <string name="power_discharging_duration_short" msgid="4192244429001842403">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> tilbage"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> til fuldt opladet"</string>
+ <string name="power_charging_duration_short" msgid="1098603958472207920">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> til fuldt opladet med adapter"</string>
+ <string name="power_charging_duration_ac_short" msgid="7895864687218765582">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> til fuldt opladet med USB"</string>
+ <string name="power_charging_duration_usb_short" msgid="941854728040426399">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> til fuldt opladet med trådløs"</string>
+ <string name="power_charging_duration_wireless_short" msgid="1642664799869599476">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Ukendt"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Oplader"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Opladning med AC"</string>
+ <string name="battery_info_status_charging_ac_short" msgid="7431401092096415502">"Oplader"</string>
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Opladning via USB"</string>
+ <string name="battery_info_status_charging_usb_short" msgid="6733371990319101366">"Oplader"</string>
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Trådløs opladning"</string>
+ <string name="battery_info_status_charging_wireless_short" msgid="752569941028903610">"Oplader"</string>
<string name="battery_info_status_discharging" msgid="310932812698268588">"Oplader ikke"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Oplader ikke"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Fuld"</string>
@@ -316,4 +325,10 @@
<string name="home" msgid="8263346537524314127">"Start"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> siden"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> tilbage"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Lille"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Standard"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Stor"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Større"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Størst"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Tilpasset (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 67d0198..514228f 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Hierbei handelt es sich um eine experimentelle Funktion. Dies kann sich auf die Leistung auswirken."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Außer Kraft gesetzt von \"<xliff:g id="TITLE">%1$s</xliff:g>\""</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Noch ca. <xliff:g id="TIME">%1$s</xliff:g> verbleibend"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – noch etwa <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – voll in <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – bei Stromanschluss voll in <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – über USB voll in <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – bei kabellosem Laden voll in <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Unbekannt"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Wird aufgeladen"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Laden über Netzteil"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Laden über USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Kabelloses Laden"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Wird nicht geladen"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Wird nicht geladen"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Voll"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Startseite"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Vor <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Noch <xliff:g id="ID_1">%1$s</xliff:g> verbleibend"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Klein"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Standard"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Groß"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Größer"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Am größten"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Benutzerdefiniert (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 263703c..8df693a 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Αυτή η λειτουργία είναι πειραματική και ενδεχομένως να επηρεάσει τις επιδόσεις."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Αντικαταστάθηκε από <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Απομένουν περίπου <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - απομένουν περίπου <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> για πλήρη φόρτιση"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> για πλήρη φόρτιση με φορτιστή AC"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> για πλήρη φόρτιση μέσω USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> για πλήρη ασύρματη φόρτιση"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Άγνωστο"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Φόρτιση"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Φόρτιση με AC"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Φόρτιση μέσω USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Ασύρματη φόρτιση"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Δεν φορτίζει"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Δεν φορτίζει"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Πλήρης"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Αρχική οθόνη"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Πριν από <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Απομένουν <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Μικρά"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Προεπιλογή"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Μεγάλα"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Πιο μεγάλα"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Μεγαλύτερα"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Προσαρμοσμένη (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index 7a00eb7..466291a 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"This feature is experimental and may affect performance."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Overridden by <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Approx. <xliff:g id="TIME">%1$s</xliff:g> left"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – approx. <xliff:g id="TIME">%2$s</xliff:g> left"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full on AC"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full over USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full from wireless"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Unknown"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Charging"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Charging on AC"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Charging over USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Charging wirelessly"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Not charging"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Not charging"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Full"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Home"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> ago"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> left"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Small"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Default"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Large"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Larger"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Largest"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Custom (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index 7a00eb7..466291a 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"This feature is experimental and may affect performance."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Overridden by <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Approx. <xliff:g id="TIME">%1$s</xliff:g> left"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – approx. <xliff:g id="TIME">%2$s</xliff:g> left"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full on AC"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full over USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full from wireless"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Unknown"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Charging"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Charging on AC"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Charging over USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Charging wirelessly"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Not charging"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Not charging"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Full"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Home"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> ago"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> left"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Small"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Default"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Large"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Larger"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Largest"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Custom (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index 7a00eb7..466291a 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"This feature is experimental and may affect performance."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Overridden by <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Approx. <xliff:g id="TIME">%1$s</xliff:g> left"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – approx. <xliff:g id="TIME">%2$s</xliff:g> left"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full on AC"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full over USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full from wireless"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Unknown"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Charging"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Charging on AC"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Charging over USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Charging wirelessly"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Not charging"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Not charging"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Full"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Home"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> ago"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> left"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Small"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Default"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Large"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Larger"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Largest"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Custom (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index b45753d..9bc2cf4 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Esta función es experimental y puede afectar el rendimiento."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Reemplazado por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Falta <xliff:g id="TIME">%1$s</xliff:g> aproximadamente"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g>: alrededor de <xliff:g id="TIME">%2$s</xliff:g> para completar la carga"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> para completar la carga"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> para completar la carga por CA"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> para completar la carga por USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> para completar la carga inalámbrica"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Desconocido"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Cargando"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Carga en CA"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Carga con USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Carga inalámbrica"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"No se está cargando."</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"No se realiza la carga"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Cargado"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Página principal"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Hace <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Falta <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Pequeño"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Predeterminado"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Grande"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Más grande"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Máximo"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Personalizado (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 1e36088..ec0cafe 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -34,7 +34,7 @@
<string name="connected_via_passpoint" msgid="2826205693803088747">"Conectado a través de %1$s"</string>
<string name="available_via_passpoint" msgid="1617440946846329613">"Disponible a través de %1$s"</string>
<string name="wifi_connected_no_internet" msgid="3149853966840874992">"Conexión sin Internet"</string>
- <string name="bluetooth_disconnected" msgid="6557104142667339895">"Desconectada"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Desconectado"</string>
<string name="bluetooth_disconnecting" msgid="8913264760027764974">"Desconectando…"</string>
<string name="bluetooth_connecting" msgid="8555009514614320497">"Estableciendo conexión…"</string>
<string name="bluetooth_connected" msgid="6038755206916626419">"Conectado"</string>
@@ -296,17 +296,26 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Esta función es experimental y puede afectar al rendimiento."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Anulado por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Tiempo restante (aproximado): <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Tiempo restante: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - Quedan <xliff:g id="TIME">%2$s</xliff:g> aproximadamente"</string>
+ <string name="power_discharging_duration_short" msgid="4192244429001842403">"<xliff:g id="LEVEL">%1$s</xliff:g> - Tiempo restante: <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar la batería"</string>
+ <string name="power_charging_duration_short" msgid="1098603958472207920">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar la batería con CA"</string>
+ <string name="power_charging_duration_ac_short" msgid="7895864687218765582">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar la batería por USB"</string>
+ <string name="power_charging_duration_usb_short" msgid="941854728040426399">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar la batería con Wi-Fi"</string>
+ <string name="power_charging_duration_wireless_short" msgid="1642664799869599476">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Desconocido"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Cargando"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Cargando en CA"</string>
+ <string name="battery_info_status_charging_ac_short" msgid="7431401092096415502">"Cargando"</string>
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Cargando por USB"</string>
+ <string name="battery_info_status_charging_usb_short" msgid="6733371990319101366">"Cargando"</string>
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Cargando de forma inalámbrica"</string>
+ <string name="battery_info_status_charging_wireless_short" msgid="752569941028903610">"Cargando"</string>
<string name="battery_info_status_discharging" msgid="310932812698268588">"No se está cargando"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"No se está cargando"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Completa"</string>
@@ -316,4 +325,10 @@
<string name="home" msgid="8263346537524314127">"Inicio"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Hace <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Tiempo restante: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Pequeño"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Predeterminado"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Grande"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Más grande"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Lo más grande posible"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Personalizado (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-et-rEE/strings.xml b/packages/SettingsLib/res/values-et-rEE/strings.xml
index cb0e7f2..29a1aea 100644
--- a/packages/SettingsLib/res/values-et-rEE/strings.xml
+++ b/packages/SettingsLib/res/values-et-rEE/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"See funktsioon on katseline ja võib mõjutada toimivust."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Alistas <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Umbes <xliff:g id="TIME">%1$s</xliff:g> on jäänud"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – jäänud on umbes <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>, kuni aku on täis"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>, kuni aku on täis (vahelduvvool)"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>, kuni aku on täis (USB)"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>, kuni aku on täis (juhtmeta laad.)"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Tundmatu"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Laadimine"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Laad. vahelduvv.-v."</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Laadimine USB kaudu"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Juhtmevaba laadimine"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Ei lae"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ei lae"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Täis"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Avaekraan"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> tagasi"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> on jäänud"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Väike"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Vaikimisi"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Suur"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Suurem"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Suurim"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Kohandatud (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-eu-rES/strings.xml b/packages/SettingsLib/res/values-eu-rES/strings.xml
index f09949f..4f55e35 100644
--- a/packages/SettingsLib/res/values-eu-rES/strings.xml
+++ b/packages/SettingsLib/res/values-eu-rES/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Eginbidea esperimentala da eta eragina izan dezake funtzionamenduan."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> hobespena gainjarri zaio"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"<xliff:g id="TIME">%1$s</xliff:g> inguru guztiz kargatu arte"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> inguru. <xliff:g id="TIME">%2$s</xliff:g> geratzen d(ir)a"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> guztiz kargatu arte"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> korrontearen bidez guztiz kargatu arte"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB bidez guztiz kargatu arte"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> haririk gabe guztiz kargatu arte"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Ezezaguna"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Kargatzea"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"KA bidez kargatzen"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB bidez kargatzen"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Hari gabe kargatzen"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Ez da kargatzen ari"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ez da kargatzen ari"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Beteta"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Hasierako pantaila"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Duela <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> guztiz kargatu arte"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Txikia"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Lehenetsia"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Handia"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Oso handia"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Handiena"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Pertsonalizatua (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index c621be3..e9d63fd 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -278,8 +278,7 @@
<string name="enable_webview_multiprocess_desc" msgid="852226124223847283">"اجرای تولیدکننده تصویر وبنما در یک پردازش مجزا."</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"اجرای وبنما"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"تنظیم اجرای وبنما"</string>
- <!-- no translation found for select_webview_provider_toast_text (5466970498308266359) -->
- <skip />
+ <string name="select_webview_provider_toast_text" msgid="5466970498308266359">"این انتخاب دیگر معتبر نیست. دوباره امتحان کنید."</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"تبدیل به رمزگذاری برحسب فایل"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"تبدیل…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"از قبل به رمزگذاری بر حسب فایل تبدیل شده است"</string>
@@ -297,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"این قابلیت آزمایشی است و ممکن است عملکرد را تحت تأثیر قرار دهد."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"توسط <xliff:g id="TITLE">%1$s</xliff:g> لغو شد"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"تقریباً <xliff:g id="TIME">%1$s</xliff:g> باقی مانده است"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - تقریباً <xliff:g id="TIME">%2$s</xliff:g> باقی مانده است"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> تا شارژ کامل"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> تا شارژ کامل با جریان متناوب"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> تا شارژ کامل از طریق USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> تا شارژ کامل بهطور بیسیم"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"ناشناس"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"در حال شارژ شدن"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"شارژ با جریان متناوب"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"شارژ از طریق USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"شارژ به صورت بیسیم"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"شارژ نمیشود"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"شارژ نمیشود"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"پر"</string>
@@ -317,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"صفحه اصلی"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> قبل"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> باقی مانده است"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"کوچک"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"پیشفرض"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"بزرگ"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"بزرگتر"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"بزرگترین"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"سفارشی (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 8ec2f6e..5531d27 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Tämä ominaisuus on kokeellinen ja voi vaikuttaa suorituskykyyn."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Tämän ohittaa <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Noin <xliff:g id="TIME">%1$s</xliff:g> jäljellä"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – noin <xliff:g id="TIME">%2$s</xliff:g> jäljellä"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> kunnes täynnä"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> kunnes täynnä (laturilataus)"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> kunnes täynnä (USB-lataus)"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> kunnes täynnä (WiFi-lataus)"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Tuntematon"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Ladataan"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Laturilataus"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB-lataus"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Langaton lataus"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Ei laturissa"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ei laturissa"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Täynnä"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Aloitusnäyttö"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> sitten"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> jäljellä"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Pieni"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Oletus"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Suuri"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Suurempi"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Suurin"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Muokattu (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index e2de253..df6bdc6 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Cette fonctionnalité est expérimentale et peut toucher les performances."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Remplacé par <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Il reste environ <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> %% – Temps restant : environ <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> %% (chargée à 100 %% dans <xliff:g id="TIME">%2$s</xliff:g>)"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> %% (charge complète sur c.a. dans <xliff:g id="TIME">%2$s</xliff:g>)"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> %% (chargée à 100 %% par USB dans <xliff:g id="TIME">%2$s</xliff:g>)"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> %% (chargée à 100 %% avec chargeur sans fil dans <xliff:g id="TIME">%2$s</xliff:g>)"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Inconnu"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Batterie en charge"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"En charge (c.a.)"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"En charge par USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"En charge sans fil"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"N\'est pas en charge"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"N\'est pas en charge"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Pleine"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Accueil"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Il y a <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Durée restante :<xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Petite"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Par défaut"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Grande"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Plus grande"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"La plus grande"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Personnalisée (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index e3dd87a..ee40b2a 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Cette fonctionnalité est expérimentale et peut affecter les performances."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Remplacé par <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Il reste environ <xliff:g id="TIME">%1$s</xliff:g>."</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – Temps restant : <xliff:g id="TIME">%2$s</xliff:g> environ"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> (chargée à 100 %% dans <xliff:g id="TIME">%2$s</xliff:g>)"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> (chargée à 100 %% sur secteur dans <xliff:g id="TIME">%2$s</xliff:g>)"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> (chargée à 100 %% via USB dans <xliff:g id="TIME">%2$s</xliff:g>)"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> (chargée à 100 %% sans fil dans <xliff:g id="TIME">%2$s</xliff:g>)"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Inconnu"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Batterie en charge"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"En charge sur secteur"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"En charge via USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"En charge sans fil"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Pas en charge"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Débranchée"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"pleine"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Accueil"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Il y a <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Il reste <xliff:g id="ID_1">%1$s</xliff:g>."</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Petit"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Par défaut"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Grand"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Plus grand"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Le plus grand"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Personnalisé (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-gl-rES/strings.xml b/packages/SettingsLib/res/values-gl-rES/strings.xml
index e75fee6..4a0451e 100644
--- a/packages/SettingsLib/res/values-gl-rES/strings.xml
+++ b/packages/SettingsLib/res/values-gl-rES/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Esta función é experimental e pode afectar ao rendemento."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Anulado por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Duración aproximada de <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - faltan aproximadamente <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar a carga"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar a carga con CA"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar a carga con USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar a carga co modo sen fíos"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Descoñecido"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Cargando"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Cargando con CA"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Cargando por USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Cargando sen fíos"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Non se está cargando"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Non está cargando"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Completa"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Inicio"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Hai <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Tempo restante: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Pequeno"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Predeterminado"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Grande"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Máis grande"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"O máis grande"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Personalizado (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-gu-rIN/strings.xml b/packages/SettingsLib/res/values-gu-rIN/strings.xml
index 38b6089..becda28 100644
--- a/packages/SettingsLib/res/values-gu-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-gu-rIN/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"આ સુવિધા પ્રાયોગિક છે અને કામગીરી પર અસર કરી શકે છે."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> દ્વારા ઓવરરાઇડ થયું"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"અંદાજે. <xliff:g id="TIME">%1$s</xliff:g> બાકી"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - આશરે <xliff:g id="TIME">%2$s</xliff:g> બાકી"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"સંપૂર્ણ થવામાં <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g>, AC પર પૂર્ણ ચાર્જ થયાંને <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g>, USB પર પૂર્ણ ચાર્જ થયાંને <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> વાયરલેસ દ્વારા પૂર્ણ થાય ત્યાં સુધી"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"અજાણ્યું"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"ચાર્જ થઈ રહ્યું છે"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC પર ચાર્જિંગ"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB થી ચાર્જિંગ"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"વાયરલેસથી ચાર્જિંગ"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"ચાર્જ થઈ રહ્યું નથી"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"ચાર્જ થઈ રહ્યું નથી"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"પૂર્ણ"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"હોમ"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> પહેલાં"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> બાકી"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"નાનું"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"ડિફોલ્ટ"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"મોટું"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"વધુ મોટું"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"સૌથી મોટું"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"કસ્ટમ (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index dce538b..6dffdd9 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"यह सुविधा प्रायोगिक है और निष्पादन को प्रभावित कर सकती है."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> के द्वारा ओवरराइड किया गया"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"लगभग <xliff:g id="TIME">%1$s</xliff:g> शेष"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - लगभग <xliff:g id="TIME">%2$s</xliff:g> शेष"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> पूरी होने तक"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> AC पर पूरी होने तक"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB पर पूरी होने तक"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> वायरलेस से पूरी होने तक"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"अज्ञात"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"चार्ज हो रही है"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC से चार्ज हो रही"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB पर चार्ज हो रही"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"वायरलेस रूप से चार्ज हो रही"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"चार्ज नहीं हो रही है"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"चार्ज नहीं हो रही है"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"पूरी"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"होम"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> पहले"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> शेष"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"छोटा"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"डिफ़ॉल्ट"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"बड़ा"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"अधिक बड़ा"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"सबसे बड़ा"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"कस्टम (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 7a2acd8..1edb015 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -296,17 +296,26 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ova je značajka eksperimentalna i može utjecati na performanse."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Premošćeno postavkom <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Još približno <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Još <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – još približno <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_discharging_duration_short" msgid="4192244429001842403">"<xliff:g id="LEVEL">%1$s</xliff:g> – još <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napunjenosti"</string>
+ <string name="power_charging_duration_short" msgid="1098603958472207920">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napunjenosti strujnim napajanjem"</string>
+ <string name="power_charging_duration_ac_short" msgid="7895864687218765582">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napunjenosti putem USB-a"</string>
+ <string name="power_charging_duration_usb_short" msgid="941854728040426399">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napunjenosti bežičnim putem"</string>
+ <string name="power_charging_duration_wireless_short" msgid="1642664799869599476">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Nepoznato"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Punjenje"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Punjenje punjačem"</string>
+ <string name="battery_info_status_charging_ac_short" msgid="7431401092096415502">"Punjenje"</string>
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Punjenje putem USB-a"</string>
+ <string name="battery_info_status_charging_usb_short" msgid="6733371990319101366">"Punjenje"</string>
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Bežično punjenje"</string>
+ <string name="battery_info_status_charging_wireless_short" msgid="752569941028903610">"Punjenje"</string>
<string name="battery_info_status_discharging" msgid="310932812698268588">"Ne puni se"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ne puni se"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Puna"</string>
@@ -316,4 +325,10 @@
<string name="home" msgid="8263346537524314127">"Početni zaslon"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Prije <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Još <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Malo"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Zadano"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Veliko"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Veće"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Najveće"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Prilagođeno (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 426cff4..060599c 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -296,17 +296,26 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ez egy kísérleti funkció, és hatással lehet a teljesítményre."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Felülírva erre: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Kb. <xliff:g id="TIME">%1$s</xliff:g> van hátra"</string>
+ <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> van hátra"</string>
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – kb. <xliff:g id="TIME">%2$s</xliff:g> van hátra"</string>
+ <string name="power_discharging_duration_short" msgid="4192244429001842403">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> van hátra"</string>
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> a teljes töltöttség eléréséig"</string>
+ <string name="power_charging_duration_short" msgid="1098603958472207920">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> a teljes feltöltésig hálózatról"</string>
+ <string name="power_charging_duration_ac_short" msgid="7895864687218765582">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> a teljes feltöltésig USB-ről"</string>
+ <string name="power_charging_duration_usb_short" msgid="941854728040426399">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> a feltöltésig vezeték nélkül"</string>
+ <string name="power_charging_duration_wireless_short" msgid="1642664799869599476">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Ismeretlen"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Töltés"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Hálózati töltés"</string>
+ <string name="battery_info_status_charging_ac_short" msgid="7431401092096415502">"Töltés"</string>
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB-s töltés"</string>
+ <string name="battery_info_status_charging_usb_short" msgid="6733371990319101366">"Töltés"</string>
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Nem vezetékes töltés"</string>
+ <string name="battery_info_status_charging_wireless_short" msgid="752569941028903610">"Töltés"</string>
<string name="battery_info_status_discharging" msgid="310932812698268588">"Nem tölt"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nem töltődik"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Feltöltve"</string>
@@ -316,4 +325,10 @@
<string name="home" msgid="8263346537524314127">"Főoldal"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Ennyi ideje: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> van hátra"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Kicsi"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Alapértelmezett"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Nagy"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Nagyobb"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Legnagyobb"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Egyéni (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-hy-rAM/strings.xml b/packages/SettingsLib/res/values-hy-rAM/strings.xml
index 8c62221..63c4336 100644
--- a/packages/SettingsLib/res/values-hy-rAM/strings.xml
+++ b/packages/SettingsLib/res/values-hy-rAM/strings.xml
@@ -278,8 +278,7 @@
<string name="enable_webview_multiprocess_desc" msgid="852226124223847283">"Գործարկել WebView-ի մշակիչները առանձնացված գործընթացում:"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView-ի իրականացում"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Ընտրեք WebView-ի իրականացումը"</string>
- <!-- no translation found for select_webview_provider_toast_text (5466970498308266359) -->
- <skip />
+ <string name="select_webview_provider_toast_text" msgid="5466970498308266359">"Այս ընտրանքն այլևս վավեր չէ: Փորձեք նորից:"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Վերածել ֆայլային գաղտնագրման"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Փոխարկել…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Ֆայլային գաղտնագրումն արդեն կատարվել է"</string>
@@ -297,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Սա փորձնական գործառույթ է և կարող է ազդել աշխատանքի վրա:"</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Գերազանցված է <xliff:g id="TITLE">%1$s</xliff:g>-ից"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Մնացել է մոտ <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - մնաց մոտավորապես <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> մինչև լրիվ լիցքավորումը"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> մինչև լրիվ լիցքավորումը հոսանքից"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> մինչև լրիվ լիցքավորումը USB-ով"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> մինչև լրիվ լիցքավորումը անլար ցանցից"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Անհայտ"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Լիցքավորում"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Լիցքավորում AC-ով"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Լիցքավորում USB-ով"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Անլար լիցքավորում"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Չի լիցքավորվում"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Չի լիցքավորվում"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Լիցքավորված"</string>
@@ -317,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Գլխավոր էջ"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> առաջ"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Մնացել է <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Փոքր"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Կանխադրված"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Մեծ"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Ավելի մեծ"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Ամենամեծ"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Հատուկ (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 662bb22..c86c768 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Fitur ini bersifat eksperimental dan dapat memengaruhi kinerja."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Digantikan oleh <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Kira-kira tersisa <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - kira-kira tersisa. <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sampai penuh"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sampai penuh pada AC"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sampai penuh melalui USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sampai penuh dari nirkabel"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Tidak diketahui"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Mengisi daya"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Mengisi daya pada AC"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Isi daya lewat USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Isi daya nirkabel"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Tidak mengisi daya"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Tidak mengisi daya"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Penuh"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Layar Utama"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> lalu"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Tersisa <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Kecil"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Default"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Besar"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Lebih besar"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Terbesar"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"(<xliff:g id="DENSITYDPI">%d</xliff:g>) khusus"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-is-rIS/strings.xml b/packages/SettingsLib/res/values-is-rIS/strings.xml
index 30440da..1fc0dc0 100644
--- a/packages/SettingsLib/res/values-is-rIS/strings.xml
+++ b/packages/SettingsLib/res/values-is-rIS/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Þessi eiginleiki er á tilraunastigi og getur haft áhrif á frammistöðu."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Hnekkt af <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Um það bil <xliff:g id="TIME">%1$s</xliff:g> eftir"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – u.þ.b. <xliff:g id="TIME">%2$s</xliff:g> eftir"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> í fulla hleðslu"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> í fulla hleðslu með hleðslutæki"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> í fulla hleðslu í gegnum USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> í fulla hleðslu þráðlaust"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Óþekkt"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Í hleðslu"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Hleðslutæki tengt"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Hleður um USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Hleður þráðlaust"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Ekki í hleðslu"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ekki í hleðslu"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Fullhlaðin"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Heim"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Fyrir <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> eftir"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Lítið"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Sjálfgefið"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Stórt"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Stærra"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Stærst"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Sérsniðið (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 40802bc..78cd29b 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Questa funzione è sperimentale e potrebbe influire sulle prestazioni."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Valore sostituito da <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Circa <xliff:g id="TIME">%1$s</xliff:g> rimanenti"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – Tempo rimanente: <xliff:g id="TIME">%2$s</xliff:g> circa"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> alla carica completa"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> alla carica completa tramite CA"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> alla carica completa tramite USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> lla carica completa con wireless"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Sconosciuta"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"In carica"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"In carica tramite CA"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"In carica tramite USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"In carica, wireless"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Non in carica"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Non in carica"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Carica"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Home page"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> fa"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> rimanenti"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Piccolo"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Predefinito"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Grande"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Più grande"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Massimo"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Personalizzato (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index a651a3a..16e0f44d 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -278,8 +278,7 @@
<string name="enable_webview_multiprocess_desc" msgid="852226124223847283">"הרץ מעבדי תצוגת אתר בהליך מבודד"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"יישום WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"הגדרת יישום WebView"</string>
- <!-- no translation found for select_webview_provider_toast_text (5466970498308266359) -->
- <skip />
+ <string name="select_webview_provider_toast_text" msgid="5466970498308266359">"אפשרות זו כבר אינה תקפה. נסה שוב."</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"המר להצפנת קבצים"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"המר..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"הצפנת קבצים כבר מוגדרת"</string>
@@ -297,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"תכונה זו היא ניסיונית ועשויה להשפיע על הביצועים."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"נעקף על ידי <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"נשארו <xliff:g id="TIME">%1$s</xliff:g> בערך"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> בקירוב עד לסיום"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> עד למילוי"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> עד למילוי בזרם חילופין"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> עד למילוי ב-USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> עד למילוי בטעינה אלחוטית"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"לא ידוע"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"טוען"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"טוען בזרם חילופין"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"טוען ב-USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"טוען באופן אלחוטי"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"לא בטעינה"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"לא טוען"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"מלא"</string>
@@ -317,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"דף הבית"</string>
<string name="charge_length_format" msgid="8978516217024434156">"לפני <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"נשארו <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"קטן"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"ברירת מחדל"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"גדול"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"יותר גדול"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"הכי גדול"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"מותאם אישית (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index e17db51..ce405df 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"この機能は試験運用機能であり、パフォーマンスに影響することがあります。"</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g>によって上書き済み"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"あと約 <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - 残り約<xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - フル充電まで<xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - フル充電まで<xliff:g id="TIME">%2$s</xliff:g>(AC)"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - フル充電まで<xliff:g id="TIME">%2$s</xliff:g>(USB)"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - フル充電まで<xliff:g id="TIME">%2$s</xliff:g>(ワイヤレス)"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"不明"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"充電中"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"ACで充電しています"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USBで充電しています"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"無線で充電しています"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"充電していません"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"充電していません"</string>
<!-- String.format failed for translation -->
@@ -318,4 +336,10 @@
<string name="home" msgid="8263346537524314127">"ホーム"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g>前"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"あと <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"小"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"デフォルト"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"大"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"特大"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"最大"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"カスタム(<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ka-rGE/strings.xml b/packages/SettingsLib/res/values-ka-rGE/strings.xml
index 11fa8a6..20a1d2a 100644
--- a/packages/SettingsLib/res/values-ka-rGE/strings.xml
+++ b/packages/SettingsLib/res/values-ka-rGE/strings.xml
@@ -278,8 +278,7 @@
<string name="enable_webview_multiprocess_desc" msgid="852226124223847283">"WebView ვიზუალიზატორების იზოლირებულ პროცესში გაშვება."</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView რეალიზაცია"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView რეალიზაციის დაყენება"</string>
- <!-- no translation found for select_webview_provider_toast_text (5466970498308266359) -->
- <skip />
+ <string name="select_webview_provider_toast_text" msgid="5466970498308266359">"თქვენი არჩევანი აღარ მოქმედებს. ცადეთ ხელახლა."</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"ფაილების დაშიფვრაზე გარდაქმნა"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"გარდაქმნა…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"უკვე დაშიფრულია ფაილების დონეზე"</string>
@@ -297,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ეს ფუნქცია საცდელია და შეიძლება გავლენა იქონიოს შესრულებაზე."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"უკუგებულია <xliff:g id="TITLE">%1$s</xliff:g>-ის მიერ"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"დარჩენილია დაახლოებით <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"დაახლ. <xliff:g id="LEVEL">%1$s</xliff:g> დარჩენილია <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> სრულ დატენვამდე"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ელკვებით სრულ დატენვამდე"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB-თი სრულ დატენვამდე"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> უსადენოდან სრულ დატენვამდე"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"უცნობი"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"იტენება"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"დატენვა ელკვებაზე"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"დატენვა USB-ზე"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"დატენვა უსადენოდ"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"არ იტენება"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"არ იტენება"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"ბატარეა დატენილია"</string>
@@ -317,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"მთავარი"</string>
<string name="charge_length_format" msgid="8978516217024434156">"გავიდა <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"დარჩენილია <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"პატარა"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"ნაგულისხმევი"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"დიდი"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"უფრო დიდი"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"უდიდესი"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"მორგებული (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-kk-rKZ/strings.xml b/packages/SettingsLib/res/values-kk-rKZ/strings.xml
index aae7449..e71c8d0 100644
--- a/packages/SettingsLib/res/values-kk-rKZ/strings.xml
+++ b/packages/SettingsLib/res/values-kk-rKZ/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Бұл мүмкіндік эксперименттік болып табылады және өнімділікке әсер етуі мүмкін."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> үстінен басқан"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Шамамен <xliff:g id="TIME">%1$s</xliff:g> қалды"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - шамамен <xliff:g id="TIME">%2$s</xliff:g> қалды"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - толғанша <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - айнымалы токпен толғанша <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - USB арқылы толғанша <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - сымсыз толғанша <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Белгісіз"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Зарядталуда"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Айнымалы токпен зар."</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB арқылы зарядтау"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Сымсыз зарядтау"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Зарядталу орындалып жатқан жоқ"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Зарядталып тұрған жоқ"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Толық"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Негізгі бет"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> бұрын"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> қалды"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Кішкентай"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Әдепкі"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Үлкен"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Үлкенірек"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Ең үлкен"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Арнаулы (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-km-rKH/strings.xml b/packages/SettingsLib/res/values-km-rKH/strings.xml
index 4373c43..687b71b 100644
--- a/packages/SettingsLib/res/values-km-rKH/strings.xml
+++ b/packages/SettingsLib/res/values-km-rKH/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"លក្ខណៈនេះគឺជាការពិសោធន៍ ហើយអាចប៉ះពាល់ការអនុវត្ត។"</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"បដិសេធដោយ <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"នៅសល់ប្រហែល <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - នៅសល់ប្រហែល <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> រហូតដល់ពេញ"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> រហូតដល់ពេញរចន្តឆ្លាស់"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> រហូតដល់ពេញតាមយូអេសប៊ី"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> រហូតដល់ពេញពីឥតខ្សែ"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"មិនស្គាល់"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"កំពុងបញ្ចូលថ្ម"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"បញ្ចូលថ្មតាម AC"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"បញ្ចូលថ្មតាមយូអេសប៊ី"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"បញ្ចូលថ្មដោយឥតខ្សែ"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"មិនកំពុងបញ្ចូលថ្ម"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"មិនបញ្ចូលថ្ម"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"ពេញ"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"ដើម"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> មុន"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"នៅសល់ <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"តូច"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"លំនាំដើម"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"ធំ"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"ធំជាង"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"ធំបំផុត"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"ផ្ទាល់ខ្លួន (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-kn-rIN/strings.xml b/packages/SettingsLib/res/values-kn-rIN/strings.xml
index f0d63fb..713d9b3 100644
--- a/packages/SettingsLib/res/values-kn-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-kn-rIN/strings.xml
@@ -278,8 +278,7 @@
<string name="enable_webview_multiprocess_desc" msgid="852226124223847283">"ಪ್ರತ್ಯೇಕಗೊಳಿಸಿದ ಪ್ರಕ್ರಿಯೆಯಲ್ಲಿ WebView ರೆಂಡರರ್ ರನ್ ಮಾಡಿ."</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView ಅನುಷ್ಠಾನಗೊಳಿಸುವಿಕೆ"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView ಅನುಷ್ಠಾನಗೊಳಿಸುವಿಕೆಯನ್ನು ಹೊಂದಿಸಿ"</string>
- <!-- no translation found for select_webview_provider_toast_text (5466970498308266359) -->
- <skip />
+ <string name="select_webview_provider_toast_text" msgid="5466970498308266359">"ಈ ಆಯ್ಕೆಯು ಇನ್ನು ಮುಂದೆ ಮಾನ್ಯವಾಗಿರುವುದಿಲ್ಲ. ಮತ್ತೊಮ್ಮೆ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"ಫೈಲ್ ಎನ್ಕ್ರಿಪ್ಶನ್ಗೆ ಪರಿವರ್ತಿಸು"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"ಪರಿವರ್ತಿಸು…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"ಫೈಲ್ ಈಗಾಗಲೇ ಎನ್ಕ್ರಿಪ್ಟ್ ಮಾಡಲಾಗಿದೆ"</string>
@@ -297,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ಇದು ಪ್ರಾಯೋಗಿಕ ವೈಶಿಷ್ಟ್ಯವಾಗಿದೆ. ಕಾರ್ಯಕ್ಷಮತೆ ಮೇಲೆ ಪರಿಣಾಮ ಬೀರಬಹುದು."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> ಮೂಲಕ ಅತಿಕ್ರಮಿಸುತ್ತದೆ"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"ಸುಮಾರು <xliff:g id="TIME">%1$s</xliff:g> ಉಳಿದಿದೆ"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"ಸುಮಾರು <xliff:g id="LEVEL">%1$s</xliff:g> <xliff:g id="TIME">%2$s</xliff:g> ಉಳಿದಿದೆ"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ಪೂರ್ಣವಾಗುವವರೆಗೆ"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> AC ನಲ್ಲಿ ಪೂರ್ಣವಾಗುವವರೆಗೆ"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB ಮೂಲಕ ಪೂರ್ಣವಾಗುವವರೆಗೆ"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ವೈರ್ಲೆಸ್ನಿಂದ ಪೂರ್ಣವಾಗುವವರೆಗೆ"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"ಅಜ್ಞಾತ"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC ನಲ್ಲಿ ಚಾರ್ಜ್"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB ಮೂಲಕ ಚಾರ್ಜ್"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"ನಿಸ್ತಂತುವಾಗಿ ಚಾರ್ಜ್"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"ಚಾರ್ಜ್ ಆಗುತ್ತಿಲ್ಲ"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"ಚಾರ್ಜ್ ಆಗುತ್ತಿಲ್ಲ"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"ಭರ್ತಿ"</string>
@@ -317,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"ಮುಖಪುಟ"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> ಹಿಂದೆ"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> ಉಳಿದಿದೆ"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"ಸಣ್ಣದು"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"ಡಿಫಾಲ್ಟ್"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"ದೊಡ್ಡದು"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"ಸ್ವಲ್ಪ ದೊಡ್ಡ"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"ದೊಡ್ಡ"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"ಕಸ್ಟಮ್ (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index a8b6c36..348141e 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"실험실 기능이며 성능에 영향을 줄 수 있습니다."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> 우선 적용됨"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"약 <xliff:g id="TIME">%1$s</xliff:g> 남음"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - 대략 <xliff:g id="TIME">%2$s</xliff:g> 남음"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 후 충전 완료"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 후 충전 완료(AC 전원)"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 후 충전 완료(USB)"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 후 충전 완료(무선)"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"알 수 없음"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"충전 중"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"충전 중(AC 전원)"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"충전 중(USB)"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"충전 중(무선)"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"충전 안함"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"충전 안함"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"충전 완료"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"홈"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> 전"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> 남음"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"작게"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"기본"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"크게"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"더 크게"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"가장 크게"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"맞춤(<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ky-rKG/strings.xml b/packages/SettingsLib/res/values-ky-rKG/strings.xml
index 6491d3c..2016f04 100644
--- a/packages/SettingsLib/res/values-ky-rKG/strings.xml
+++ b/packages/SettingsLib/res/values-ky-rKG/strings.xml
@@ -278,8 +278,7 @@
<string name="enable_webview_multiprocess_desc" msgid="852226124223847283">"WebView рендерерлерин корголгон процессте иштетүү."</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView аткарылышы"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView аткарылышын коюу"</string>
- <!-- no translation found for select_webview_provider_toast_text (5466970498308266359) -->
- <skip />
+ <string name="select_webview_provider_toast_text" msgid="5466970498308266359">"Тандалган нерсе жараксыз болуп калган. Кайра аракет кылыңыз."</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Файл шифрлөөсүнө айландыруу"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Айландыруу…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Файл мурунтан эле шифрленген"</string>
@@ -297,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Бул сынамык мүмкүнчүлүк болгондуктан, иштин майнаптуулугуна таасир этиши мүмкүн."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> менен алмаштырылган"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Болжол менен <xliff:g id="TIME">%1$s</xliff:g> калды"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - болжол менен <xliff:g id="TIME">%2$s</xliff:g> саат калды"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> толгончо"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> AC аркылуу толгончо"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB аркылуу толгончо"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> зымсыз кубаттоо аркылуу толгончо"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Белгисиз"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Кубатталууда"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"ӨА кубатталууда"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB\'ден кубатталууда"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Зымсыз кубатталууда"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Кубат алган жок"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Кубатталган жок"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Толук"</string>
@@ -317,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Башкы бет"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> мурун"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> калды"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Кичине"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Демейки"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Чоң"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Чоңураак"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Эң чоң"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Ыңгайлаштырылган (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-lo-rLA/strings.xml b/packages/SettingsLib/res/values-lo-rLA/strings.xml
index cf90c4b..7e6db5e 100644
--- a/packages/SettingsLib/res/values-lo-rLA/strings.xml
+++ b/packages/SettingsLib/res/values-lo-rLA/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ຄຸນສົມບັດນີ້ກຳລັງຢູ່ໃນການທົດລອງແລະອາດມີຜົນຕໍ່ປະສິດທິພາບ."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"ຖືກແທນໂດຍ <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"ຍັງເຫຼືອປະມານ <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - ເຫຼືອປະມານ <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ຈຶ່ງຈະເຕັມ"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ຈຶ່ງຈະເຕັມໂດຍສາກດ້ວຍໄຟ AC"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ຈຶ່ງຈະເຕັມໂດຍສາກດ້ວຍ USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ຈຶ່ງຈະເຕັມໂດຍສາກແບບໄຮ້ສາຍ"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"ບໍ່ຮູ້ຈັກ"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"ກຳລັງສາກໄຟ"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"ກຳລັງສາກຜ່ານໝໍ້ໄຟ"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"ກຳລັງສາກຜ່ານ USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"ກຳລັງສາກໄຮ້ສາຍ"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"ບໍ່ໄດ້ສາກໄຟ"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"ບໍ່ໄດ້ສາກໄຟ"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"ເຕັມ"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"ໜ້າຫຼັກ"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> ກ່ອນນີ້"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"ຍັງເຫຼືອ <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"ນ້ອຍ"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"ຄ່າເລີ່ມຕົ້ນ"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"ໃຫຍ່"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"ໃຫຍ່ກວ່າ"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"ໃຫຍ່ທີ່ສຸດ"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"ປັບແຕ່ງເອງ (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index fd245f3..d49876e 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ši funkcija yra eksperimentinė ir ji gali turėti įtakos našumui."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Nepaisyta naudojant nuostatą „<xliff:g id="TITLE">%1$s</xliff:g>“"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Liko maždaug <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – liko maždaug <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> iki visiško įkrovimo"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> iki visiško įkrovimo naud. kint. sr."</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> iki visiško įkrovimo naudojant USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> iki visiško įkrovimo belaid. ryš."</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Nežinomas"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Kraunasi..."</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Įkr. naud. kint. sr."</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Įkraunama naud. USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Įkraunama be laidų"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Nekraunama"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nekraunama"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Visiškai įkrautas"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Pagrindinis ekranas"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Prieš <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Liko <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Mažas"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Numatytasis"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Didelis"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Didesnis"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Didžiausias"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Tinkintas (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index b470fb54..6cd9cfd5 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Šī funkcija ir eksperimentāla un var ietekmēt veiktspēju."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Jaunā preference: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Atlikušais laiks: aptuveni <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> — aptuvenais atlikušais laiks: <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="TIME">%2$s</xliff:g> līdz pilnai uzlādei"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="TIME">%2$s</xliff:g> līdz pilnai maiņstrāvas uzlādei"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="TIME">%2$s</xliff:g> līdz pilnai USB uzlādei"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="TIME">%2$s</xliff:g> līdz pilnai bezvadu uzlādei"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Nezināms"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Uzlāde"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Maiņstrāvas uzlāde"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB uzlāde"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Bezvadu uzlāde"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Nenotiek uzlāde"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nenotiek uzlāde"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Pilns"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Sākums"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Pirms šāda laika: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Atlikušais laiks: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Mazs"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Noklusējuma"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Liels"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Lielāks"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Vislielākais"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Pielāgots (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-mk-rMK/strings.xml b/packages/SettingsLib/res/values-mk-rMK/strings.xml
index 7592cd3..f8f65ec 100644
--- a/packages/SettingsLib/res/values-mk-rMK/strings.xml
+++ b/packages/SettingsLib/res/values-mk-rMK/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Функцијата е експериментална и може да влијае на изведбата."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Прескокнато според <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Преостанаа прибл. <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – преостанува приближно <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до целосно полна"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до целосно полна на AC"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до целосно полна преку USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до целосно полна, безжично"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Непознато"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Се полни"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Полнење на струја"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Полнење преку УСБ"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Безжично полнење"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Не се полни"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Не се полни"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Целосна"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Почетна страница"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Пред <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Преостанаа <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Мал"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Стандардно"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Голем"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Поголем"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Најголем"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Приспособен (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ml-rIN/strings.xml b/packages/SettingsLib/res/values-ml-rIN/strings.xml
index 13a7013..7350b7f 100644
--- a/packages/SettingsLib/res/values-ml-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-ml-rIN/strings.xml
@@ -278,8 +278,7 @@
<string name="enable_webview_multiprocess_desc" msgid="852226124223847283">"ഒറ്റപ്പെട്ടൊരു പ്രോസസ്സിൽ WebView റെൻഡററുകൾ റൺ ചെയ്യുക."</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView നടപ്പാക്കൽ"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView നടപ്പാക്കൽ സജ്ജമാക്കുക"</string>
- <!-- no translation found for select_webview_provider_toast_text (5466970498308266359) -->
- <skip />
+ <string name="select_webview_provider_toast_text" msgid="5466970498308266359">"ഈ തിരഞ്ഞെടുപ്പിന് തുടർന്നങ്ങോട്ട് സാധുതയില്ല. വീണ്ടും ശ്രമിക്കുക."</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"ഫയൽ എൻക്രിപ്ഷനിലേക്ക് പരിവർത്തിപ്പിക്കുക"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"പരിവർത്തിപ്പിക്കുക…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"ഇതിനകം തന്നെ ഫയൽ എൻക്രിപ്റ്റ് ചെയ്തു"</string>
@@ -297,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ഈ ഫീച്ചർ പരീക്ഷണാത്മകമായതിനാൽ പ്രകടനത്തെ ബാധിച്ചേക്കാം."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> ഉപയോഗിച്ച് അസാധുവാക്കി"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"ഏകദേശം <xliff:g id="TIME">%1$s</xliff:g> ശേഷിക്കുന്നു"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - ഏകദേശം <xliff:g id="TIME">%2$s</xliff:g> ശേഷിക്കുന്നു"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - പൂർണ്ണമായും ചാർജ്ജാകുന്നതിന്, <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - AC-യിൽ പൂർണ്ണമായും ചാർജ്ജാകുന്നതിന്, <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - USB വഴി പൂർണ്ണമായും ചാർജ്ജാകുന്നതിന്, <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - വയർലെസ് വഴി പൂർണ്ണമായും ചാർജ്ജാകുന്നതിന്, <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"അജ്ഞാതം"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"ചാർജ്ജുചെയ്യുന്നു"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC-യിൽ ചാർജ്ജുചെയ്യുന്നു"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB-യിലൂടെ ചാർജ്ജുചെയ്യുന്നു"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"വയർലെസ്സ് കണക്ഷനിലൂടെ ചാർജ്ജുചെയ്യുന്നു"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"ചാർജ്ജുചെയ്യുന്നില്ല"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"ചാർജ്ജുചെയ്യുന്നില്ല"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"നിറഞ്ഞു"</string>
@@ -317,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"ഹോം"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> മുമ്പ്"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> ശേഷിക്കുന്നു"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"ചെറുത്"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"ഡിഫോൾട്ട്"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"വലുത്"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"കൂടുതൽ വലുത്"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"ഏറ്റവും വലുത്"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"ഇഷ്ടാനുസൃതം ( <xliff:g id="DENSITYDPI">%d</xliff:g> )"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-mn-rMN/strings.xml b/packages/SettingsLib/res/values-mn-rMN/strings.xml
index a77e32e..7eeae06 100644
--- a/packages/SettingsLib/res/values-mn-rMN/strings.xml
+++ b/packages/SettingsLib/res/values-mn-rMN/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Энэ функц туршилтынх бөгөөд ажиллагаанд нөлөөлж болзошгүй."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Давхарласан <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Ойролцоогоор <xliff:g id="TIME">%1$s</xliff:g> үлдсэн"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - ойролцоогоор <xliff:g id="TIME">%2$s</xliff:g> үлдсэн"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"дүүртэл <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"АС-р дүүртэл <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"USB-р дүүртэл <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"утасгүй цэнэглэгчээр дүүртэл <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Тодорхойгүй"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Цэнэглэж байна"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC-р цэнэглэж байна"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB-р цэнэглэж байна"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Кабльгүйгээр цэнэглэж байна"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Цэнэглэхгүй байна"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Цэнэглэхгүй байна"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Дүүрэн"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Нүүр"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> өмнө"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> үлдсэн"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Жижиг"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Өгөгдмөл"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Том"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Илүү том"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Хамгийн том"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Тогтмол утга (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-mr-rIN/strings.xml b/packages/SettingsLib/res/values-mr-rIN/strings.xml
index 4f12e46..0521ba1 100644
--- a/packages/SettingsLib/res/values-mr-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-mr-rIN/strings.xml
@@ -278,8 +278,7 @@
<string name="enable_webview_multiprocess_desc" msgid="852226124223847283">"एक वेगळ्या प्रक्रियेत WebView प्रस्तुतकर्ते चालवा."</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"वेबदृश्य अंमलबजावणी"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"वेबदृश्य अंमलबजावणी सेट करा"</string>
- <!-- no translation found for select_webview_provider_toast_text (5466970498308266359) -->
- <skip />
+ <string name="select_webview_provider_toast_text" msgid="5466970498308266359">"ही निवड यापुढे वैध असणार नाही. पुन्हा प्रयत्न करा."</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"फाईल कूटबद्धीकरणावर रूपांतरित करा"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"रूपांतरित करा..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"फाईल आधीपासून कूटबद्ध केली"</string>
@@ -297,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"हे वैशिष्ट्य प्रायोगिक आहे आणि कदाचित कार्यप्रदर्शन प्रभावित करू शकते."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> द्वारे अधिलिखित"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"अंदाजे. <xliff:g id="TIME">%1$s</xliff:g> शिल्लक"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - अंदाजे. <xliff:g id="TIME">%2$s</xliff:g> शिल्लक"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> पूर्ण होण्यात"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> AC वरून पूर्ण होण्यात"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB वरून पूर्ण होण्यात"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> वायरलेसवरून पूर्ण होण्यात"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"अज्ञात"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"चार्ज होत आहे"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC वर चार्ज करीत आहे"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB वरून चार्ज करीत आहे"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"वायरलेस वरून चार्ज करीत आहे"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"चार्ज होत नाही"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"चार्ज होत नाही"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"पूर्ण"</string>
@@ -317,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"मुख्यपृष्ठ"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> पूर्वी"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> शिल्लक"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"लहान"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"डीफॉल्ट"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"मोठा"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"आणखी मोठा"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"सर्वात मोठा"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"सानुकूल करा (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ms-rMY/strings.xml b/packages/SettingsLib/res/values-ms-rMY/strings.xml
index 6ba7d20..a8be0e5 100644
--- a/packages/SettingsLib/res/values-ms-rMY/strings.xml
+++ b/packages/SettingsLib/res/values-ms-rMY/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ciri ini adalah percubaan dan boleh menjejaskan prestasi."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Diatasi oleh <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Kira-kira <xliff:g id="TIME">%1$s</xliff:g> lagi"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - kira-kira. <xliff:g id="TIME">%2$s</xliff:g> yang tinggal"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sehingga penuh"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sehingga penuh di AC"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sehingga penuh melalui USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sehingga penuh dari wayarles"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Tidak diketahui"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Mengecas"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Mengecas pada AC"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Mengecas melalui USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Mengecas tanpa wayar"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Tidak mengecas"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Tidak mengecas"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Penuh"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Skrin Utama"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> yang lalu"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> lagi"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Kecil"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Lalai"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Besar"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Lebih besar"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Terbesar"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Tersuai (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-my-rMM/strings.xml b/packages/SettingsLib/res/values-my-rMM/strings.xml
index 5c1a5ee..220deff 100644
--- a/packages/SettingsLib/res/values-my-rMM/strings.xml
+++ b/packages/SettingsLib/res/values-my-rMM/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ဒီအင်္ဂါရပ်မှာ စမ်းသပ်မှု ဖြစ်၍ လုပ်ကိုင်မှုကို အကျိုးသက်ရောက်နိုင်သည်။"</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> မှ ကျော်၍ လုပ်ထားသည်။"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"ခန့်မှန်းခြေ <xliff:g id="TIME">%1$s</xliff:g> ကျန်ပါသည်"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - ခန့်မှန်းခြေ။ <xliff:g id="TIME">%2$s</xliff:g> ကျန်ရှိနေ"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> အပြည့်အထိ"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> လျှပ်စစ်ဖြင့် အပြည့်အထိ"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB ဖြင့် အပြည့်အထိ"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ကြိုးမဲ့ဖြင့် အပြည့်အထိ"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"အကြောင်းအရာ မသိရှိ"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"အားသွင်းနေပါသည်"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"လျှပ်စစ်ဖြင့် အားသွင်းနေ"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USBဖြင့် အားသွင်းနေ"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"ကြိုးမဲ့ အားသွင်းနေ"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"အားသွင်းမနေပါ"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"အားသွင်းမနေပါ"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"အပြည့်"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"ပင်မ"</string>
<string name="charge_length_format" msgid="8978516217024434156">"ပြီးခဲ့သည့် <xliff:g id="ID_1">%1$s</xliff:g> က"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> ကျန်ပါသည်"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"သေး"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"မူရင်း"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"ကြီး"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"ပိုကြီး"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"အကြီးဆုံး"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"စိတ်ကြိုက် (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index de6faf4..a1407ac 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Denne funksjonen er eksperimentell og kan påvirke ytelsen."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Overstyres av <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Ca. <xliff:g id="TIME">%1$s</xliff:g> gjenstår"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – ca. <xliff:g id="TIME">%2$s</xliff:g> igjen"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – fulladet om <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – fulladet om <xliff:g id="TIME">%2$s</xliff:g> med vekselstrøm"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – fulladet om <xliff:g id="TIME">%2$s</xliff:g> via USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – fulladet om <xliff:g id="TIME">%2$s</xliff:g> via trådløs lading"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Ukjent"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Lader"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Lader via strømuttak"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Lader via USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Lader trådløst"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Lader ikke"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Lader ikke"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Fullt"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Startside"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> siden"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> gjenstår"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Liten"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Standard"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Stor"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Større"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Størst"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Egendefinert (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ne-rNP/strings.xml b/packages/SettingsLib/res/values-ne-rNP/strings.xml
index 47ec137..83e2e74 100644
--- a/packages/SettingsLib/res/values-ne-rNP/strings.xml
+++ b/packages/SettingsLib/res/values-ne-rNP/strings.xml
@@ -278,8 +278,7 @@
<string name="enable_webview_multiprocess_desc" msgid="852226124223847283">"एउटा पृथक प्रक्रियामा वेबभ्यु रेन्डररहरू चलाउनुहोस्।"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView कार्यान्वयन"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView कार्यान्वयन सेट गर्नुहोस्"</string>
- <!-- no translation found for select_webview_provider_toast_text (5466970498308266359) -->
- <skip />
+ <string name="select_webview_provider_toast_text" msgid="5466970498308266359">"यो छनोट अब मान्य छैन। फेरि प्रयास गर्नुहोस्।"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"फाइल इन्क्रिप्सनमा रूपान्तरण गर्नुहोस्"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"रुपान्तरण गर्नुहोस्…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"पहिल्यै फाइल इन्क्रिप्ट गरिएको छ"</string>
@@ -297,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"यो सुविधा प्रयोगात्मक छ र प्रदर्शनमा असर गर्न सक्छ।"</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> द्वारा अधिरोहित"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"लगभग <xliff:g id="TIME">%1$s</xliff:g> बाँकी छ"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - लगभग। <xliff:g id="TIME">%2$s</xliff:g> बायाँ"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> पूर्ण नभए सम्म"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> AC मा पूर्ण नभए सम्म"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB मा पूर्ण नभए सम्म"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> वायरलेसबाट पूर्ण नभए सम्म"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"अज्ञात"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"चार्ज हुँदै"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC मा चार्ज गर्दै"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB मा चार्ज गर्दै"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"बिना तार चार्ज गर्दै"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"चार्ज भइरहेको छैन"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"चार्ज हुँदै छैन"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"पूर्ण"</string>
@@ -317,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"गृह"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> पहिले"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> बाँकी"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"सानो"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"पूर्वनिर्धारित"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"ठूलो"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"अझ ठूलो"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"सबैभन्दा ठूलो"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"अनुकूलन (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 9f72db5..55017d8 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Deze functie is experimenteel en kan invloed hebben op de prestaties."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Overschreven door <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Ca. <xliff:g id="TIME">%1$s</xliff:g> resterend"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - ca. <xliff:g id="TIME">%2$s</xliff:g> resterend"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> tot vol"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> tot vol via wisselstroom"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> tot vol via USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> tot vol via draadloos"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Onbekend"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Opladen"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Opladen via netvoeding"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Opladen via USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Draadloos opladen"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Wordt niet opgeladen"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Wordt niet opgeladen"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Volledig"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Startpagina"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> geleden"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> resterend"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Klein"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Standaard"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Groot"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Groter"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Grootst"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Aangepast (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-pa-rIN/strings.xml b/packages/SettingsLib/res/values-pa-rIN/strings.xml
index 8625b90..eb1eaac 100644
--- a/packages/SettingsLib/res/values-pa-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-pa-rIN/strings.xml
@@ -278,8 +278,7 @@
<string name="enable_webview_multiprocess_desc" msgid="852226124223847283">"ਕਿਸੇ ਵੱਖ ਕੀਤੀ ਗਈ ਪ੍ਰਕਿਰਿਆ ਵਿੱਚ WebView ਰੈਂਡਰਰਾਂ ਨੂੰ ਚਲਾਓ।"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView ਅਮਲ"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView ਅਮਲ ਸੈੱਟ ਕਰੋ"</string>
- <!-- no translation found for select_webview_provider_toast_text (5466970498308266359) -->
- <skip />
+ <string name="select_webview_provider_toast_text" msgid="5466970498308266359">"ਇਹ ਚੋਣ ਹੁਣ ਵੈਧ ਨਹੀਂ ਹੈ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"ਫ਼ਾਈਲ ਇਨਕ੍ਰਿਪਸ਼ਨ ਵਿੱਚ ਤਬਦੀਲ ਕਰੋ"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"ਤਬਦੀਲ ਕਰੋ ..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"ਫ਼ਾਈਲ ਪਹਿਲਾਂ ਤੋਂ ਇਨਕ੍ਰਿਪਟਡ ਹੈ"</string>
@@ -297,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ਇਹ ਵਿਸ਼ੇਸ਼ਤਾ ਪ੍ਰਯੋਗਾਤਮਿਕ ਹੈ ਅਤੇ ਪ੍ਰਦਰਸ਼ਨ ਤੇ ਅਸਰ ਪਾ ਸਕਦੀ ਹੈ।"</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> ਦੁਆਰਾ ਓਵਰਰਾਈਡ ਕੀਤਾ"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"ਲਗਭਗ <xliff:g id="TIME">%1$s</xliff:g> ਬਾਕੀ"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਲਗਭਗ <xliff:g id="TIME">%2$s</xliff:g> ਬਾਕੀ"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ਪੂਰੀ ਹੋਣ ਤੱਕ"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> AC ਤੇ ਪੂਰਾ ਹੋਣ ਤੱਕ"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB ਤੇ ਪੂਰਾ ਹੋਣ ਤੱਕ"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ਵਾਇਰਲੈਸ ਤੋਂ ਪੂਰਾ ਹੋਣ ਤੱਕ"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"ਅਗਿਆਤ"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"ਚਾਰਜਿੰਗ"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC ਤੇ ਚਾਰਜਿੰਗ"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB ਤੇ ਚਾਰਜਿੰਗ"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"ਵਾਇਰਲੈਸ ਤੌਰ ਤੇ ਚਾਰਜਿੰਗ"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"ਚਾਰਜ ਨਹੀਂ ਹੋ ਰਿਹਾ"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"ਚਾਰਜ ਨਹੀਂ ਹੋ ਰਿਹਾ"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"ਪੂਰੀ"</string>
@@ -317,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"ਮੁੱਖ ਸਕ੍ਰੀਨ"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> ਪਹਿਲਾਂ"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> ਬਾਕੀ"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"ਛੋਟਾ"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"ਪੂਰਵ-ਨਿਰਧਾਰਤ"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"ਵੱਡਾ"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"ਥੋੜ੍ਹਾ ਵੱਡਾ"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"ਸਭ ਤੋਂ ਵੱਡਾ"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"ਵਿਸ਼ੇਸ਼-ਵਿਉਂਤਬੱਧ (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index b4c030d..1e56cfd 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"To jest funkcja eksperymentalna i może wpływać na działanie urządzenia."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Nadpisana przez <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Pozostało około <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – zostało ok. <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do pełnego naładowania"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do pełnego naładowania z gniazdka"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do pełnego naładowania przez USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do pełnego naładowania bezprzewodowo"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Nieznane"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Ładowanie"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Ładowanie zasilaczem"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Ładowanie przez USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Ład. bezprzewodowe"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Nie podłączony"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nie podłączony"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Naładowana"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Ekran główny"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> temu"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Pozostało <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Małe"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Domyślne"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Duże"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Większe"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Największe"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Niestandardowe (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index e76535a..93f333a 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Este recurso é experimental e pode afetar o desempenho."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Substituído por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Aproximadamente <xliff:g id="TIME">%1$s</xliff:g> restante(s)"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - cerca de <xliff:g id="TIME">%2$s</xliff:g> restantes"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até concluir"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até concluir em CA"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até concluir via USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até concluir sem fio"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Desconhecido"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Carregando"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Carregamento CA"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Carregamento via USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Carregamento sem fio"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Não está carregando"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Não está carregando"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Cheio"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Início"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> atrás"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> restante(s)"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Pequena"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Padrão"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Grande"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Muito grande"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Maior"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Personalizada (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 7e4744c..dda00fe 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Esta funcionalidade é experimental e pode afetar o desempenho."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Substituído por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Resta(m) aproximadamente <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – resta(m) aprox. <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> até ficar completa"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> até ficar completa através de CA"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> até ficar completa através de USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até ficar compl. por rede s/ fios"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Desconhecido"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"A carregar"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"A carregar por CA"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"A carregar por USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"A carregar sem fios"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Não está a carregar"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Não está a carregar"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Completo"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Página inicial"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Há <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Resta(m) <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Pequeno"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Predefinição"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Grande"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Maior"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"O maior"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Personalizado (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index e76535a..93f333a 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Este recurso é experimental e pode afetar o desempenho."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Substituído por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Aproximadamente <xliff:g id="TIME">%1$s</xliff:g> restante(s)"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - cerca de <xliff:g id="TIME">%2$s</xliff:g> restantes"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até concluir"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até concluir em CA"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até concluir via USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até concluir sem fio"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Desconhecido"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Carregando"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Carregamento CA"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Carregamento via USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Carregamento sem fio"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Não está carregando"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Não está carregando"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Cheio"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Início"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> atrás"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> restante(s)"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Pequena"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Padrão"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Grande"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Muito grande"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Maior"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Personalizada (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 5b712d0..3c46d41 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Această funcție este experimentală și poate afecta performanțele."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Valoare înlocuită de <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Timp rămas: aproximativ <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – timp rămas: aproximativ <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> până la încărcare completă"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> până la încărcare completă la c.a."</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> până la încărcare completă prin USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> până la încărcare completă wireless"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Necunoscut"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Încarcă"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Se încarcă la C.A."</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Se încarcă prin USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Se încarcă fără fir"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Nu se încarcă"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nu încarcă"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Complet"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Ecranul principal"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Acum <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Timp rămas: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Mic"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Prestabilit"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Mare"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Mai mare"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Cel mai mare"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Personalizat (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index b233c4cb7..f4929de 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Это экспериментальная функция, она может снизить производительность устройства."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Новая настройка: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Осталось примерно <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – осталось около <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до полной зарядки"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до полной зарядки (от сети)"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до полной зарядки (через USB)"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до полной зарядки (беспроводная)"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Неизвестно"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Идет зарядка"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Зарядка от сети"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Зарядка через USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Беспроводная зарядка"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Не заряжается"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Не заряжается"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Батарея заряжена"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Главная"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> назад"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Осталось <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Мелкий"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"По умолчанию"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Крупный"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Очень крупный"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Максимальный"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Другой (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-si-rLK/strings.xml b/packages/SettingsLib/res/values-si-rLK/strings.xml
index c8af144..99f018b 100644
--- a/packages/SettingsLib/res/values-si-rLK/strings.xml
+++ b/packages/SettingsLib/res/values-si-rLK/strings.xml
@@ -278,8 +278,7 @@
<string name="enable_webview_multiprocess_desc" msgid="852226124223847283">"හුදකලා වූ ක්රියාවලියක WebView විදහා දැක්වීම් ධාවනය කරන්න."</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView ක්රියාත්මක කිරීම"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView ක්රියාත්මක කිරීම සකසන්න"</string>
- <!-- no translation found for select_webview_provider_toast_text (5466970498308266359) -->
- <skip />
+ <string name="select_webview_provider_toast_text" msgid="5466970498308266359">"මෙම තෝරා ගැනීම තව දුරටත් වලංගු නැත. නැවත උත්සාහ කරන්න."</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"ගොනු සංකේතනයට පරිවර්තනය කරන්න"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"පරිවර්තනය කරන්න..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"දැනටමත් ගොනුව සංකේතනය කර ඇත"</string>
@@ -297,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"මෙම විශේෂාංගය පරීක්ෂණාත්මක සහ ඇතැම් විට ක්රියාකාරිත්වයට බලපෑ හැක."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> මගින් ඉක්මවන ලදී"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"දළ වශයෙන් <xliff:g id="TIME">%1$s</xliff:g>ක් ඉතිරිය"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - ආසන්න <xliff:g id="TIME">%2$s</xliff:g> වම"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> සම්පුර්ණ වන තෙක්"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"AC හි <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> සම්පුර්ණ වන තෙක්"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"USB හරහ <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> සම්පුර්ණ වන තෙක්"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"රේඩියෝව වෙතින් <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> සම්පූර්ණ වන තෙක්"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"නොදනී"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"ආරෝපණය වෙමින්"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC හි ආරෝපණය වෙමින්"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB හරහා ආරෝපණය වෙමින්"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"රැහැන් රහිතව ආරෝපණය වෙමින්"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"ආරෝපණය නොවේ"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"ආරෝපණය නොවෙමින්"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"පූර්ණ"</string>
@@ -317,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"මුල් පිටුව"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g>කට පෙර"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g>ක් ඉතිරිය"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"කුඩා"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"පෙරනිමි"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"විශාල"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"වඩා විශාල"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"විශාලතම"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"අභිරුචි (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 2c7a3b7..4dffdef 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Funkcia je experimentálna a môže mať vplyv na výkonnosť."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Prekonané predvoľbou <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Zostáva cca. <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – zostáva približne <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabitia"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabitia zo zásuvky"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabitia cez USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabitia bezdrôtovo"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Neznáme"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Nabíjanie"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Nabíjanie zo zásuvky"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Nabíjanie cez USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Bezdrôtové nabíjanie"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Nenabíja sa"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nenabíja sa"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Nabitá"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Domov"</string>
<string name="charge_length_format" msgid="8978516217024434156">"pred <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Zostáva <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Malé"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Predvolená"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Veľké"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Väčšie"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Najväčšie"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Vlastné (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 62797c3..676a3ab 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"To je preskusna funkcija in lahko vpliva na učinkovitost delovanja."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Preglasila nastavitev: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Še približno <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – še približno <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napolnjenosti"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napolnjenosti prek napajalnika"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napolnjenosti prek USB-ja"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napolnjenosti prek brezž. pol."</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Neznano"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Polnjenje"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Polnj. prek iz. toka"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Polnj. prek USB-ja"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Brezžično polnjenje"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Se ne polni"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Se ne polni"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Poln"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Začetni zaslon"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Pred toliko časa: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Še <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Majhno"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Privzeto"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Veliko"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Večje"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Največje"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Po meri (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sq-rAL/strings.xml b/packages/SettingsLib/res/values-sq-rAL/strings.xml
index 0a069de..c18e639 100644
--- a/packages/SettingsLib/res/values-sq-rAL/strings.xml
+++ b/packages/SettingsLib/res/values-sq-rAL/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ky funksion është eksperimental dhe mund të ndikojë në veprimtari."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Mbivendosur nga <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Afërsisht <xliff:g id="TIME">%1$s</xliff:g> të mbetura"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - afërsisht <xliff:g id="TIME">%2$s</xliff:g> të mbetura"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> derisa të jetë e plotë"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> deri sa të mbushet në AC"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> deri sa të mbushet me USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> deri sa të mbushet nga lidhja pa tel"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"I panjohur"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Po ngarkohet"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Po ngarkohet në AC"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Po ngarkohet me USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Po ngarkohet me valë"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Nuk po ngarkohet"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nuk po ngarkohet"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"E mbushur"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Kreu"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> më parë"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> të mbetura"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"I vogël"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"I parazgjedhur"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"I madh"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Më i madh"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Më i madhi"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"I personalizuar (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 61a87b5..c0791fc 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ова функција је експериментална и може да утиче на перформансе."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Замењује га <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Још отприлике <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – преостало око <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> док се не напуни"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> док се не напуни пуњачем"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> док се не напуни преко USB-а"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> док се не напуни бежично"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Непознато"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Пуњење"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Пуњење преко пуњача"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Пуњење преко USB-а"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Бежично пуњење"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Не пуни се"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Не пуни се"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Пуно"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Почетни"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Пре <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Још <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Мали"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Подразумевано"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Велики"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Већи"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Највећи"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Прилагођени (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 1de7317..60c42a5 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Den här funktionen är experimentell och kan påverka prestandan."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Har åsidosatts av <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Ca <xliff:g id="TIME">%1$s</xliff:g> kvar"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – ca <xliff:g id="TIME">%2$s</xliff:g> kvar"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> till fulladdat"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> till fulladdat via laddare"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> till fulladdat via USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> till fulladdat via trådlös laddning"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Okänd"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Laddar"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Laddas via adapter"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Laddas via USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Laddas trådlöst"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Laddar inte"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Laddar inte"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Fullt"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Startsida"</string>
<string name="charge_length_format" msgid="8978516217024434156">"för <xliff:g id="ID_1">%1$s</xliff:g> sedan"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> kvar"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Små"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Standardinställning"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Stora"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Större"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Störst"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Anpassad (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index c02c256..5de7686 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Kipengele hiki ni cha majaribio na huenda kikaathiri utendaji."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Imetanguliwa na <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Zimesalia takribani <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - imesalia takriban <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - imesalia <xliff:g id="TIME">%2$s</xliff:g> hadi ijae"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - imesalia <xliff:g id="TIME">%2$s</xliff:g> hadi ijae kwa kutumia AC"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g>%% - imesalia <xliff:g id="TIME">%2$s</xliff:g> hadi ijae kwa kutumia USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - imesalia <xliff:g id="TIME">%2$s</xliff:g> hadi ijae kwa isiyotumia waya"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Haijulikani"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Inachaji"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Inachaji kupitia AC"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Inachaji kupitia USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Inachaji bila kutumia waya"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Haichaji"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Haichaji"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Imejaa"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Mwanzo"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Zimepita <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Zimesalia <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Ndogo"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Chaguo-msingi"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Kubwa"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Kubwa kiasi"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Kubwa zaidi"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Kiwango maalum (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ta-rIN/strings.xml b/packages/SettingsLib/res/values-ta-rIN/strings.xml
index d28fe75..a5f04d0 100644
--- a/packages/SettingsLib/res/values-ta-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-ta-rIN/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"இது சோதனை முறையிலான அம்சம், இது செயல்திறனைப் பாதிக்கலாம்."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> மூலம் மேலெழுதப்பட்டது"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"தோராயமாக <xliff:g id="TIME">%1$s</xliff:g> உள்ளது"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"தோராயம்: <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> உள்ளது"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"முழு சார்ஜிற்கு: <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"முழு AC சார்ஜிற்கு: <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"முழு USB சார்ஜிற்கு: <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"முழு வயர்லெஸ் சார்ஜிற்கு: <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"அறியப்படாத"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"சார்ஜ் ஏற்றப்படுகிறது"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC மூலம் சார்ஜாகிறது"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB மூலம் சார்ஜாகிறது"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"வயர்லெஸில் சார்ஜாகிறது"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"சார்ஜ் செய்யப்படவில்லை"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"சார்ஜ் ஏறவில்லை"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"முழுமை"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"முகப்பு"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> முன்"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> உள்ளது"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"சிறியது"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"இயல்பு"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"பெரியது"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"கொஞ்சம் பெரியது"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"மிகப் பெரியது"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"தனிப்பயன் (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-te-rIN/strings.xml b/packages/SettingsLib/res/values-te-rIN/strings.xml
index 9b6694c..fe40766 100644
--- a/packages/SettingsLib/res/values-te-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-te-rIN/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ఈ లక్షణం ప్రయోగాత్మకమైనది మరియు పనితీరుపై ప్రభావం చూపవచ్చు."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> ద్వారా భర్తీ చేయబడింది"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"సుమారు <xliff:g id="TIME">%1$s</xliff:g> మిగిలి ఉంది"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - సుమారు <xliff:g id="TIME">%2$s</xliff:g> మిగిలి ఉంది"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - పూర్తిగా నిండటానికి <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - ACలో పూర్తిగా నిండటానికి <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - USB ద్వారా పూర్తిగా నిండటానికి <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - వైర్లెస్ నుండి పూర్తిగా నిండటానికి <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"తెలియదు"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"ఛార్జ్ అవుతోంది"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"ACలో ఛార్జ్ అవుతోంది"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB ద్వారా ఛార్జ్ అవుతోంది"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"వైర్లెస్ ద్వారా ఛార్జ్ అవుతోంది"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"ఛార్జ్ కావడం లేదు"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"ఛార్జ్ కావడం లేదు"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"నిండింది"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"హోమ్"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> క్రితం"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> మిగిలి ఉంది"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"చిన్నగా"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"డిఫాల్ట్"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"పెద్దగా"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"చాలా పెద్దగా"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"అతి పెద్దగా"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"అనుకూలం (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 5880b98..c58692f 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -278,8 +278,7 @@
<string name="enable_webview_multiprocess_desc" msgid="852226124223847283">"เรียกใช้โหมดแสดงภาพ WebView ในการดำเนินการที่แยกออกมา"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"การใช้งาน WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"ตั้งค่าการใช้งาน WebView"</string>
- <!-- no translation found for select_webview_provider_toast_text (5466970498308266359) -->
- <skip />
+ <string name="select_webview_provider_toast_text" msgid="5466970498308266359">"ตัวเลือกนี้ใช้ไม่ได้อีกต่อไป โปรดลองอีกครั้ง"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"แปลงเป็นการเข้ารหัสไฟล์"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"แปลง…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"เข้ารหัสไฟล์แล้ว"</string>
@@ -297,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"คุณลักษณะนี้เป็นแบบทดลองและอาจส่งผลต่อประสิทธิภาพการทำงาน"</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"แทนที่โดย <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"เหลืออีกประมาณ <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - เหลือประมาณ <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> จนกว่าจะเต็ม"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> จนกว่าจะเต็มเมื่อชาร์จผ่าน AC"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> จนกว่าจะเต็มเมื่อชาร์จผ่าน USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> จนกว่าจะเต็มเมื่อชาร์จผ่านระบบไร้สาย"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"ไม่ทราบ"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"กำลังชาร์จ"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"กำลังชาร์จไฟ AC"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"กำลังชาร์จผ่าน USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"กำลังชาร์จแบบไร้สาย"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"ไม่ได้ชาร์จ"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"ไม่ได้ชาร์จ"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"เต็ม"</string>
@@ -317,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"หน้าแรก"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g>ที่ผ่านมา"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"เหลือ <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"เล็ก"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"ค่าเริ่มต้น"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"ใหญ่"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"ใหญ่ขึ้น"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"ใหญ่ที่สุด"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"กำหนดเอง (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 3764d22..093256c 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ang feature na ito ay pinag-eeksperimentuhan at maaaring makaapekto sa pagganap."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Na-override ng <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Humigit-kumulang <xliff:g id="TIME">%1$s</xliff:g> na lang ang natitira"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - humigit kumulang <xliff:g id="TIME">%2$s</xliff:g> ang natitira"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> bago mapuno"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> bago mapuno sa AC"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> bago mapuno sa USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> bago mapuno mula sa wireless"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Hindi Kilala"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Nagcha-charge"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Nagcha-charge sa AC"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Nagcha-charge sa USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Wireless nag-charge"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Hindi nagcha-charge"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Hindi nagkakarga"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Puno"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Home"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> na ang nakalipas"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> na lang"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Maliit"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Default"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Malaki"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Mas malaki"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Pinakamalaki"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Custom (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 0a2c654..1947d39 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Bu özellik deneyseldir ve performansı etkileyebilir."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> tarafından geçersiz kılındı"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Yaklaşık <xliff:g id="TIME">%1$s</xliff:g> kaldı"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - yaklaşık <xliff:g id="TIME">%2$s</xliff:g> kaldı"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - tam şarj olmasına <xliff:g id="TIME">%2$s</xliff:g> var"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - prize takılı, tam şarj olmasına <xliff:g id="TIME">%2$s</xliff:g> var"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - USB üzerinden şarj olmasına <xliff:g id="TIME">%2$s</xliff:g> var"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - kablosuzdan tam şarj olmasına <xliff:g id="TIME">%2$s</xliff:g> var"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Bilinmiyor"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Şarj oluyor"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC ile şarj oluyor"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB ile şarj oluyor"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Kablosuz şarj oluyor"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Şarj olmuyor"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Şarj etmiyor"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Dolu"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Ana Ekran"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> önce"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> kaldı"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Küçük"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Varsayılan"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Büyük"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Daha büyük"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"En büyük"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Özel (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 5b88647..0ed6cc0 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Це експериментальна функція. Вона може вплинути на продуктивність."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Замінено на <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Залишилося приблизно <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – залишилось близько <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до повного зарядження"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до повного зарядження з розетки"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до повного зарядження через USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до повного з бездротового зарядження"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Невідомо"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Зарядж-ся"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Заряджання з розетки"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Заряджання через USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Заряджання без дроту"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Не заряджається"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Не заряджається"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Акумулятор заряджено"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Головний екран"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> тому"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Залишилося <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Малі елементи"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"За умовчанням"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Великі елементи"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Більші елементи"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Найбільші елементи"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Спеціальний масштаб (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ur-rPK/strings.xml b/packages/SettingsLib/res/values-ur-rPK/strings.xml
index e066736..99a0f2b 100644
--- a/packages/SettingsLib/res/values-ur-rPK/strings.xml
+++ b/packages/SettingsLib/res/values-ur-rPK/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"یہ خصوصیت تجرباتی ہے اور اس کی وجہ سے کاکردگی متاثر ہو سکتی ہے۔"</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> کے ذریعہ منسوخ کردیا گیا"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"تقریبا <xliff:g id="TIME">%1$s</xliff:g> باقی ہیں"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - تقریبا <xliff:g id="TIME">%2$s</xliff:g> باقی"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> پورا ہونے تک"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> AC پر پورا ہونے تک"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB پر پورا ہونے تک"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> وائرلیس سے پورا ہونے تک"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"نامعلوم"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"چارج ہو رہا ہے"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC پر چارج ہو رہی ہے"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB پر چارج ہورہی ہے"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"وائرلیس چارجنگ"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"چارج نہیں ہو رہا ہے"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"چارج نہیں ہو رہا ہے"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"مکمل"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"ہوم"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> قبل"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> باقی ہیں"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"چھوٹا"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"ڈیفالٹ"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"بڑا"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"قدرے بڑا"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"سب سے بڑا"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"حسب ضرورت (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-uz-rUZ/strings.xml b/packages/SettingsLib/res/values-uz-rUZ/strings.xml
index 23e8b0f..b9c0243 100644
--- a/packages/SettingsLib/res/values-uz-rUZ/strings.xml
+++ b/packages/SettingsLib/res/values-uz-rUZ/strings.xml
@@ -278,8 +278,7 @@
<string name="enable_webview_multiprocess_desc" msgid="852226124223847283">"WebView renderlovchilarini alohida jarayonda ishga tushirish."</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView ta’minotchisi"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView ta’minotchisini sozlash"</string>
- <!-- no translation found for select_webview_provider_toast_text (5466970498308266359) -->
- <skip />
+ <string name="select_webview_provider_toast_text" msgid="5466970498308266359">"Bu variant endi yaroqsiz. Qaytadan urining."</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Faylli shifrga o‘girish"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"O‘girish…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Fayl allaqachon shifrlangan"</string>
@@ -297,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Bu funksiya tajribaviy bo‘lib, u qurilma unumdorligiga ta’sir qilishi mumkin."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> bilan almashtirildi"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Taxminan <xliff:g id="TIME">%1$s</xliff:g> qoldi"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – taxminan <xliff:g id="TIME">%2$s</xliff:g> qoldi"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>, to‘lguncha"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>, o‘zgaruvchan tok orqali to‘lguncha"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>, USB orqali to‘lguncha"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>, simsiz quvvatlash orqali to‘lguncha"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Noma’lum"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Quvvat olmoqda"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Quvvat olmoqda (AC)"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Quvvat olmoqda (USB)"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Simsiz quvvat olmoqda"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Quvvat olmayapti"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Quvvatlanmayapti"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"To‘la"</string>
@@ -317,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Bosh ekran"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> oldin"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> qoldi"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Kichkina"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Birlamchi"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Katta"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Kattaroq"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Eng katta"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Moslashtirilgan (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 27dd482..0c3dc89 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Tính năng này là tính năng thử nghiệm và có thể ảnh hưởng đến hoạt động."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Bị ghi đè bởi <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Còn khoảng <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - còn khoảng <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> cho đến khi đầy"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> cho đến khi đầy khi cắm vào nguồn AC"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> cho đến khi đầy qua USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> cho đến khi đầy từ không dây"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Không xác định"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Đang sạc"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Sạc trên AC"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Sạc qua USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Sạc không dây"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Hiện không sạc"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Hiện không sạc"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Đầy"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Màn hình chính"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> trước"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Còn <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Nhỏ"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Mặc định"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Lớn"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Lớn hơn"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Lớn nhất"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Tùy chỉnh (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 73cd252..2b8ce65 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"此功能为实验性功能,可能会影响性能。"</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"已被“<xliff:g id="TITLE">%1$s</xliff:g>”覆盖"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"还剩大约 <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - 还可用大约<xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - 还需<xliff:g id="TIME">%2$s</xliff:g>充满"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - 还需<xliff:g id="TIME">%2$s</xliff:g>充满(交流电充电)"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - 还需<xliff:g id="TIME">%2$s</xliff:g>充满(USB充电)"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - 还需<xliff:g id="TIME">%2$s</xliff:g>充满(无线充电)"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"未知"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"正在充电"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"正在通过交流电源充电"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"正在通过USB充电"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"正在无线充电"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"未在充电"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"未在充电"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"电量充足"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"主屏幕"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g>前"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"还剩 <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"小"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"默认"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"大"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"较大"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"最大"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"自定义 (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 944082f..8dd0417 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"這是一項實驗性功能,可能會影響效能。"</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"已由「<xliff:g id="TITLE">%1$s</xliff:g>」覆寫"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"尚餘大約 <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - 尚餘大約 <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 後完成充電"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 後完成充電 (透過插頭充電)"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 後完成充電 (透過 USB 充電)"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 後完成充電 (無線充電)"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"未知"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"充電中"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"正在透過 AC 充電"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"正在透過 USB 充電"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"正在透過無線方式充電"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"非充電中"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"未開始充電"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"電量已滿"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"主畫面"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g>前"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"尚餘 <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"小"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"預設"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"大"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"較大"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"最大"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"自訂 (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index dc029d9..dd59954 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"這是一項實驗性功能,可能會對效能造成影響。"</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"已改為<xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"還剩大約 <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - 大約還剩 <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充飽"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充飽 (AC)"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充飽 (USB)"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充飽 (無線充電)"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"不明"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"充電中"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"正在透過 AC 變壓器充電"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"正在透過 USB 充電"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"正在透過無線方式充電"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"非充電中"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"非充電中"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"電力充足"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"主畫面"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g>前"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"還剩 <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"小"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"預設"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"大"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"較大"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"最大"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"自訂 (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 9ba957a..610cac4 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -296,17 +296,35 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Lesi sici esesilingo futhi singathinta ukusebenza."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Igitshezwe ngaphezulu yi-<xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="4400068916452346544">"Cishe ngu-<xliff:g id="TIME">%1$s</xliff:g> osele"</string>
+ <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+ <skip />
<string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - isilinganiso esingu-<xliff:g id="TIME">%2$s</xliff:g> esisele"</string>
+ <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+ <skip />
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> kuze igcwale"</string>
+ <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+ <skip />
<string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> kuze igcwale ku-AC"</string>
+ <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+ <skip />
<string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> kuze igcwale ngaphezulu kwe-USB"</string>
+ <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+ <skip />
<string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> kuze igcwale kusukela kokungenantambo"</string>
+ <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="196130600938058547">"Akwaziwa"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Iyashaja"</string>
<string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Iyashaja ku-AC"</string>
+ <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+ <skip />
<string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Iyashaja ngaphezulu kwe-USB"</string>
+ <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+ <skip />
<string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Iyashaja ngaphandle kwentambo"</string>
+ <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+ <skip />
<string name="battery_info_status_discharging" msgid="310932812698268588">"Ayishaji"</string>
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ayishaji"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Kugcwele"</string>
@@ -316,4 +334,10 @@
<string name="home" msgid="8263346537524314127">"Ekhaya"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> edlule"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> osele"</string>
+ <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Okuncane"</string>
+ <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Okuzenzakalelayo"</string>
+ <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Okukhulu"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Okukhulu kakhulu"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Okukhulu kakhulu"</string>
+ <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Ngokwezifiso (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
</resources>
diff --git a/core/java/com/android/internal/app/ProcessStats.aidl b/packages/SettingsLib/res/values/config.xml
old mode 100644
new mode 100755
similarity index 71%
copy from core/java/com/android/internal/app/ProcessStats.aidl
copy to packages/SettingsLib/res/values/config.xml
index 48b1f85..299a5b7
--- a/core/java/com/android/internal/app/ProcessStats.aidl
+++ b/packages/SettingsLib/res/values/config.xml
@@ -1,5 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
/*
-** Copyright 2013, The Android Open Source Project
+** Copyright 2016, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
@@ -13,7 +15,8 @@
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-
-package com.android.internal.app;
-
-parcelable ProcessStats;
+-->
+<resources>
+ <!-- Configuration for automotive -->
+ <bool name="enable_pbap_pce_profile">false</bool>
+</resources>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index a2a3fd3..a560e3c 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -733,25 +733,44 @@
<!-- [CHAR_LIMIT=40] Label for estimated remaining duration of battery charging/discharging -->
<string name="power_remaining_duration_only">Approx. <xliff:g id="time">%1$s</xliff:g> left</string>
+ <!-- [CHAR_LIMIT=40] Short label for estimated remaining duration of battery charging/discharging -->
+ <string name="power_remaining_duration_only_short"><xliff:g id="time">%1$s</xliff:g> left</string>
+
<!-- [CHAR_LIMIT=40] Label for battery level chart when discharging with duration -->
<string name="power_discharging_duration"><xliff:g id="level">%1$s</xliff:g>
- approx. <xliff:g id="time">%2$s</xliff:g> left</string>
+ <!-- [CHAR_LIMIT=40] Label for battery level chart when discharging with duration -->
+ <string name="power_discharging_duration_short"><xliff:g id="level">%1$s</xliff:g>
+ - <xliff:g id="time">%2$s</xliff:g> left</string>
+
<!-- [CHAR_LIMIT=40] Label for battery level chart when charging -->
<string name="power_charging"><xliff:g id="level">%1$s</xliff:g> -
<xliff:g id="state">%2$s</xliff:g></string>
<!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
<string name="power_charging_duration"><xliff:g id="level">%1$s</xliff:g> -
<xliff:g id="time">%2$s</xliff:g> until full</string>
+ <!-- [CHAR_LIMIT=40] Short label for battery level chart when charging with duration -->
+ <string name="power_charging_duration_short"><xliff:g id="level">%1$s</xliff:g> -
+ <xliff:g id="time">%2$s</xliff:g></string>
<!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
<string name="power_charging_duration_ac"><xliff:g id="level">%1$s</xliff:g> -
<xliff:g id="time">%2$s</xliff:g> until full on AC</string>
+ <!-- [CHAR_LIMIT=40] Short label for battery level chart when charging with duration -->
+ <string name="power_charging_duration_ac_short"><xliff:g id="level">%1$s</xliff:g> -
+ <xliff:g id="time">%2$s</xliff:g></string>
<!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
<string name="power_charging_duration_usb"><xliff:g id="level">%1$s</xliff:g> -
<xliff:g id="time">%2$s</xliff:g> until full over USB</string>
+ <!-- [CHAR_LIMIT=40] Short label for battery level chart when charging with duration -->
+ <string name="power_charging_duration_usb_short"><xliff:g id="level">%1$s</xliff:g> -
+ <xliff:g id="time">%2$s</xliff:g></string>
<!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
<string name="power_charging_duration_wireless"><xliff:g id="level">%1$s</xliff:g> -
<xliff:g id="time">%2$s</xliff:g> until full from wireless</string>
+ <!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
+ <string name="power_charging_duration_wireless_short"><xliff:g id="level">%1$s</xliff:g> -
+ <xliff:g id="time">%2$s</xliff:g></string>
<!-- Battery Info screen. Value for a status item. Used for diagnostic info screens, precise translation isn't needed -->
<string name="battery_info_status_unknown">Unknown</string>
@@ -759,10 +778,16 @@
<string name="battery_info_status_charging">Charging</string>
<!-- [CHAR_LIMIT=20] Battery use screen. Battery status shown in chart label when charging on AC. -->
<string name="battery_info_status_charging_ac">Charging on AC</string>
+ <!-- [CHAR_LIMIT=20] Battery short status label when charing on AC -->
+ <string name="battery_info_status_charging_ac_short">Charging</string>
<!-- [CHAR_LIMIT=20] Battery use screen. Battery status shown in chart label when charging over USB. -->
<string name="battery_info_status_charging_usb">Charging over USB</string>
+ <!-- [CHAR_LIMIT=20] Battery short status label when charging over USB. -->
+ <string name="battery_info_status_charging_usb_short">Charging</string>
<!-- [CHAR_LIMIT=20] Battery use screen. Battery status shown in chart label when charging over a wireless connection. -->
<string name="battery_info_status_charging_wireless">Charging wirelessly</string>
+ <!-- [CHAR_LIMIT=20] Battery short status label when charging wirelessly. -->
+ <string name="battery_info_status_charging_wireless_short">Charging</string>
<!-- Battery Info screen. Value for a status item. Used for diagnostic info screens, precise translation isn't needed -->
<string name="battery_info_status_discharging">Not charging</string>
<!-- Battery Info screen. Value for a status item. Used for diagnostic info screens, precise translation isn't needed -->
@@ -812,4 +837,18 @@
<!-- HTTP proxy settings. The hint text field for the hostname. -->
<string name="proxy_hostname_hint" translatable="false">proxy.example.com</string>
+ <!-- Description for the screen zoom level that makes interface elements small. [CHAR LIMIT=24] -->
+ <string name="screen_zoom_summary_small">Small</string>
+ <!-- Description for the device's default screen zoom level. [CHAR LIMIT=24] -->
+ <string name="screen_zoom_summary_default">Default</string>
+ <!-- Description for the screen zoom level that makes interface elements large. [CHAR LIMIT=24] -->
+ <string name="screen_zoom_summary_large">Large</string>
+ <!-- Description for the screen zoom level that makes interface elements larger. [CHAR LIMIT=24] -->
+ <string name="screen_zoom_summary_very_large">Larger</string>
+ <!-- Description for the screen zoom level that makes interface elements largest. [CHAR LIMIT=24] -->
+ <string name="screen_zoom_summary_extremely_large">Largest</string>
+ <!-- Description for a custom screen zoom level. This shows the requested display
+ density in raw pixels per inch rather than using a relative description. [CHAR LIMIT=24] -->
+ <string name="screen_zoom_summary_custom">Custom (<xliff:g id="densityDpi" example="160">%d</xliff:g>)</string>
+
</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/BatteryInfo.java b/packages/SettingsLib/src/com/android/settingslib/BatteryInfo.java
index 6b29e21..fa1f91f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/BatteryInfo.java
+++ b/packages/SettingsLib/src/com/android/settingslib/BatteryInfo.java
@@ -25,6 +25,7 @@
import android.os.Bundle;
import android.os.SystemClock;
import android.text.format.Formatter;
+import android.util.Log;
import android.util.SparseIntArray;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settingslib.graph.UsageView;
@@ -95,6 +96,11 @@
}
public static void getBatteryInfo(final Context context, final Callback callback) {
+ BatteryInfo.getBatteryInfo(context, callback, false);
+ }
+
+ public static void getBatteryInfo(final Context context, final Callback callback,
+ boolean shortString) {
new AsyncTask<Void, Void, BatteryStats>() {
@Override
protected BatteryStats doInBackground(Void... params) {
@@ -109,14 +115,20 @@
Intent batteryBroadcast = context.registerReceiver(null,
new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
BatteryInfo batteryInfo = BatteryInfo.getBatteryInfo(context,
- batteryBroadcast, batteryStats, elapsedRealtimeUs);
+ batteryBroadcast, batteryStats, elapsedRealtimeUs, shortString);
callback.onBatteryInfoLoaded(batteryInfo);
}
}.execute();
}
public static BatteryInfo getBatteryInfo(Context context, Intent batteryBroadcast,
- BatteryStats stats, long elapsedRealtimeUs) {
+ BatteryStats stats, long elapsedRealtimeUs) {
+ return BatteryInfo.getBatteryInfo(context, batteryBroadcast, stats, elapsedRealtimeUs,
+ false);
+ }
+
+ public static BatteryInfo getBatteryInfo(Context context, Intent batteryBroadcast,
+ BatteryStats stats, long elapsedRealtimeUs, boolean shortString) {
BatteryInfo info = new BatteryInfo();
info.mStats = stats;
info.mBatteryLevel = Utils.getBatteryLevel(batteryBroadcast);
@@ -129,9 +141,13 @@
info.remainingTimeUs = drainTime;
String timeString = Formatter.formatShortElapsedTime(context,
drainTime / 1000);
- info.remainingLabel = resources.getString(R.string.power_remaining_duration_only,
+ info.remainingLabel = resources.getString(
+ shortString ? R.string.power_remaining_duration_only_short
+ : R.string.power_remaining_duration_only,
timeString);
- info.mChargeLabelString = resources.getString(R.string.power_discharging_duration,
+ info.mChargeLabelString = resources.getString(
+ shortString ? R.string.power_discharging_duration_short
+ : R.string.power_discharging_duration,
info.batteryPercentString, timeString);
} else {
info.remainingLabel = null;
@@ -140,7 +156,7 @@
} else {
final long chargeTime = stats.computeChargeTimeRemaining(elapsedRealtimeUs);
final String statusLabel = Utils.getBatteryStatus(
- resources, batteryBroadcast);
+ resources, batteryBroadcast, shortString);
final int status = batteryBroadcast.getIntExtra(BatteryManager.EXTRA_STATUS,
BatteryManager.BATTERY_STATUS_UNKNOWN);
if (chargeTime > 0 && status != BatteryManager.BATTERY_STATUS_FULL) {
@@ -151,13 +167,17 @@
int plugType = batteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
int resId;
if (plugType == BatteryManager.BATTERY_PLUGGED_AC) {
- resId = R.string.power_charging_duration_ac;
+ resId = shortString ? R.string.power_charging_duration_ac_short
+ : R.string.power_charging_duration_ac;
} else if (plugType == BatteryManager.BATTERY_PLUGGED_USB) {
- resId = R.string.power_charging_duration_usb;
+ resId = shortString ? R.string.power_charging_duration_usb_short
+ : R.string.power_charging_duration_usb;
} else if (plugType == BatteryManager.BATTERY_PLUGGED_WIRELESS) {
- resId = R.string.power_charging_duration_wireless;
+ resId = shortString ? R.string.power_charging_duration_wireless_short
+ : R.string.power_charging_duration_wireless;
} else {
- resId = R.string.power_charging_duration;
+ resId = shortString ? R.string.power_charging_duration_short
+ : R.string.power_charging_duration;
}
info.remainingLabel = resources.getString(R.string.power_remaining_duration_only,
timeString);
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
index 1f1a9b8..e86ca82 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
@@ -78,7 +78,8 @@
int deviceOwnerUserId = dpm.getDeviceOwnerUserId();
boolean enforcedByDeviceOwner = false;
if (deviceOwner != null && deviceOwnerUserId != UserHandle.USER_NULL) {
- Bundle enforcedRestrictions = dpm.getUserRestrictions(deviceOwner, deviceOwnerUserId);
+ Bundle enforcedRestrictions =
+ dpm.getUserRestrictionsForUser(deviceOwner, deviceOwnerUserId);
if (enforcedRestrictions != null
&& enforcedRestrictions.getBoolean(userRestriction, false)) {
enforcedByDeviceOwner = true;
@@ -90,7 +91,8 @@
if (userId != UserHandle.USER_NULL) {
profileOwner = dpm.getProfileOwnerAsUser(userId);
if (profileOwner != null) {
- Bundle enforcedRestrictions = dpm.getUserRestrictions(profileOwner, userId);
+ Bundle enforcedRestrictions =
+ dpm.getUserRestrictionsForUser(profileOwner, userId);
if (enforcedRestrictions != null
&& enforcedRestrictions.getBoolean(userRestriction, false)) {
enforcedByProfileOwner = true;
@@ -234,7 +236,7 @@
if (ipm.isPackageSuspendedForUser(packageName, userId)) {
return getProfileOrDeviceOwner(context, userId);
}
- } catch (RemoteException e) {
+ } catch (RemoteException | IllegalArgumentException e) {
// Nothing to do
}
return null;
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 74c1ebd..586f269 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -112,20 +112,26 @@
}
public static String getBatteryStatus(Resources res, Intent batteryChangedIntent) {
- final Intent intent = batteryChangedIntent;
+ return Utils.getBatteryStatus(res, batteryChangedIntent, false);
+ }
- int plugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
- int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
+ public static String getBatteryStatus(Resources res, Intent batteryChangedIntent,
+ boolean shortString) {
+ int plugType = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
+ int status = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_STATUS,
BatteryManager.BATTERY_STATUS_UNKNOWN);
String statusString;
if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
int resId;
if (plugType == BatteryManager.BATTERY_PLUGGED_AC) {
- resId = R.string.battery_info_status_charging_ac;
+ resId = shortString ? R.string.battery_info_status_charging_ac_short
+ : R.string.battery_info_status_charging_ac;
} else if (plugType == BatteryManager.BATTERY_PLUGGED_USB) {
- resId = R.string.battery_info_status_charging_usb;
+ resId = shortString ? R.string.battery_info_status_charging_usb_short
+ : R.string.battery_info_status_charging_usb;
} else if (plugType == BatteryManager.BATTERY_PLUGGED_WIRELESS) {
- resId = R.string.battery_info_status_charging_wireless;
+ resId = shortString ? R.string.battery_info_status_charging_wireless_short
+ : R.string.battery_info_status_charging_wireless;
} else {
resId = R.string.battery_info_status_charging;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 6226b23..6052ccd 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -24,12 +24,14 @@
import android.bluetooth.BluetoothMap;
import android.bluetooth.BluetoothInputDevice;
import android.bluetooth.BluetoothPan;
+import android.bluetooth.BluetoothPbapClient;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothUuid;
import android.content.Context;
import android.content.Intent;
import android.os.ParcelUuid;
import android.util.Log;
+import com.android.settingslib.R;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -82,7 +84,9 @@
private final HidProfile mHidProfile;
private OppProfile mOppProfile;
private final PanProfile mPanProfile;
+ private PbapClientProfile mPbapClientProfile;
private final PbapServerProfile mPbapProfile;
+ private final boolean mUsePbapPce;
/**
* Mapping from profile name, e.g. "HEADSET" to profile object.
@@ -99,6 +103,7 @@
mLocalAdapter = adapter;
mDeviceManager = deviceManager;
mEventManager = eventManager;
+ mUsePbapPce = mContext.getResources().getBoolean(R.bool.enable_pbap_pce_profile);
// pass this reference to adapter and event manager (circular dependency)
mLocalAdapter.setProfileManager(this);
mEventManager.setProfileManager(this);
@@ -205,9 +210,24 @@
} else if (mOppProfile != null) {
Log.w(TAG, "Warning: OPP profile was previously added but the UUID is now missing.");
}
+
+ //PBAP Client
+ if (mUsePbapPce) {
+ if (mPbapClientProfile == null) {
+ if(DEBUG) Log.d(TAG, "Adding local PBAP Client profile");
+ mPbapClientProfile = new PbapClientProfile(mContext, mLocalAdapter, mDeviceManager,
+ this);
+ addProfile(mPbapClientProfile, PbapClientProfile.NAME,
+ BluetoothPbapClient.ACTION_CONNECTION_STATE_CHANGED);
+ }
+ } else if (mPbapClientProfile != null) {
+ Log.w(TAG,
+ "Warning: PBAP Client profile was previously added but the UUID is now missing.");
+ }
+
mEventManager.registerProfileIntentReceiver();
- // There is no local SDP record for HID and Settings app doesn't control PBAP
+ // There is no local SDP record for HID and Settings app doesn't control PBAP Server.
}
private final Collection<ServiceListener> mServiceListeners =
@@ -351,6 +371,10 @@
}
}
+ public PbapClientProfile getPbapClientProfile() {
+ return mPbapClientProfile;
+ }
+
public PbapServerProfile getPbapProfile(){
return mPbapProfile;
}
@@ -430,6 +454,12 @@
removedProfiles.remove(mMapProfile);
mMapProfile.setPreferred(device, true);
}
- }
+ if (mUsePbapPce) {
+ profiles.add(mPbapClientProfile);
+ removedProfiles.remove(mPbapClientProfile);
+ profiles.remove(mPbapProfile);
+ removedProfiles.add(mPbapProfile);
+ }
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
new file mode 100755
index 0000000..d7c9eab
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothPbapClient;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.content.Context;
+import android.os.ParcelUuid;
+import android.util.Log;
+
+import com.android.settingslib.R;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+final class PbapClientProfile implements LocalBluetoothProfile {
+ private static final String TAG = "PbapClientProfile";
+ private static boolean V = false;
+
+ private BluetoothPbapClient mService;
+ private boolean mIsProfileReady;
+
+ private final LocalBluetoothAdapter mLocalAdapter;
+ private final CachedBluetoothDeviceManager mDeviceManager;
+
+ static final ParcelUuid[] SRC_UUIDS = {
+ BluetoothUuid.PBAP_PSE,
+ };
+
+ static final String NAME = "PbapClient";
+ private final LocalBluetoothProfileManager mProfileManager;
+
+ // Order of this profile in device profiles list
+ private static final int ORDINAL = 6;
+
+ // These callbacks run on the main thread.
+ private final class PbapClientServiceListener
+ implements BluetoothProfile.ServiceListener {
+
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ if (V) {
+ Log.d(TAG,"Bluetooth service connected");
+ }
+ mService = (BluetoothPbapClient) proxy;
+ // We just bound to the service, so refresh the UI for any connected PBAP devices.
+ List<BluetoothDevice> deviceList = mService.getConnectedDevices();
+ while (!deviceList.isEmpty()) {
+ BluetoothDevice nextDevice = deviceList.remove(0);
+ CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice);
+ // we may add a new device here, but generally this should not happen
+ if (device == null) {
+ Log.w(TAG, "PbapClientProfile found new device: " + nextDevice);
+ device = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, nextDevice);
+ }
+ device.onProfileStateChanged(PbapClientProfile.this, BluetoothProfile.STATE_CONNECTED);
+ device.refresh();
+ }
+ mIsProfileReady = true;
+ }
+
+ public void onServiceDisconnected(int profile) {
+ if (V) {
+ Log.d(TAG,"Bluetooth service disconnected");
+ }
+ mIsProfileReady = false;
+ }
+ }
+
+ private void refreshProfiles() {
+ Collection<CachedBluetoothDevice> cachedDevices = mDeviceManager.getCachedDevicesCopy();
+ for (CachedBluetoothDevice device : cachedDevices) {
+ device.onUuidChanged();
+ }
+ }
+
+ public boolean pbapClientExists() {
+ return (mService != null);
+ }
+
+ public boolean isProfileReady() {
+ return mIsProfileReady;
+ }
+
+ PbapClientProfile(Context context, LocalBluetoothAdapter adapter,
+ CachedBluetoothDeviceManager deviceManager,
+ LocalBluetoothProfileManager profileManager) {
+ mLocalAdapter = adapter;
+ mDeviceManager = deviceManager;
+ mProfileManager = profileManager;
+ mLocalAdapter.getProfileProxy(context, new PbapClientServiceListener(),
+ BluetoothProfile.PBAP_CLIENT);
+ }
+
+ public boolean isConnectable() {
+ return true;
+ }
+
+ public boolean isAutoConnectable() {
+ return true;
+ }
+
+ public List<BluetoothDevice> getConnectedDevices() {
+ if (mService == null) {
+ return new ArrayList<BluetoothDevice>(0);
+ }
+ return mService.getDevicesMatchingConnectionStates(
+ new int[] {BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.STATE_CONNECTING,
+ BluetoothProfile.STATE_DISCONNECTING});
+ }
+
+ public boolean connect(BluetoothDevice device) {
+ if (V) {
+ Log.d(TAG,"PBAPClientProfile got connect request");
+ }
+ if (mService == null) {
+ return false;
+ }
+ List<BluetoothDevice> srcs = getConnectedDevices();
+ if (srcs != null) {
+ for (BluetoothDevice src : srcs) {
+ if (src.equals(device)) {
+ // Connect to same device, Ignore it
+ Log.d(TAG,"Ignoring Connect");
+ return true;
+ }
+ }
+ for (BluetoothDevice src : srcs) {
+ mService.disconnect(device);
+ }
+ }
+ Log.d(TAG,"PBAPClientProfile attempting to connect to " + device.getAddress());
+
+ return mService.connect(device);
+ }
+
+ public boolean disconnect(BluetoothDevice device) {
+ if (V) {
+ Log.d(TAG,"PBAPClientProfile got disconnect request");
+ }
+ if (mService == null) {
+ return false;
+ }
+ return mService.disconnect(device);
+ }
+
+ public int getConnectionStatus(BluetoothDevice device) {
+ if (mService == null) {
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+ return mService.getConnectionState(device);
+ }
+
+ public boolean isPreferred(BluetoothDevice device) {
+ if (mService == null) {
+ return false;
+ }
+ return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+ }
+
+ public int getPreferred(BluetoothDevice device) {
+ if (mService == null) {
+ return BluetoothProfile.PRIORITY_OFF;
+ }
+ return mService.getPriority(device);
+ }
+
+ public void setPreferred(BluetoothDevice device, boolean preferred) {
+ if (mService == null) {
+ return;
+ }
+ if (preferred) {
+ if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
+ mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ }
+ } else {
+ mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+ }
+ }
+
+ public String toString() {
+ return NAME;
+ }
+
+ public int getOrdinal() {
+ return ORDINAL;
+ }
+
+ public int getNameResource(BluetoothDevice device) {
+ // we need to have same string in UI as the server side.
+ return R.string.bluetooth_profile_pbap;
+ }
+
+ public int getSummaryResourceForDevice(BluetoothDevice device) {
+ return R.string.bluetooth_profile_pbap_summary;
+ }
+
+ public int getDrawableResource(BluetoothClass btClass) {
+ return R.drawable.ic_bt_cellphone;
+ }
+
+ protected void finalize() {
+ if (V) {
+ Log.d(TAG, "finalize()");
+ }
+ if (mService != null) {
+ try {
+ BluetoothAdapter.getDefaultAdapter().closeProfileProxy(
+ BluetoothProfile.PBAP_CLIENT,mService);
+ mService = null;
+ } catch (Throwable t) {
+ Log.w(TAG, "Error cleaning up PBAP Client proxy", t);
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
index 3b818c8..0e3e0d5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
@@ -60,7 +60,7 @@
public class StorageMeasurement {
private static final String TAG = "StorageMeasurement";
- private static final boolean LOCAL_LOGV = true;
+ private static final boolean LOCAL_LOGV = false;
static final boolean LOGV = LOCAL_LOGV && Log.isLoggable(TAG, Log.VERBOSE);
private static final String DEFAULT_CONTAINER_PACKAGE = "com.android.defcontainer";
diff --git a/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java b/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java
new file mode 100644
index 0000000..78d7c56
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.display;
+
+import com.android.settingslib.R;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.AsyncTask;
+import android.os.RemoteException;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.MathUtils;
+import android.view.Display;
+import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
+
+import java.util.Arrays;
+
+/**
+ * Utility methods for working with display density.
+ */
+public class DisplayDensityUtils {
+ private static final String LOG_TAG = "DisplayDensityUtils";
+
+ /** Minimum increment between density scales. */
+ private static final float MIN_SCALE_INTERVAL = 0.09f;
+
+ /** Minimum density scale. This is available on all devices. */
+ private static final float MIN_SCALE = 0.85f;
+
+ /** Maximum density scale. The actual scale used depends on the device. */
+ private static final float MAX_SCALE = 1.50f;
+
+ /** Summary used for "default" scale. */
+ public static final int SUMMARY_DEFAULT = R.string.screen_zoom_summary_default;
+
+ /** Summary used for "custom" scale. */
+ private static final int SUMMARY_CUSTOM = R.string.screen_zoom_summary_custom;
+
+ /**
+ * Summaries for scales smaller than "default" in order of smallest to
+ * largest.
+ */
+ private static final int[] SUMMARIES_SMALLER = new int[] {
+ R.string.screen_zoom_summary_small
+ };
+
+ /**
+ * Summaries for scales larger than "default" in order of smallest to
+ * largest.
+ */
+ private static final int[] SUMMARIES_LARGER = new int[] {
+ R.string.screen_zoom_summary_large,
+ R.string.screen_zoom_summary_very_large,
+ R.string.screen_zoom_summary_extremely_large,
+ };
+
+ /**
+ * Minimum allowed screen dimension, corresponds to resource qualifiers
+ * "small" or "sw320dp". This value must be at least the minimum screen
+ * size required by the CDD so that we meet developer expectations.
+ */
+ private static final int MIN_DIMENSION_DP = 320;
+
+ private final String[] mEntries;
+ private final int[] mValues;
+
+ private final int mDefaultDensity;
+ private final int mCurrentIndex;
+
+ public DisplayDensityUtils(Context context) {
+ final int defaultDensity = DisplayDensityUtils.getDefaultDisplayDensity(
+ Display.DEFAULT_DISPLAY);
+ if (defaultDensity <= 0) {
+ mEntries = null;
+ mValues = null;
+ mDefaultDensity = 0;
+ mCurrentIndex = -1;
+ return;
+ }
+
+ final Resources res = context.getResources();
+ final DisplayMetrics metrics = res.getDisplayMetrics();
+ final int currentDensity = metrics.densityDpi;
+ int currentDensityIndex = -1;
+
+ // Compute number of "larger" and "smaller" scales for this display.
+ final int minDimensionPx = Math.min(metrics.widthPixels, metrics.heightPixels);
+ final int maxDensity = DisplayMetrics.DENSITY_MEDIUM * minDimensionPx / MIN_DIMENSION_DP;
+ final float maxScale = Math.min(MAX_SCALE, maxDensity / (float) defaultDensity);
+ final float minScale = MIN_SCALE;
+ final int numLarger = (int) MathUtils.constrain((maxScale - 1) / MIN_SCALE_INTERVAL,
+ 0, SUMMARIES_LARGER.length);
+ final int numSmaller = (int) MathUtils.constrain((1 - minScale) / MIN_SCALE_INTERVAL,
+ 0, SUMMARIES_SMALLER.length);
+
+ String[] entries = new String[1 + numSmaller + numLarger];
+ int[] values = new int[entries.length];
+ int curIndex = 0;
+
+ if (numSmaller > 0) {
+ final float interval = (1 - minScale) / numSmaller;
+ for (int i = numSmaller - 1; i >= 0; i--) {
+ // Round down to a multiple of 2 by truncating the low bit.
+ final int density = ((int) (defaultDensity * (1 - (i + 1) * interval))) & ~1;
+ if (currentDensity == density) {
+ currentDensityIndex = curIndex;
+ }
+ entries[curIndex] = res.getString(SUMMARIES_SMALLER[i]);
+ values[curIndex] = density;
+ curIndex++;
+ }
+ }
+
+ if (currentDensity == defaultDensity) {
+ currentDensityIndex = curIndex;
+ }
+ values[curIndex] = defaultDensity;
+ entries[curIndex] = res.getString(SUMMARY_DEFAULT);
+ curIndex++;
+
+ if (numLarger > 0) {
+ final float interval = (maxScale - 1) / numLarger;
+ for (int i = 0; i < numLarger; i++) {
+ // Round down to a multiple of 2 by truncating the low bit.
+ final int density = ((int) (defaultDensity * (1 + (i + 1) * interval))) & ~1;
+ if (currentDensity == density) {
+ currentDensityIndex = curIndex;
+ }
+ values[curIndex] = density;
+ entries[curIndex] = res.getString(SUMMARIES_LARGER[i]);
+ curIndex++;
+ }
+ }
+
+ final int displayIndex;
+ if (currentDensityIndex >= 0) {
+ displayIndex = currentDensityIndex;
+ } else {
+ // We don't understand the current density. Must have been set by
+ // someone else. Make room for another entry...
+ int newLength = values.length + 1;
+ values = Arrays.copyOf(values, newLength);
+ values[curIndex] = currentDensity;
+
+ entries = Arrays.copyOf(entries, newLength);
+ entries[curIndex] = res.getString(SUMMARY_CUSTOM, currentDensity);
+
+ displayIndex = curIndex;
+ }
+
+ mDefaultDensity = defaultDensity;
+ mCurrentIndex = displayIndex;
+ mEntries = entries;
+ mValues = values;
+ }
+
+ public String[] getEntries() {
+ return mEntries;
+ }
+
+ public int[] getValues() {
+ return mValues;
+ }
+
+ public int getCurrentIndex() {
+ return mCurrentIndex;
+ }
+
+ public int getDefaultDensity() {
+ return mDefaultDensity;
+ }
+
+ /**
+ * Returns the default density for the specified display.
+ *
+ * @param displayId the identifier of the display
+ * @return the default density of the specified display, or {@code -1} if
+ * the display does not exist or the density could not be obtained
+ */
+ private static int getDefaultDisplayDensity(int displayId) {
+ try {
+ final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+ return wm.getInitialDisplayDensity(displayId);
+ } catch (RemoteException exc) {
+ return -1;
+ }
+ }
+
+ /**
+ * Asynchronously applies display density changes to the specified display.
+ *
+ * @param displayId the identifier of the display to modify
+ */
+ public static void clearForcedDisplayDensity(final int displayId) {
+ AsyncTask.execute(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+ wm.clearForcedDisplayDensity(displayId);
+ } catch (RemoteException exc) {
+ Log.w(LOG_TAG, "Unable to clear forced display density setting");
+ }
+ }
+ });
+ }
+
+ /**
+ * Asynchronously applies display density changes to the specified display.
+ *
+ * @param displayId the identifier of the display to modify
+ * @param density the density to force for the specified display
+ */
+ public static void setForcedDisplayDensity(final int displayId, final int density) {
+ AsyncTask.execute(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+ wm.setForcedDisplayDensity(displayId, density);
+ } catch (RemoteException exc) {
+ Log.w(LOG_TAG, "Unable to save forced display density setting");
+ }
+ }
+ });
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
index ce69c5a..ff70190 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
@@ -41,6 +41,7 @@
import android.widget.ListView;
import android.widget.Toolbar;
import com.android.settingslib.R;
+import com.android.settingslib.applications.InterestingConfigChanges;
import java.util.ArrayList;
import java.util.HashMap;
@@ -55,6 +56,7 @@
private static List<DashboardCategory> sDashboardCategories;
private static HashMap<Pair<String, String>, Tile> sTileCache;
+ private static InterestingConfigChanges sConfigTracker;
private final PackageReceiver mPackageReceiver = new PackageReceiver();
private final List<CategoryListener> mCategoryListeners = new ArrayList<>();
@@ -153,8 +155,10 @@
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
updateDrawer();
} else {
- mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
- mDrawerLayout = null;
+ if (mDrawerLayout != null) {
+ mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
+ mDrawerLayout = null;
+ }
}
}
@@ -172,8 +176,11 @@
@Override
public void setContentView(@LayoutRes int layoutResID) {
- LayoutInflater.from(this).inflate(layoutResID,
- (ViewGroup) findViewById(R.id.content_frame));
+ final ViewGroup parent = (ViewGroup) findViewById(R.id.content_frame);
+ if (parent != null) {
+ parent.removeAllViews();
+ }
+ LayoutInflater.from(this).inflate(layoutResID, parent);
}
@Override
@@ -208,6 +215,7 @@
public List<DashboardCategory> getDashboardCategories() {
if (sDashboardCategories == null) {
sTileCache = new HashMap<>();
+ sConfigTracker = new InterestingConfigChanges();
sDashboardCategories = TileUtils.getCategories(this, sTileCache);
}
return sDashboardCategories;
@@ -267,6 +275,9 @@
private class CategoriesUpdater extends AsyncTask<Void, Void, List<DashboardCategory>> {
@Override
protected List<DashboardCategory> doInBackground(Void... params) {
+ if (sConfigTracker.applyNewConfig(getResources())) {
+ sTileCache.clear();
+ }
return TileUtils.getCategories(SettingsDrawerActivity.this, sTileCache);
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 987b5ea..61c9f2b 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -21,6 +21,7 @@
import android.app.AppGlobals;
import android.app.backup.BackupManager;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
@@ -63,6 +64,7 @@
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
import com.android.providers.settings.SettingsState.Setting;
+import com.android.server.SystemConfig;
import java.io.File;
import java.io.FileDescriptor;
@@ -1940,7 +1942,7 @@
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 125;
+ private static final int SETTINGS_VERSION = 126;
private final int mUserId;
@@ -2136,6 +2138,35 @@
currentVersion = 125;
}
+ if (currentVersion == 125) {
+ // Version 125: Allow OEMs to set the default VR service.
+ final SettingsState secureSettings = getSecureSettingsLocked(userId);
+
+ Setting currentSetting = secureSettings.getSettingLocked(
+ Settings.Secure.ENABLED_VR_LISTENERS);
+ if (currentSetting == null) {
+ ArraySet<ComponentName> l =
+ SystemConfig.getInstance().getDefaultVrComponents();
+
+ if (l != null && !l.isEmpty()) {
+ StringBuilder b = new StringBuilder();
+ boolean start = true;
+ for (ComponentName c : l) {
+ if (!start) {
+ b.append(':');
+ }
+ b.append(c.flattenToString());
+ start = false;
+ }
+ secureSettings.insertSettingLocked(
+ Settings.Secure.ENABLED_VR_LISTENERS, b.toString(),
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+
+ }
+ currentVersion = 126;
+ }
+
// vXXX: Add new settings above this point.
// Return the current version.
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 0c35573..b557dc4 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -139,7 +139,7 @@
<activity
android:name=".BugreportWarningActivity"
- android:theme="@*android:style/Theme.Material.DayNight.Dialog.Alert"
+ android:theme="@android:style/Theme.Material.Light.Dialog.Alert"
android:finishOnCloseSystemDialogs="true"
android:excludeFromRecents="true"
android:exported="false" />
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index c9582ea..7ca7614 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -409,10 +409,11 @@
final BugreportInfo info = new BugreportInfo(mContext, id, pid, name, max);
if (mProcesses.indexOfKey(id) >= 0) {
+ // BUGREPORT_STARTED intent was already received; ignore it.
Log.w(TAG, "ID " + id + " already watched");
- } else {
- mProcesses.put(info.id, info);
+ return true;
}
+ mProcesses.put(info.id, info);
// Take initial screenshot.
takeScreenshot(id, false);
updateProgress(info);
@@ -462,7 +463,6 @@
.setContentTitle(title)
.setTicker(title)
.setContentText(name)
- .setContentInfo(percentageText)
.setProgress(info.max, info.progress, false)
.setOngoing(true)
.setContentIntent(infoPendingIntent)
@@ -957,7 +957,7 @@
.setDeleteIntent(newCancelIntent(context, info));
if (!TextUtils.isEmpty(info.name)) {
- builder.setContentInfo(info.name);
+ builder.setSubText(info.name);
}
Log.v(TAG, "Sending 'Share' notification for ID " + info.id + ": " + title);
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 589eac6..9e2442c 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -184,6 +184,14 @@
android:exported="true"
/>
+ <!-- Recents depends on every user having their own SystemUI process, so on user switch,
+ ensure that the process is created by starting this service.
+ -->
+ <service android:name="SystemUISecondaryUserService"
+ android:exported="true"
+ android:permission="com.android.systemui.permission.SELF" />
+
+
<!-- started from PhoneWindowManager
TODO: Should have an android:permission attribute -->
<service android:name=".screenshot.TakeScreenshotService"
@@ -263,6 +271,14 @@
</intent-filter>
</activity>
+ <activity
+ android:name=".stackdivider.ForcedResizableInfoActivity"
+ android:theme="@style/ForcedResizableTheme"
+ android:excludeFromRecents="true"
+ android:stateNotNeeded="true"
+ android:exported="false">
+ </activity>
+
<!-- Callback for dismissing screenshot notification after a share target is picked -->
<receiver android:name=".screenshot.GlobalScreenshot$TargetChosenReceiver"
android:process=":screenshot"
@@ -358,7 +374,6 @@
android:name="com.android.systemui.tv.pip.PipOverlayActivity"
android:exported="true"
android:theme="@style/PipTheme"
- android:launchMode="singleTop"
android:taskAffinity=""
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
android:resizeableActivity="true"
diff --git a/packages/SystemUI/res/anim/forced_resizable_enter.xml b/packages/SystemUI/res/anim/forced_resizable_enter.xml
new file mode 100644
index 0000000..01b8fdb
--- /dev/null
+++ b/packages/SystemUI/res/anim/forced_resizable_enter.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2016 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromAlpha="0.0"
+ android:toAlpha="1.0"
+ android:interpolator="@android:interpolator/linear_out_slow_in"
+ android:duration="280" />
diff --git a/packages/SystemUI/res/anim/forced_resizable_exit.xml b/packages/SystemUI/res/anim/forced_resizable_exit.xml
new file mode 100644
index 0000000..6f316a7
--- /dev/null
+++ b/packages/SystemUI/res/anim/forced_resizable_exit.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2016 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:duration="160"
+ android:interpolator="@android:interpolator/fast_out_linear_in"
+ android:zAdjustment="top"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/major_a_b_dot_01_animation.xml b/packages/SystemUI/res/anim/major_a_b_dot_01_animation.xml
new file mode 100644
index 0000000..b5bb4dc
--- /dev/null
+++ b/packages/SystemUI/res/anim/major_a_b_dot_01_animation.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android" >
+ <objectAnimator
+ android:duration="250"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:pathData="M 3.25,4.0 c 0.79167,0.0 3.95833,0.0 4.75,0.0"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/packages/SystemUI/res/anim/major_a_b_dot_animation.xml b/packages/SystemUI/res/anim/major_a_b_dot_animation.xml
new file mode 100644
index 0000000..6443167
--- /dev/null
+++ b/packages/SystemUI/res/anim/major_a_b_dot_animation.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android" >
+ <objectAnimator
+ android:duration="250"
+ android:propertyName="pathData"
+ android:valueFrom="M 0.0,-2.75 l 0.0,0.0 c 1.51878306195,0.0 2.75,1.23121693805 2.75,2.75 l 0.0,0.0 c 0.0,1.51878306195 -1.23121693805,2.75 -2.75,2.75 l 0.0,0.0 c -1.51878306195,0.0 -2.75,-1.23121693805 -2.75,-2.75 l 0.0,0.0 c 0.0,-1.51878306195 1.23121693805,-2.75 2.75,-2.75 Z"
+ android:valueTo="M -4.75,-2.75 l 9.5,0.0 c 1.51878306195,0.0 2.75,1.23121693805 2.75,2.75 l 0.0,0.0 c 0.0,1.51878306195 -1.23121693805,2.75 -2.75,2.75 l -9.5,0.0 c -1.51878306195,0.0 -2.75,-1.23121693805 -2.75,-2.75 l 0.0,0.0 c 0.0,-1.51878306195 1.23121693805,-2.75 2.75,-2.75 Z"
+ android:valueType="pathType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/packages/SystemUI/res/anim/major_b_a_dot_01_animation.xml b/packages/SystemUI/res/anim/major_b_a_dot_01_animation.xml
new file mode 100644
index 0000000..2e0a4fa
--- /dev/null
+++ b/packages/SystemUI/res/anim/major_b_a_dot_01_animation.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android" >
+ <objectAnimator
+ android:duration="250"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:pathData="M 8.0,4.0 c -0.79167,0.0 -3.95833,0.0 -4.75,0.0"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/packages/SystemUI/res/anim/major_b_a_dot_animation.xml b/packages/SystemUI/res/anim/major_b_a_dot_animation.xml
new file mode 100644
index 0000000..731c87c
--- /dev/null
+++ b/packages/SystemUI/res/anim/major_b_a_dot_animation.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android" >
+ <objectAnimator
+ android:duration="250"
+ android:propertyName="pathData"
+ android:valueFrom="M -4.75,-2.75 l 9.5,0.0 c 1.51878306195,0.0 2.75,1.23121693805 2.75,2.75 l 0.0,0.0 c 0.0,1.51878306195 -1.23121693805,2.75 -2.75,2.75 l -9.5,0.0 c -1.51878306195,0.0 -2.75,-1.23121693805 -2.75,-2.75 l 0.0,0.0 c 0.0,-1.51878306195 1.23121693805,-2.75 2.75,-2.75 Z"
+ android:valueTo="M 0.0,-2.75 l 0.0,0.0 c 1.51878306195,0.0 2.75,1.23121693805 2.75,2.75 l 0.0,0.0 c 0.0,1.51878306195 -1.23121693805,2.75 -2.75,2.75 l 0.0,0.0 c -1.51878306195,0.0 -2.75,-1.23121693805 -2.75,-2.75 l 0.0,0.0 c 0.0,-1.51878306195 1.23121693805,-2.75 2.75,-2.75 Z"
+ android:valueType="pathType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/packages/SystemUI/res/anim/major_b_c_dot_01_animation.xml b/packages/SystemUI/res/anim/major_b_c_dot_01_animation.xml
new file mode 100644
index 0000000..e8c2687
--- /dev/null
+++ b/packages/SystemUI/res/anim/major_b_c_dot_01_animation.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android" >
+ <objectAnimator
+ android:duration="250"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:pathData="M 8.0,4.0 c 0.79167,0.0 3.95833,0.0 4.75,0.0"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/packages/SystemUI/res/anim/major_b_c_dot_animation.xml b/packages/SystemUI/res/anim/major_b_c_dot_animation.xml
new file mode 100644
index 0000000..731c87c
--- /dev/null
+++ b/packages/SystemUI/res/anim/major_b_c_dot_animation.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android" >
+ <objectAnimator
+ android:duration="250"
+ android:propertyName="pathData"
+ android:valueFrom="M -4.75,-2.75 l 9.5,0.0 c 1.51878306195,0.0 2.75,1.23121693805 2.75,2.75 l 0.0,0.0 c 0.0,1.51878306195 -1.23121693805,2.75 -2.75,2.75 l -9.5,0.0 c -1.51878306195,0.0 -2.75,-1.23121693805 -2.75,-2.75 l 0.0,0.0 c 0.0,-1.51878306195 1.23121693805,-2.75 2.75,-2.75 Z"
+ android:valueTo="M 0.0,-2.75 l 0.0,0.0 c 1.51878306195,0.0 2.75,1.23121693805 2.75,2.75 l 0.0,0.0 c 0.0,1.51878306195 -1.23121693805,2.75 -2.75,2.75 l 0.0,0.0 c -1.51878306195,0.0 -2.75,-1.23121693805 -2.75,-2.75 l 0.0,0.0 c 0.0,-1.51878306195 1.23121693805,-2.75 2.75,-2.75 Z"
+ android:valueType="pathType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/packages/SystemUI/res/anim/major_c_b_dot_01_animation.xml b/packages/SystemUI/res/anim/major_c_b_dot_01_animation.xml
new file mode 100644
index 0000000..d0174bc
--- /dev/null
+++ b/packages/SystemUI/res/anim/major_c_b_dot_01_animation.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android" >
+ <objectAnimator
+ android:duration="250"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:pathData="M 12.75,4.0 c -0.79167,0.0 -3.95833,0.0 -4.75,0.0"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/packages/SystemUI/res/anim/major_c_b_dot_animation.xml b/packages/SystemUI/res/anim/major_c_b_dot_animation.xml
new file mode 100644
index 0000000..6443167
--- /dev/null
+++ b/packages/SystemUI/res/anim/major_c_b_dot_animation.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android" >
+ <objectAnimator
+ android:duration="250"
+ android:propertyName="pathData"
+ android:valueFrom="M 0.0,-2.75 l 0.0,0.0 c 1.51878306195,0.0 2.75,1.23121693805 2.75,2.75 l 0.0,0.0 c 0.0,1.51878306195 -1.23121693805,2.75 -2.75,2.75 l 0.0,0.0 c -1.51878306195,0.0 -2.75,-1.23121693805 -2.75,-2.75 l 0.0,0.0 c 0.0,-1.51878306195 1.23121693805,-2.75 2.75,-2.75 Z"
+ android:valueTo="M -4.75,-2.75 l 9.5,0.0 c 1.51878306195,0.0 2.75,1.23121693805 2.75,2.75 l 0.0,0.0 c 0.0,1.51878306195 -1.23121693805,2.75 -2.75,2.75 l -9.5,0.0 c -1.51878306195,0.0 -2.75,-1.23121693805 -2.75,-2.75 l 0.0,0.0 c 0.0,-1.51878306195 1.23121693805,-2.75 2.75,-2.75 Z"
+ android:valueType="pathType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/packages/SystemUI/res/anim/minor_a_b_dot_02_animation.xml b/packages/SystemUI/res/anim/minor_a_b_dot_02_animation.xml
new file mode 100644
index 0000000..b5bb4dc
--- /dev/null
+++ b/packages/SystemUI/res/anim/minor_a_b_dot_02_animation.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android" >
+ <objectAnimator
+ android:duration="250"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:pathData="M 3.25,4.0 c 0.79167,0.0 3.95833,0.0 4.75,0.0"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/packages/SystemUI/res/anim/minor_b_a_dot_02_animation.xml b/packages/SystemUI/res/anim/minor_b_a_dot_02_animation.xml
new file mode 100644
index 0000000..2e0a4fa
--- /dev/null
+++ b/packages/SystemUI/res/anim/minor_b_a_dot_02_animation.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android" >
+ <objectAnimator
+ android:duration="250"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:pathData="M 8.0,4.0 c -0.79167,0.0 -3.95833,0.0 -4.75,0.0"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/packages/SystemUI/res/anim/minor_b_c_dot_02_animation.xml b/packages/SystemUI/res/anim/minor_b_c_dot_02_animation.xml
new file mode 100644
index 0000000..e8c2687
--- /dev/null
+++ b/packages/SystemUI/res/anim/minor_b_c_dot_02_animation.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android" >
+ <objectAnimator
+ android:duration="250"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:pathData="M 8.0,4.0 c 0.79167,0.0 3.95833,0.0 4.75,0.0"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/packages/SystemUI/res/anim/minor_c_b_dot_02_animation.xml b/packages/SystemUI/res/anim/minor_c_b_dot_02_animation.xml
new file mode 100644
index 0000000..d0174bc
--- /dev/null
+++ b/packages/SystemUI/res/anim/minor_c_b_dot_02_animation.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android" >
+ <objectAnimator
+ android:duration="250"
+ android:propertyXName="translateX"
+ android:propertyYName="translateY"
+ android:pathData="M 12.75,4.0 c -0.79167,0.0 -3.95833,0.0 -4.75,0.0"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_buttons_in_recents_focus_gain_animation.xml b/packages/SystemUI/res/anim/tv_pip_controls_buttons_in_recents_focus_gain_animation.xml
new file mode 100644
index 0000000..ebc6a4a7
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_pip_controls_buttons_in_recents_focus_gain_animation.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <objectAnimator
+ android:propertyName="scaleX"
+ android:valueTo="1.0"
+ android:interpolator="@android:interpolator/fast_out_slow_in"
+ android:duration="@integer/recents_tv_pip_focus_anim_duration" />
+ <objectAnimator
+ android:propertyName="scaleY"
+ android:valueTo="1.0"
+ android:interpolator="@android:interpolator/fast_out_slow_in"
+ android:duration="@integer/recents_tv_pip_focus_anim_duration" />
+</set>
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_buttons_in_recents_focus_lose_animation.xml b/packages/SystemUI/res/anim/tv_pip_controls_buttons_in_recents_focus_lose_animation.xml
new file mode 100644
index 0000000..95499bd
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_pip_controls_buttons_in_recents_focus_lose_animation.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <objectAnimator
+ android:propertyName="scaleX"
+ android:valueTo="0.7"
+ android:interpolator="@android:interpolator/fast_out_slow_in"
+ android:duration="@integer/recents_tv_pip_focus_anim_duration" />
+ <objectAnimator
+ android:propertyName="scaleY"
+ android:valueTo="0.7"
+ android:interpolator="@android:interpolator/fast_out_slow_in"
+ android:duration="@integer/recents_tv_pip_focus_anim_duration" />
+</set>
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_gain_animation.xml b/packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_gain_animation.xml
new file mode 100644
index 0000000..7555bdd
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_gain_animation.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:propertyName="translationY"
+ android:valueTo="0dp"
+ android:interpolator="@android:interpolator/fast_out_slow_in"
+ android:duration="@integer/recents_tv_pip_focus_anim_duration" />
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_lose_animation.xml b/packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_lose_animation.xml
new file mode 100644
index 0000000..b40ccd4
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_lose_animation.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:propertyName="translationY"
+ android:valueTo="-57dp"
+ android:interpolator="@android:interpolator/fast_out_slow_in"
+ android:duration="@integer/recents_tv_pip_focus_anim_duration" />
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml b/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml
new file mode 100644
index 0000000..681ff91
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_gain_animation.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:propertyName="alpha"
+ android:valueTo="1"
+ android:interpolator="@android:interpolator/fast_out_slow_in"
+ android:duration="@integer/recents_tv_pip_focus_anim_duration" />
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_lose_animation.xml b/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_lose_animation.xml
new file mode 100644
index 0000000..e6deb0f
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_pip_controls_text_in_recents_focus_lose_animation.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:propertyName="alpha"
+ android:valueTo="0"
+ android:interpolator="@android:interpolator/fast_out_slow_in"
+ android:duration="@integer/recents_tv_pip_focus_anim_duration" />
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_notify_image.png b/packages/SystemUI/res/drawable-hdpi/stat_notify_image.png
deleted file mode 100644
index 7b0fcc7..0000000
--- a/packages/SystemUI/res/drawable-hdpi/stat_notify_image.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_notify_image_error.png b/packages/SystemUI/res/drawable-hdpi/stat_notify_image_error.png
deleted file mode 100644
index 73e9c96..0000000
--- a/packages/SystemUI/res/drawable-hdpi/stat_notify_image_error.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_notify_image.png b/packages/SystemUI/res/drawable-mdpi/stat_notify_image.png
deleted file mode 100644
index a02e21c..0000000
--- a/packages/SystemUI/res/drawable-mdpi/stat_notify_image.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_notify_image_error.png b/packages/SystemUI/res/drawable-mdpi/stat_notify_image_error.png
deleted file mode 100644
index 4af2617..0000000
--- a/packages/SystemUI/res/drawable-mdpi/stat_notify_image_error.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/stat_notify_image.png b/packages/SystemUI/res/drawable-xhdpi/stat_notify_image.png
deleted file mode 100644
index 24bdbb6..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/stat_notify_image.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/stat_notify_image_error.png b/packages/SystemUI/res/drawable-xhdpi/stat_notify_image_error.png
deleted file mode 100644
index 6ecd2d3..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/stat_notify_image_error.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/stat_notify_image.png b/packages/SystemUI/res/drawable-xxhdpi/stat_notify_image.png
deleted file mode 100644
index 5e733ef..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/stat_notify_image.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/stat_notify_image_error.png b/packages/SystemUI/res/drawable-xxhdpi/stat_notify_image_error.png
deleted file mode 100644
index ecc2c83..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/stat_notify_image_error.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable/ic_data_saver.xml b/packages/SystemUI/res/drawable/ic_data_saver.xml
index 426238c..7356772 100644
--- a/packages/SystemUI/res/drawable/ic_data_saver.xml
+++ b/packages/SystemUI/res/drawable/ic_data_saver.xml
@@ -20,9 +20,11 @@
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFFFF"
- android:pathData="
- M9.0,16.0l2.0,0.0L11.0,8.0L9.0,8.0l0.0,8.0z
- m3.0,-14.0C6.48,2.0 2.0,6.48 2.0,12.0s4.48,10.0 10.0,10.0 10.0,-4.48 10.0,-10.0S17.52,2.0 12.0,2.0z
- m0.0,18.0c-4.41,0.0 -8.0,-3.59 -8.0,-8.0s3.59,-8.0 8.0,-8.0 8.0,3.59 8.0,8.0 -3.59,8.0 -8.0,8.0z
- m1.0,-4.0l2.0,0.0l0.0,-8.0l-2.0,0.0l0.0,8.0z"/>
+ android:pathData="M12.0,19.0c-3.9,0.0 -7.0,-3.1 -7.0,-7.0c0.0,-3.5 2.6,-6.4 6.0,-6.9L11.0,2.0C5.9,2.5 2.0,6.8 2.0,12.0c0.0,5.5 4.5,10.0 10.0,10.0c3.3,0.0 6.2,-1.6 8.1,-4.1l-2.6,-1.5C16.2,18.0 14.2,19.0 12.0,19.0z"/>
+ <path
+ android:fillColor="#4DFFFFFF"
+ android:pathData="M13.0,2.0l0.0,3.0c3.4,0.5 6.0,3.4 6.0,6.9c0.0,0.9 -0.2,1.8 -0.5,2.5l2.6,1.5c0.6,-1.2 0.9,-2.6 0.9,-4.1C22.0,6.8 18.0,2.6 13.0,2.0z"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M16.0,11.0l0.0,2.0 -3.0,0.0 0.0,3.0 -2.0,0.0 0.0,-3.0 -3.0,0.0 0.0,-2.0 3.0,0.0 0.0,-3.0 2.0,0.0 0.0,3.0z"/>
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_data_saver_off.xml b/packages/SystemUI/res/drawable/ic_data_saver_off.xml
index 0713548..fd9701e 100644
--- a/packages/SystemUI/res/drawable/ic_data_saver_off.xml
+++ b/packages/SystemUI/res/drawable/ic_data_saver_off.xml
@@ -17,12 +17,12 @@
android:width="24.0dp"
android:height="24.0dp"
android:viewportWidth="24.0"
- android:viewportHeight="24.0">
+ android:viewportHeight="24.0"
+ android:tint="#4DFFFFFF">
<path
- android:fillColor="#4DFFFFFF"
- android:pathData="
- M9.0,16.0l2.0,0.0L11.0,8.0L9.0,8.0l0.0,8.0z
- m3.0,-14.0C6.48,2.0 2.0,6.48 2.0,12.0s4.48,10.0 10.0,10.0 10.0,-4.48 10.0,-10.0S17.52,2.0 12.0,2.0z
- m0.0,18.0c-4.41,0.0 -8.0,-3.59 -8.0,-8.0s3.59,-8.0 8.0,-8.0 8.0,3.59 8.0,8.0 -3.59,8.0 -8.0,8.0z
- m1.0,-4.0l2.0,0.0l0.0,-8.0l-2.0,0.0l0.0,8.0z"/>
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M12.0,19.0c-3.9,0.0 -7.0,-3.1 -7.0,-7.0c0.0,-3.5 2.6,-6.4 6.0,-6.9L11.0,2.0C5.9,2.5 2.0,6.8 2.0,12.0c0.0,5.5 4.5,10.0 10.0,10.0c3.3,0.0 6.2,-1.6 8.1,-4.1l-2.6,-1.5C16.2,18.0 14.2,19.0 12.0,19.0z"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M13.0,2.0l0.0,3.0c3.4,0.5 6.0,3.4 6.0,6.9c0.0,0.9 -0.2,1.8 -0.5,2.5l2.6,1.5c0.6,-1.2 0.9,-2.6 0.9,-4.1C22.0,6.8 18.0,2.6 13.0,2.0z"/>
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_ksh_key_backspace.xml b/packages/SystemUI/res/drawable/ic_ksh_key_backspace.xml
new file mode 100644
index 0000000..6519673
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_ksh_key_backspace.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path android:pathData="M0 0h24v24H0z" />
+ <path android:fillColor="@color/ksh_key_item_color"
+ android:pathData="M22 3H7c-.69 0-1.23 .35 -1.59 .88 L0 12l5.41 8.11c.36 .53 .9 .89
+1.59 .89 h15c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-3 12.59L17.59 17 14 13.41 10.41 17 9 15.59
+12.59 12 9 8.41 10.41 7 14 10.59 17.59 7 19 8.41 15.41 12 19 15.59z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_ksh_key_down.xml b/packages/SystemUI/res/drawable/ic_ksh_key_down.xml
new file mode 100644
index 0000000..25a2560
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_ksh_key_down.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2016 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path android:fillColor="@color/ksh_key_item_color"
+ android:pathData="M7.41 7.84L12 12.42l4.59-4.58L18 9.25l-6 6-6-6z" />
+ <path android:pathData="M0-.75h24v24H0z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_ksh_key_enter.xml b/packages/SystemUI/res/drawable/ic_ksh_key_enter.xml
new file mode 100644
index 0000000..599f350
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_ksh_key_enter.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2016 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path android:pathData="M0 0h24v24H0z" />
+ <path android:fillColor="@color/ksh_key_item_color"
+ android:pathData="M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_ksh_key_left.xml b/packages/SystemUI/res/drawable/ic_ksh_key_left.xml
new file mode 100644
index 0000000..038187e8
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_ksh_key_left.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2016 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path android:fillColor="@color/ksh_key_item_color"
+ android:pathData="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z" />
+ <path android:pathData="M0 0h24v24H0z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_ksh_key_meta.xml b/packages/SystemUI/res/drawable/ic_ksh_key_meta.xml
new file mode 100644
index 0000000..1e2195e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_ksh_key_meta.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2016 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path android:fillColor="@color/ksh_key_item_color"
+ android:pathData="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91
+3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27 .28 v.79l5 4.99L20.49
+19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5
+14z" />
+ <path android:pathData="M0 0h24v24H0z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_ksh_key_right.xml b/packages/SystemUI/res/drawable/ic_ksh_key_right.xml
new file mode 100644
index 0000000..f2d7315
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_ksh_key_right.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2016 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path android:fillColor="@color/ksh_key_item_color"
+ android:pathData="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z" />
+ <path android:pathData="M0 0h24v24H0z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_ksh_key_up.xml b/packages/SystemUI/res/drawable/ic_ksh_key_up.xml
new file mode 100644
index 0000000..36a83b1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_ksh_key_up.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2016 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path android:fillColor="@color/ksh_key_item_color"
+ android:pathData="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z" />
+ <path android:pathData="M0 0h24v24H0z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_disconnected.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_disconnected.xml
new file mode 100644
index 0000000..4d2a35e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_disconnected.xml
@@ -0,0 +1,45 @@
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="26.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="26.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M0 0h26v24H0z"
+ android:fillColor="#00000000"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M21.0,8.5
+ c0.85,0.0 1.6,0.23 2.3,0.62l2.24,-2.79
+ C25.1,5.96 20.26,2.0 13.0,2.0
+ S0.9,5.9 0.42,6.32
+ l12.57,15.6 4.21,-5.17
+ c-0.76,-0.87 -1.22,-2.0 -1.22,-3.25
+ c0.0,-2.76 2.24,-5.0 5.0,-5.0z"
+ android:fillAlpha=".3"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M21.0,10.0
+ c-1.93,0.0 -3.5,1.57 -3.5,3.5l1.75,0.0
+ c0.0,-0.9 0.78,-1.75 1.75,-1.75s1.7,0.78 1.75,1.75
+ c0.0,0.48 -0.2,0.92 -0.51,1.24l-1.09,1.1
+ c-0.6,0.63 -1.02,1.51 -1.02,2.47l0.0,0.44l1.75,0.0
+ c0.0,-1.3 0.39,-1.84 1.03,-2.47l0.78,-0.8
+ c0.5,-0.5 0.82,-1.2 0.82,-1.97
+ C24.5,11.57 22.93,10.0 21.0,10.0z
+ m-0.95,11.95l1.9,0.0l0.0,-1.9l-1.9,0.0l0.0,1.9z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/major_a_b.xml b/packages/SystemUI/res/drawable/major_a_b.xml
new file mode 100644
index 0000000..9900048
--- /dev/null
+++ b/packages/SystemUI/res/drawable/major_a_b.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:name="major_a_b"
+ android:width="16dp"
+ android:viewportWidth="16"
+ android:height="8dp"
+ android:viewportHeight="8" >
+ <group
+ android:name="dot_01"
+ android:translateX="3.25"
+ android:translateY="4" >
+ <group
+ android:name="dot_group" >
+ <path
+ android:name="dot"
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M 0.0,-2.75 l 0.0,0.0 c 1.51878306195,0.0 2.75,1.23121693805 2.75,2.75 l 0.0,0.0 c 0.0,1.51878306195 -1.23121693805,2.75 -2.75,2.75 l 0.0,0.0 c -1.51878306195,0.0 -2.75,-1.23121693805 -2.75,-2.75 l 0.0,0.0 c 0.0,-1.51878306195 1.23121693805,-2.75 2.75,-2.75 Z" />
+ </group>
+ </group>
+</vector>
diff --git a/packages/SystemUI/res/drawable/major_a_b_animation.xml b/packages/SystemUI/res/drawable/major_a_b_animation.xml
new file mode 100644
index 0000000..74d7544
--- /dev/null
+++ b/packages/SystemUI/res/drawable/major_a_b_animation.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<animated-vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/major_a_b" >
+ <target
+ android:name="dot_01"
+ android:animation="@anim/major_a_b_dot_01_animation" />
+ <target
+ android:name="dot"
+ android:animation="@anim/major_a_b_dot_animation" />
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/major_b_a.xml b/packages/SystemUI/res/drawable/major_b_a.xml
new file mode 100644
index 0000000..3115887
--- /dev/null
+++ b/packages/SystemUI/res/drawable/major_b_a.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:name="major_b_a"
+ android:width="16dp"
+ android:viewportWidth="16"
+ android:height="8dp"
+ android:viewportHeight="8" >
+ <group
+ android:name="dot_01"
+ android:translateX="8"
+ android:translateY="4" >
+ <group
+ android:name="dot_group" >
+ <path
+ android:name="dot"
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M -4.75,-2.75 l 9.5,0.0 c 1.51878306195,0.0 2.75,1.23121693805 2.75,2.75 l 0.0,0.0 c 0.0,1.51878306195 -1.23121693805,2.75 -2.75,2.75 l -9.5,0.0 c -1.51878306195,0.0 -2.75,-1.23121693805 -2.75,-2.75 l 0.0,0.0 c 0.0,-1.51878306195 1.23121693805,-2.75 2.75,-2.75 Z" />
+ </group>
+ </group>
+</vector>
diff --git a/packages/SystemUI/res/drawable/major_b_a_animation.xml b/packages/SystemUI/res/drawable/major_b_a_animation.xml
new file mode 100644
index 0000000..cf446e6
--- /dev/null
+++ b/packages/SystemUI/res/drawable/major_b_a_animation.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<animated-vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/major_b_a" >
+ <target
+ android:name="dot_01"
+ android:animation="@anim/major_b_a_dot_01_animation" />
+ <target
+ android:name="dot"
+ android:animation="@anim/major_b_a_dot_animation" />
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/major_b_c.xml b/packages/SystemUI/res/drawable/major_b_c.xml
new file mode 100644
index 0000000..899109e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/major_b_c.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:name="major_b_c"
+ android:width="16dp"
+ android:viewportWidth="16"
+ android:height="8dp"
+ android:viewportHeight="8" >
+ <group
+ android:name="dot_01"
+ android:translateX="8"
+ android:translateY="4" >
+ <group
+ android:name="dot_group" >
+ <path
+ android:name="dot"
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M -4.75,-2.75 l 9.5,0.0 c 1.51878306195,0.0 2.75,1.23121693805 2.75,2.75 l 0.0,0.0 c 0.0,1.51878306195 -1.23121693805,2.75 -2.75,2.75 l -9.5,0.0 c -1.51878306195,0.0 -2.75,-1.23121693805 -2.75,-2.75 l 0.0,0.0 c 0.0,-1.51878306195 1.23121693805,-2.75 2.75,-2.75 Z" />
+ </group>
+ </group>
+</vector>
diff --git a/packages/SystemUI/res/drawable/major_b_c_animation.xml b/packages/SystemUI/res/drawable/major_b_c_animation.xml
new file mode 100644
index 0000000..38e12f4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/major_b_c_animation.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<animated-vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/major_b_c" >
+ <target
+ android:name="dot_01"
+ android:animation="@anim/major_b_c_dot_01_animation" />
+ <target
+ android:name="dot"
+ android:animation="@anim/major_b_c_dot_animation" />
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/major_c_b.xml b/packages/SystemUI/res/drawable/major_c_b.xml
new file mode 100644
index 0000000..cc6c615
--- /dev/null
+++ b/packages/SystemUI/res/drawable/major_c_b.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:name="major_c_b"
+ android:width="16dp"
+ android:viewportWidth="16"
+ android:height="8dp"
+ android:viewportHeight="8" >
+ <group
+ android:name="dot_01"
+ android:translateX="12.75"
+ android:translateY="4" >
+ <group
+ android:name="dot_group" >
+ <path
+ android:name="dot"
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M 0.0,-2.75 l 0.0,0.0 c 1.51878306195,0.0 2.75,1.23121693805 2.75,2.75 l 0.0,0.0 c 0.0,1.51878306195 -1.23121693805,2.75 -2.75,2.75 l 0.0,0.0 c -1.51878306195,0.0 -2.75,-1.23121693805 -2.75,-2.75 l 0.0,0.0 c 0.0,-1.51878306195 1.23121693805,-2.75 2.75,-2.75 Z" />
+ </group>
+ </group>
+</vector>
diff --git a/packages/SystemUI/res/drawable/major_c_b_animation.xml b/packages/SystemUI/res/drawable/major_c_b_animation.xml
new file mode 100644
index 0000000..7f7850d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/major_c_b_animation.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<animated-vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/major_c_b" >
+ <target
+ android:name="dot_01"
+ android:animation="@anim/major_c_b_dot_01_animation" />
+ <target
+ android:name="dot"
+ android:animation="@anim/major_c_b_dot_animation" />
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/minor_a_b.xml b/packages/SystemUI/res/drawable/minor_a_b.xml
new file mode 100644
index 0000000..c5f5c98
--- /dev/null
+++ b/packages/SystemUI/res/drawable/minor_a_b.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:name="minor_a_b"
+ android:alpha="0.3"
+ android:width="16dp"
+ android:viewportWidth="16"
+ android:height="8dp"
+ android:viewportHeight="8" >
+ <group
+ android:name="dot_02"
+ android:translateX="3.25"
+ android:translateY="4" >
+ <group
+ android:name="dot_group" >
+ <path
+ android:name="dot"
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M 0.0,-2.75 l 0.0,0.0 c 1.51878306195,0.0 2.75,1.23121693805 2.75,2.75 l 0.0,0.0 c 0.0,1.51878306195 -1.23121693805,2.75 -2.75,2.75 l 0.0,0.0 c -1.51878306195,0.0 -2.75,-1.23121693805 -2.75,-2.75 l 0.0,0.0 c 0.0,-1.51878306195 1.23121693805,-2.75 2.75,-2.75 Z" />
+ </group>
+ </group>
+</vector>
diff --git a/packages/SystemUI/res/drawable/minor_a_b_animation.xml b/packages/SystemUI/res/drawable/minor_a_b_animation.xml
new file mode 100644
index 0000000..50e20e7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/minor_a_b_animation.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<animated-vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/minor_a_b" >
+ <target
+ android:name="dot_02"
+ android:animation="@anim/minor_a_b_dot_02_animation" />
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/minor_b_a.xml b/packages/SystemUI/res/drawable/minor_b_a.xml
new file mode 100644
index 0000000..3bb08c4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/minor_b_a.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:name="minor_b_a"
+ android:alpha="0.3"
+ android:width="16dp"
+ android:viewportWidth="16"
+ android:height="8dp"
+ android:viewportHeight="8" >
+ <group
+ android:name="dot_02"
+ android:translateX="8"
+ android:translateY="4" >
+ <group
+ android:name="dot_group" >
+ <path
+ android:name="dot"
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M 0.0,-2.75 l 0.0,0.0 c 1.51878306195,0.0 2.75,1.23121693805 2.75,2.75 l 0.0,0.0 c 0.0,1.51878306195 -1.23121693805,2.75 -2.75,2.75 l 0.0,0.0 c -1.51878306195,0.0 -2.75,-1.23121693805 -2.75,-2.75 l 0.0,0.0 c 0.0,-1.51878306195 1.23121693805,-2.75 2.75,-2.75 Z" />
+ </group>
+ </group>
+</vector>
diff --git a/packages/SystemUI/res/drawable/minor_b_a_animation.xml b/packages/SystemUI/res/drawable/minor_b_a_animation.xml
new file mode 100644
index 0000000..460a2f7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/minor_b_a_animation.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<animated-vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/minor_b_a" >
+ <target
+ android:name="dot_02"
+ android:animation="@anim/minor_b_a_dot_02_animation" />
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/minor_b_c.xml b/packages/SystemUI/res/drawable/minor_b_c.xml
new file mode 100644
index 0000000..95c6463
--- /dev/null
+++ b/packages/SystemUI/res/drawable/minor_b_c.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:name="minor_b_c"
+ android:alpha="0.3"
+ android:width="16dp"
+ android:viewportWidth="16"
+ android:height="8dp"
+ android:viewportHeight="8" >
+ <group
+ android:name="dot_02"
+ android:translateX="8"
+ android:translateY="4" >
+ <group
+ android:name="dot_group" >
+ <path
+ android:name="dot"
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M 0.0,-2.75 l 0.0,0.0 c 1.51878306195,0.0 2.75,1.23121693805 2.75,2.75 l 0.0,0.0 c 0.0,1.51878306195 -1.23121693805,2.75 -2.75,2.75 l 0.0,0.0 c -1.51878306195,0.0 -2.75,-1.23121693805 -2.75,-2.75 l 0.0,0.0 c 0.0,-1.51878306195 1.23121693805,-2.75 2.75,-2.75 Z" />
+ </group>
+ </group>
+</vector>
diff --git a/packages/SystemUI/res/drawable/minor_b_c_animation.xml b/packages/SystemUI/res/drawable/minor_b_c_animation.xml
new file mode 100644
index 0000000..53b8bd6
--- /dev/null
+++ b/packages/SystemUI/res/drawable/minor_b_c_animation.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<animated-vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/minor_b_c" >
+ <target
+ android:name="dot_02"
+ android:animation="@anim/minor_b_c_dot_02_animation" />
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/minor_c_b.xml b/packages/SystemUI/res/drawable/minor_c_b.xml
new file mode 100644
index 0000000..523afaa
--- /dev/null
+++ b/packages/SystemUI/res/drawable/minor_c_b.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:name="minor_c_b"
+ android:alpha="0.3"
+ android:width="16dp"
+ android:viewportWidth="16"
+ android:height="8dp"
+ android:viewportHeight="8" >
+ <group
+ android:name="dot_02"
+ android:translateX="12.75"
+ android:translateY="4" >
+ <group
+ android:name="dot_group" >
+ <path
+ android:name="dot"
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M 0.0,-2.75 l 0.0,0.0 c 1.51878306195,0.0 2.75,1.23121693805 2.75,2.75 l 0.0,0.0 c 0.0,1.51878306195 -1.23121693805,2.75 -2.75,2.75 l 0.0,0.0 c -1.51878306195,0.0 -2.75,-1.23121693805 -2.75,-2.75 l 0.0,0.0 c 0.0,-1.51878306195 1.23121693805,-2.75 2.75,-2.75 Z" />
+ </group>
+ </group>
+</vector>
diff --git a/packages/SystemUI/res/drawable/minor_c_b_animation.xml b/packages/SystemUI/res/drawable/minor_c_b_animation.xml
new file mode 100644
index 0000000..bf5e81e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/minor_c_b_animation.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<animated-vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/minor_c_b" >
+ <target
+ android:name="dot_02"
+ android:animation="@anim/minor_c_b_dot_02_animation" />
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/stat_notify_image.xml b/packages/SystemUI/res/drawable/stat_notify_image.xml
new file mode 100644
index 0000000..c8745d7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_notify_image.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M21,19V5c0-1.1-0.9-2-2-2H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14C20.1,21,21,20.1,21,19z
+M8.5,13.5l2.5,3l3.5-4.5l4.5,6H5 L8.5,13.5z" />
+ <path
+ android:pathData="M0,0h24v24H0V0z" />
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/stat_notify_image_error.xml b/packages/SystemUI/res/drawable/stat_notify_image_error.xml
new file mode 100644
index 0000000..b929005
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_notify_image_error.xml
@@ -0,0 +1,30 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M0,0h24v24H0V0z" />
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M17,18H5l3.5-4.5l2.5,3l3.3-4.5l2.7,3.8V8h4V5c0-1.1-0.9-2-2-2H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h12V18z" />
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M19.2,21H21v-1.8h-1.8V21z M19.2,9.9v7.4H21V9.9H19.2z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_data_saver.xml b/packages/SystemUI/res/drawable/stat_sys_data_saver.xml
index a45f9a2..c36678d 100644
--- a/packages/SystemUI/res/drawable/stat_sys_data_saver.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_data_saver.xml
@@ -23,10 +23,12 @@
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFFFF"
- android:pathData="
- M9.0,16.0l2.0,0.0L11.0,8.0L9.0,8.0l0.0,8.0z
- m3.0,-14.0C6.48,2.0 2.0,6.48 2.0,12.0s4.48,10.0 10.0,10.0 10.0,-4.48 10.0,-10.0S17.52,2.0 12.0,2.0z
- m0.0,18.0c-4.41,0.0 -8.0,-3.59 -8.0,-8.0s3.59,-8.0 8.0,-8.0 8.0,3.59 8.0,8.0 -3.59,8.0 -8.0,8.0z
- m1.0,-4.0l2.0,0.0l0.0,-8.0l-2.0,0.0l0.0,8.0z"/>
+ android:pathData="M12.0,19.0c-3.9,0.0 -7.0,-3.1 -7.0,-7.0c0.0,-3.5 2.6,-6.4 6.0,-6.9L11.0,2.0C5.9,2.5 2.0,6.8 2.0,12.0c0.0,5.5 4.5,10.0 10.0,10.0c3.3,0.0 6.2,-1.6 8.1,-4.1l-2.6,-1.5C16.2,18.0 14.2,19.0 12.0,19.0z"/>
+ <path
+ android:fillColor="#4DFFFFFF"
+ android:pathData="M13.0,2.0l0.0,3.0c3.4,0.5 6.0,3.4 6.0,6.9c0.0,0.9 -0.2,1.8 -0.5,2.5l2.6,1.5c0.6,-1.2 0.9,-2.6 0.9,-4.1C22.0,6.8 18.0,2.6 13.0,2.0z"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M16.0,11.0l0.0,2.0 -3.0,0.0 0.0,3.0 -2.0,0.0 0.0,-3.0 -3.0,0.0 0.0,-2.0 3.0,0.0 0.0,-3.0 2.0,0.0 0.0,3.0z"/>
</vector>
</inset>
diff --git a/packages/SystemUI/res/drawable/tv_pip_button_focused.xml b/packages/SystemUI/res/drawable/tv_pip_button_focused.xml
index 5cabb77a..405ea0c 100644
--- a/packages/SystemUI/res/drawable/tv_pip_button_focused.xml
+++ b/packages/SystemUI/res/drawable/tv_pip_button_focused.xml
@@ -17,8 +17,8 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<size
- android:width="36dp"
- android:height="36dp" />
+ android:width="34dp"
+ android:height="34dp" />
<solid
android:color="#4DFFFFFF" />
</shape>
diff --git a/packages/SystemUI/res/drawable/tv_pip_close_button.xml b/packages/SystemUI/res/drawable/tv_pip_close_button.xml
index 86fda0d..186a4ba 100644
--- a/packages/SystemUI/res/drawable/tv_pip_close_button.xml
+++ b/packages/SystemUI/res/drawable/tv_pip_close_button.xml
@@ -19,8 +19,20 @@
<item android:state_focused="true">
<layer-list>
<item android:drawable="@drawable/tv_pip_button_focused" />
- <item android:drawable="@drawable/ic_close_white" />
+ <item android:drawable="@drawable/ic_close_white"
+ android:top="@dimen/tv_pip_button_icon_padding"
+ android:bottom="@dimen/tv_pip_button_icon_padding"
+ android:left="@dimen/tv_pip_button_icon_padding"
+ android:right="@dimen/tv_pip_button_icon_padding" />
</layer-list>
</item>
- <item android:drawable="@drawable/ic_close_white" />
+ <item>
+ <layer-list>
+ <item android:drawable="@drawable/ic_close_white"
+ android:top="@dimen/tv_pip_button_icon_padding"
+ android:bottom="@dimen/tv_pip_button_icon_padding"
+ android:left="@dimen/tv_pip_button_icon_padding"
+ android:right="@dimen/tv_pip_button_icon_padding" />
+ </layer-list>
+ </item>
</selector>
diff --git a/packages/SystemUI/res/drawable/tv_pip_full_button.xml b/packages/SystemUI/res/drawable/tv_pip_full_button.xml
index 332c669..c48dc828 100644
--- a/packages/SystemUI/res/drawable/tv_pip_full_button.xml
+++ b/packages/SystemUI/res/drawable/tv_pip_full_button.xml
@@ -19,8 +19,20 @@
<item android:state_focused="true">
<layer-list>
<item android:drawable="@drawable/tv_pip_button_focused" />
- <item android:drawable="@drawable/ic_fullscreen_white_24dp" />
+ <item android:drawable="@drawable/ic_fullscreen_white_24dp"
+ android:top="@dimen/tv_pip_button_icon_padding"
+ android:bottom="@dimen/tv_pip_button_icon_padding"
+ android:left="@dimen/tv_pip_button_icon_padding"
+ android:right="@dimen/tv_pip_button_icon_padding" />
</layer-list>
</item>
- <item android:drawable="@drawable/ic_fullscreen_white_24dp" />
+ <item>
+ <layer-list>
+ <item android:drawable="@drawable/ic_fullscreen_white_24dp"
+ android:top="@dimen/tv_pip_button_icon_padding"
+ android:bottom="@dimen/tv_pip_button_icon_padding"
+ android:left="@dimen/tv_pip_button_icon_padding"
+ android:right="@dimen/tv_pip_button_icon_padding" />
+ </layer-list>
+ </item>
</selector>
diff --git a/packages/SystemUI/res/drawable/tv_pip_pause_button.xml b/packages/SystemUI/res/drawable/tv_pip_pause_button.xml
index d277b07..bcc8973 100644
--- a/packages/SystemUI/res/drawable/tv_pip_pause_button.xml
+++ b/packages/SystemUI/res/drawable/tv_pip_pause_button.xml
@@ -19,8 +19,20 @@
<item android:state_focused="true">
<layer-list>
<item android:drawable="@drawable/tv_pip_button_focused" />
- <item android:drawable="@drawable/ic_pause_white_24dp" />
+ <item android:drawable="@drawable/ic_pause_white_24dp"
+ android:top="@dimen/tv_pip_button_icon_padding"
+ android:bottom="@dimen/tv_pip_button_icon_padding"
+ android:left="@dimen/tv_pip_button_icon_padding"
+ android:right="@dimen/tv_pip_button_icon_padding" />
</layer-list>
</item>
- <item android:drawable="@drawable/ic_pause_white_24dp" />
+ <item>
+ <layer-list>
+ <item android:drawable="@drawable/ic_pause_white_24dp"
+ android:top="@dimen/tv_pip_button_icon_padding"
+ android:bottom="@dimen/tv_pip_button_icon_padding"
+ android:left="@dimen/tv_pip_button_icon_padding"
+ android:right="@dimen/tv_pip_button_icon_padding" />
+ </layer-list>
+ </item>
</selector>
diff --git a/packages/SystemUI/res/drawable/tv_pip_play_button.xml b/packages/SystemUI/res/drawable/tv_pip_play_button.xml
index fecdc09..f77ea1d 100644
--- a/packages/SystemUI/res/drawable/tv_pip_play_button.xml
+++ b/packages/SystemUI/res/drawable/tv_pip_play_button.xml
@@ -19,8 +19,20 @@
<item android:state_focused="true">
<layer-list>
<item android:drawable="@drawable/tv_pip_button_focused" />
- <item android:drawable="@drawable/ic_play_arrow_white_24dp" />
+ <item android:drawable="@drawable/ic_play_arrow_white_24dp"
+ android:top="@dimen/tv_pip_button_icon_padding"
+ android:bottom="@dimen/tv_pip_button_icon_padding"
+ android:left="@dimen/tv_pip_button_icon_padding"
+ android:right="@dimen/tv_pip_button_icon_padding" />
</layer-list>
</item>
- <item android:drawable="@drawable/ic_play_arrow_white_24dp" />
+ <item>
+ <layer-list>
+ <item android:drawable="@drawable/ic_play_arrow_white_24dp"
+ android:top="@dimen/tv_pip_button_icon_padding"
+ android:bottom="@dimen/tv_pip_button_icon_padding"
+ android:left="@dimen/tv_pip_button_icon_padding"
+ android:right="@dimen/tv_pip_button_icon_padding" />
+ </layer-list>
+ </item>
</selector>
diff --git a/packages/SystemUI/res/layout/forced_resizable_activity.xml b/packages/SystemUI/res/layout/forced_resizable_activity.xml
new file mode 100644
index 0000000..df245bc
--- /dev/null
+++ b/packages/SystemUI/res/layout/forced_resizable_activity.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2016 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:text="@string/forced_resizable_info_text"
+ android:textColor="#ffffff"/>
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml
index 6cb8470..381fb16 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml
@@ -16,7 +16,7 @@
~ limitations under the License
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="match_parent"
android:textSize="14sp"
android:paddingStart="24dp"
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_key_icon_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_key_icon_view.xml
new file mode 100644
index 0000000..0cecb96
--- /dev/null
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_key_icon_view.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2016 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="@dimen/ksh_item_padding"
+ android:layout_marginStart="@dimen/ksh_item_margin_start"
+ android:scaleType="fitXY"
+ android:background="@color/ksh_key_item_background"/>
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml
index 5002c12..1215029 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml
@@ -17,9 +17,9 @@
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginStart="4dp"
- android:padding="4dp"
- android:background="#EEEEEE"
- android:textColor="#8C000000"
+ android:padding="@dimen/ksh_item_padding"
+ android:layout_marginStart="@dimen/ksh_item_margin_start"
+ android:background="@color/ksh_key_item_background"
+ android:textColor="@color/ksh_key_item_color"
android:singleLine="true"
- android:textSize="14sp"/>
+ android:textSize="@dimen/ksh_item_text_size"/>
diff --git a/packages/SystemUI/res/layout/qs_customize_panel_content.xml b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
index 75f8fa4..6438564 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel_content.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
@@ -31,7 +31,8 @@
android:layout_height="0dp"
android:layout_weight="1"
android:scrollIndicators="top"
- android:scrollbars="vertical" />
+ android:scrollbars="vertical"
+ android:importantForAccessibility="no" />
<View
android:layout_width="match_parent"
diff --git a/core/res/res/layout/notification_action_list.xml b/packages/SystemUI/res/layout/qs_customize_tile_divider.xml
similarity index 65%
rename from core/res/res/layout/notification_action_list.xml
rename to packages/SystemUI/res/layout/qs_customize_tile_divider.xml
index 400decc..0d932ac 100644
--- a/core/res/res/layout/notification_action_list.xml
+++ b/packages/SystemUI/res/layout/qs_customize_tile_divider.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
+<!--
+ Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -14,17 +15,11 @@
limitations under the License.
-->
-<LinearLayout
+<View
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/actions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:visibility="gone"
- android:layout_marginBottom="8dp"
- android:showDividers="middle"
- android:divider="?android:attr/listDivider"
- android:dividerPadding="12dp"
- >
- <!-- actions will be added here -->
-</LinearLayout>
+ android:layout_marginStart="16dp"
+ android:layout_marginEnd="16dp"
+ android:background="?android:attr/listDivider"
+ android:importantForAccessibility="no" />
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index cb861ec..b88846b 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -26,9 +26,6 @@
android:clipChildren="false"
android:clipToPadding="false"
android:baselineAligned="false"
- android:background="@drawable/quick_header_bg"
- android:clickable="true"
- android:focusable="true"
>
<LinearLayout
@@ -83,6 +80,10 @@
android:id="@+id/expand_indicator"
android:layout_width="48dp"
android:layout_height="48dp"
+ android:clipToPadding="false"
+ android:clickable="true"
+ android:focusable="true"
+ android:background="?android:attr/selectableItemBackgroundBorderless"
android:padding="12dp" />
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/recents_on_tv.xml b/packages/SystemUI/res/layout/recents_on_tv.xml
index f0bfebe..28ea66d 100644
--- a/packages/SystemUI/res/layout/recents_on_tv.xml
+++ b/packages/SystemUI/res/layout/recents_on_tv.xml
@@ -19,8 +19,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
- android:clipToPadding="false"
- android:layoutDirection="rtl">
+ android:clipToPadding="false">
<com.android.systemui.recents.tv.views.TaskStackHorizontalGridView
android:id="@+id/task_list"
android:layout_width="wrap_content"
@@ -29,20 +28,15 @@
android:clipToPadding="false"
android:descendantFocusability="beforeDescendants"
android:layout_marginTop="@dimen/recents_tv_gird_row_top_margin"
- android:focusable="true" />
- <View
- android:id="@+id/pip_shade"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="gone"
- android:background="#76000000"/>
+ android:focusable="true"
+ android:layoutDirection="rtl" />
- <!-- Placeholder view to handle key events for PIP when it's focused.
- Size and positions will be adjusted to comply with the PIP bounds -->
+ <!-- Placeholder view to give focus to the PIP menus. -->
<View
android:id="@+id/pip"
android:layout_width="0dp"
android:layout_height="0dp"
- android:visibility="gone"
- android:focusable="true" />
+ android:focusable="true"
+ android:visibility="gone" />
+
</com.android.systemui.recents.tv.views.RecentsTvView>
diff --git a/packages/SystemUI/res/layout/recents_stack_action_button.xml b/packages/SystemUI/res/layout/recents_stack_action_button.xml
index 8783261..625e9c1 100644
--- a/packages/SystemUI/res/layout/recents_stack_action_button.xml
+++ b/packages/SystemUI/res/layout/recents_stack_action_button.xml
@@ -18,7 +18,6 @@
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:gravity="start|center_vertical"
android:paddingStart="14dp"
android:paddingEnd="14dp"
android:paddingTop="12dp"
@@ -27,8 +26,6 @@
android:textSize="14sp"
android:textColor="#FFFFFF"
android:textAllCaps="true"
- android:drawableStart="@drawable/ic_history"
- android:drawablePadding="6dp"
android:shadowColor="#99000000"
android:shadowDx="0"
android:shadowDy="2"
diff --git a/packages/SystemUI/res/layout/recents_task_view_header.xml b/packages/SystemUI/res/layout/recents_task_view_header.xml
index 59a2bd9..2b3c5df 100644
--- a/packages/SystemUI/res/layout/recents_task_view_header.xml
+++ b/packages/SystemUI/res/layout/recents_task_view_header.xml
@@ -14,28 +14,28 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
+<!-- The layouts params are calculated in TaskViewHeader.java -->
<com.android.systemui.recents.views.TaskViewHeader
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/task_view_bar"
android:layout_width="match_parent"
- android:layout_height="@dimen/recents_task_view_header_height"
+ android:layout_height="wrap_content"
android:layout_gravity="top|center_horizontal">
<com.android.systemui.recents.views.FixedSizeImageView
android:id="@+id/icon"
android:contentDescription="@string/recents_app_info_button_label"
- android:layout_width="@dimen/recents_task_view_header_icon_width"
- android:layout_height="@dimen/recents_task_view_header_icon_height"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:layout_gravity="center_vertical|start"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:paddingStart="16dp"
android:paddingEnd="12dp" />
<LinearLayout
+ android:id="@+id/title_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|start"
- android:layout_marginStart="56dp"
- android:layout_marginEnd="56dp"
android:orientation="vertical">
<TextView
android:id="@+id/title"
@@ -67,21 +67,20 @@
</LinearLayout>
<com.android.systemui.recents.views.FixedSizeImageView
android:id="@+id/move_task"
- android:layout_width="@dimen/recents_task_view_header_button_width"
- android:layout_height="@dimen/recents_task_view_header_button_height"
- android:layout_marginEnd="@dimen/recents_task_view_header_button_width"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
- android:padding="16dp"
+ android:padding="@dimen/recents_task_view_header_button_padding"
android:src="@drawable/star"
android:background="?android:selectableItemBackground"
android:alpha="0"
android:visibility="gone" />
<com.android.systemui.recents.views.FixedSizeImageView
android:id="@+id/dismiss_task"
- android:layout_width="@dimen/recents_task_view_header_button_width"
- android:layout_height="@dimen/recents_task_view_header_button_height"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
- android:padding="16dp"
+ android:padding="@dimen/recents_task_view_header_button_padding"
android:src="@drawable/recents_dismiss_light"
android:background="?android:selectableItemBackground"
android:alpha="0"
diff --git a/packages/SystemUI/res/layout/recents_task_view_header_overlay.xml b/packages/SystemUI/res/layout/recents_task_view_header_overlay.xml
index 433e69e..cf09b1d 100644
--- a/packages/SystemUI/res/layout/recents_task_view_header_overlay.xml
+++ b/packages/SystemUI/res/layout/recents_task_view_header_overlay.xml
@@ -13,6 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
+<!-- The layouts params are calculated in TaskViewHeader.java -->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
@@ -20,8 +21,8 @@
<com.android.systemui.recents.views.FixedSizeImageView
android:id="@+id/app_icon"
android:contentDescription="@string/recents_app_info_button_label"
- android:layout_width="@dimen/recents_task_view_header_icon_width"
- android:layout_height="@dimen/recents_task_view_header_icon_height"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:layout_gravity="center_vertical|start"
android:paddingTop="8dp"
android:paddingBottom="8dp"
@@ -32,8 +33,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|start"
- android:layout_marginStart="56dp"
- android:layout_marginEnd="112dp"
android:textSize="16sp"
android:textColor="#ffffffff"
android:text="@string/recents_empty_message"
@@ -44,10 +43,10 @@
android:fadingEdge="horizontal" />
<com.android.systemui.recents.views.FixedSizeImageView
android:id="@+id/app_info"
- android:layout_width="@dimen/recents_task_view_header_button_width"
- android:layout_height="@dimen/recents_task_view_header_button_height"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
- android:padding="16dp"
+ android:padding="@dimen/recents_task_view_header_button_padding"
android:background="?android:selectableItemBackground"
android:src="@drawable/recents_info_light" />
</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml b/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml
index 224a0a0..60112be 100644
--- a/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml
+++ b/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml
@@ -112,21 +112,6 @@
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:scaleType="matrix"
- android:src="@drawable/screen_pinning_light_bg_circ" />
-
- <ImageView
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingEnd="@dimen/screen_pinning_request_inner_padding"
- android:paddingStart="@dimen/screen_pinning_request_inner_padding"
- android:paddingTop="@dimen/screen_pinning_request_inner_padding"
- android:scaleType="matrix"
- android:src="@drawable/screen_pinning_bg_circ" />
-
- <ImageView
- android:layout_width="match_parent"
- android:layout_height="match_parent"
android:paddingEnd="@dimen/screen_pinning_request_nav_side_padding"
android:paddingStart="@dimen/screen_pinning_request_nav_side_padding"
android:paddingTop="@dimen/screen_pinning_request_nav_icon_padding"
diff --git a/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml b/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml
index 1e5193f..ebad7a4 100644
--- a/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml
+++ b/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml
@@ -43,21 +43,6 @@
<ImageView
android:layout_height="match_parent"
android:layout_width="match_parent"
- android:scaleType="matrix"
- android:src="@drawable/screen_pinning_light_bg_circ" />
-
- <ImageView
- android:layout_height="match_parent"
- android:layout_width="match_parent"
- android:scaleType="matrix"
- android:paddingLeft="@dimen/screen_pinning_request_inner_padding"
- android:paddingTop="@dimen/screen_pinning_request_inner_padding"
- android:paddingBottom="@dimen/screen_pinning_request_inner_padding"
- android:src="@drawable/screen_pinning_bg_circ" />
-
- <ImageView
- android:layout_height="match_parent"
- android:layout_width="match_parent"
android:scaleType="center"
android:paddingLeft="@dimen/screen_pinning_request_nav_icon_padding"
android:paddingTop="@dimen/screen_pinning_request_nav_side_padding"
diff --git a/packages/SystemUI/res/layout/tv_pip_controls.xml b/packages/SystemUI/res/layout/tv_pip_controls.xml
new file mode 100644
index 0000000..563441f
--- /dev/null
+++ b/packages/SystemUI/res/layout/tv_pip_controls.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2016, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- Layout for {@link com.android.systemui.tv.pip.PipControlsView}. -->
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <LinearLayout
+ android:layout_width="100dp"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="center">
+
+ <ImageView android:id="@+id/full_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:focusable="true"
+ android:src="@drawable/tv_pip_full_button" />
+
+ <TextView android:id="@+id/full_desc"
+ android:layout_width="100dp"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="3dp"
+ android:gravity="center"
+ android:visibility="invisible"
+ android:text="@string/pip_fullscreen"
+ android:fontFamily="sans-serif"
+ android:textSize="12sp"
+ android:textColor="#EEEEEE" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="100dp"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="-50dp"
+ android:orientation="vertical"
+ android:gravity="center">
+
+ <ImageView android:id="@+id/close_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:focusable="true"
+ android:src="@drawable/tv_pip_close_button" />
+
+ <TextView android:id="@+id/close_desc"
+ android:layout_width="100dp"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="3dp"
+ android:gravity="center"
+ android:visibility="invisible"
+ android:text="@string/pip_close"
+ android:fontFamily="sans-serif"
+ android:textSize="12sp"
+ android:textColor="#EEEEEE" />
+ </LinearLayout>
+
+ <LinearLayout android:id="@+id/play_pause"
+ android:layout_width="100dp"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="-50dp"
+ android:orientation="vertical"
+ android:gravity="center" >
+
+ <ImageView android:id="@+id/play_pause_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:focusable="true"
+ android:src="@drawable/tv_pip_pause_button" />
+
+ <TextView android:id="@+id/play_pause_desc"
+ android:layout_width="100dp"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="3dp"
+ android:gravity="center"
+ android:visibility="invisible"
+ android:text="@string/pip_pause"
+ android:fontFamily="sans-serif"
+ android:textSize="12sp"
+ android:textColor="#EEEEEE" />
+ </LinearLayout>
+</merge>
diff --git a/packages/SystemUI/res/layout/tv_pip_menu.xml b/packages/SystemUI/res/layout/tv_pip_menu.xml
index 1fec49e..2647a99 100644
--- a/packages/SystemUI/res/layout/tv_pip_menu.xml
+++ b/packages/SystemUI/res/layout/tv_pip_menu.xml
@@ -26,85 +26,8 @@
android:gravity="top|center_horizontal"
android:clipChildren="false">
- <LinearLayout
- android:layout_width="34dp"
- android:layout_height="wrap_content"
- android:layout_marginEnd="3dp"
- android:orientation="vertical"
- android:gravity="center"
- android:clipChildren="false">
-
- <ImageView android:id="@+id/full_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:focusable="true"
- android:src="@drawable/tv_pip_full_button" />
-
- <TextView android:id="@+id/full_desc"
- android:layout_width="100dp"
- android:layout_height="wrap_content"
- android:layout_marginTop="3dp"
- android:gravity="center"
- android:visibility="invisible"
- android:text="@string/pip_fullscreen"
- android:fontFamily="sans-serif"
- android:textSize="12sp"
- android:textColor="#EEEEEE"
- android:clipChildren="false" />
- </LinearLayout>
-
- <LinearLayout android:id="@+id/play_pause"
- android:layout_width="34dp"
- android:layout_height="wrap_content"
- android:layout_marginStart="3dp"
- android:layout_marginEnd="3dp"
- android:orientation="vertical"
- android:gravity="center"
- android:clipChildren="false">
-
- <ImageView android:id="@+id/play_pause_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:focusable="true"
- android:src="@drawable/tv_pip_pause_button" />
-
- <TextView android:id="@+id/play_pause_desc"
- android:layout_width="100dp"
- android:layout_height="wrap_content"
- android:layout_marginTop="3dp"
- android:gravity="center"
- android:visibility="invisible"
- android:text="@string/pip_pause"
- android:fontFamily="sans-serif"
- android:textSize="12sp"
- android:textColor="#EEEEEE"
- android:clipChildren="false" />
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="34dp"
- android:layout_height="wrap_content"
- android:layout_marginStart="3dp"
- android:orientation="vertical"
- android:gravity="center"
- android:clipChildren="false">
-
- <ImageView android:id="@+id/close_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:focusable="true"
- android:src="@drawable/tv_pip_close_button" />
-
- <TextView android:id="@+id/close_desc"
- android:layout_width="100dp"
- android:layout_height="wrap_content"
- android:layout_marginTop="3dp"
- android:gravity="center"
- android:visibility="invisible"
- android:text="@string/pip_close"
- android:fontFamily="sans-serif"
- android:textSize="12sp"
- android:textColor="#EEEEEE"
- android:clipChildren="false" />
- </LinearLayout>
+ <com.android.systemui.tv.pip.PipControlsView
+ android:id="@+id/pip_controls"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/tv_pip_overlay.xml b/packages/SystemUI/res/layout/tv_pip_overlay.xml
index 1ba423b..64bf3b5 100644
--- a/packages/SystemUI/res/layout/tv_pip_overlay.xml
+++ b/packages/SystemUI/res/layout/tv_pip_overlay.xml
@@ -38,24 +38,4 @@
android:gravity="center"
android:maxLines="2"
android:text="@string/pip_hold_home" />
- <LinearLayout
- android:id="@+id/guide_buttons"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_centerHorizontal="true"
- android:orientation="horizontal">
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/ic_fullscreen_white_24dp" />
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/ic_pause_white_24dp" />
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/ic_close_white" />
- </LinearLayout>
</RelativeLayout>
diff --git a/packages/SystemUI/res/layout/tv_pip_recents_overlay.xml b/packages/SystemUI/res/layout/tv_pip_recents_overlay.xml
new file mode 100644
index 0000000..1e464d8
--- /dev/null
+++ b/packages/SystemUI/res/layout/tv_pip_recents_overlay.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:gravity="top|center_horizontal">
+
+ <com.android.systemui.tv.pip.PipRecentsControlsView
+ android:id="@+id/pip_controls"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent" />
+
+ <View
+ android:id="@+id/recents"
+ android:layout_width="1dp"
+ android:layout_height="1dp"
+ android:focusable="true" />
+</LinearLayout>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index d897956..3fa6245 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Maak <xliff:g id="APP">%s</xliff:g> toe."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> verwerp."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Alle onlangse programme is toegemaak."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Begin tans <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Kennisgewing is toegemaak."</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Ligging deur GPS gestel"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Liggingversoeke aktief"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Verwyder alle kennisgewings."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Kennisgewingsinstellings"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g>-instellings"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Die skerm sal outomaties draai."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"soek"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Kon nie <xliff:g id="APP">%s</xliff:g> begin nie."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> is in veiligmodus gedeaktiveer."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Geskiedenis"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Vee uit"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Vee alles uit"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Hierdie program steun nie veelvuldige vensters nie"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Program steun nie veelvuldige vensters nie"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Verdeel horisontaal"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Wys boaan die kennisgewingslys, verskyn vlugtig op die skerm en laat klank toe"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Meer instellings"</string>
<string name="notification_done" msgid="5279426047273930175">"Klaar"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g>-kennisgewingkontroles"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Kleur en voorkoms"</string>
<string name="night_mode" msgid="3540405868248625488">"Nagmodus"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Kalibreer skerm"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Batterybespaarder is nie beskikbaar wanneer gelaai word nie"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Batterybespaarder"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Verminder werkverrigting en agtergronddata"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Knoppie <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Back"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Op"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Af"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Links"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Regs"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Middel"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Spasie"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Play/Pause"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Stop"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Next"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Previous"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Rewind"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Fast Forward"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Syferpaneel <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Stelsel"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Tuis"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Onlangs"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Terug"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Kennisgewings"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Kortpadsleutels"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Wissel invoermetode"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Programme"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Bystand"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Blaaier"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontakte"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-pos"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Kitsboodskappe"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Musiek"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalender"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Wys saam met volumekontroles"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Moenie steur nie"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Volumeknoppieskortpad"</string>
@@ -540,8 +577,7 @@
<string name="select_keycode" msgid="7413765103381924584">"Kies Sleutelbordknoppie"</string>
<string name="preview" msgid="9077832302472282938">"Voorskou"</string>
<string name="drag_to_add_tiles" msgid="7058945779098711293">"Sleep om teëls by te voeg"</string>
- <!-- no translation found for drag_to_remove_tiles (3361212377437088062) -->
- <skip />
+ <string name="drag_to_remove_tiles" msgid="3361212377437088062">"Sleep hierheen om te verwyder"</string>
<string name="qs_edit" msgid="2232596095725105230">"Wysig"</string>
<string name="tuner_time" msgid="6572217313285536011">"Tyd"</string>
<string-array name="clock_options">
@@ -560,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Skuif op"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Skuif links"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Skuif regs"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Program sal dalk nie met multivenster werk nie"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Dubbeltik om by te voeg."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Posisie <xliff:g id="POSITION">%1$d</xliff:g>. Dubbeltik om te kies."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Skuif <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Verwyder <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> is by posisie <xliff:g id="POSITION">%2$d</xliff:g> gevoeg"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> is verwyder"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> is na posisie <xliff:g id="POSITION">%2$d</xliff:g> geskuif"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Kitsinstellingswysiger."</string>
</resources>
diff --git a/packages/SystemUI/res/values-af/strings_tv.xml b/packages/SystemUI/res/values-af/strings_tv.xml
index 391bf7d..0ed4860 100644
--- a/packages/SystemUI/res/values-af/strings_tv.xml
+++ b/packages/SystemUI/res/values-af/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"Hou "<b>"TUIS"</b>" om PIP te beheer"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Druk en hou die TUIS-knoppie om PIP te beheer"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"Het dit"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Maak toe"</string>
</resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 4286af0..eb5d256 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> አስወግድ።"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> ተሰናብቷል::"</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"ሁሉም የቅርብ ጊዜ ማመልከቻዎች ተሰናብተዋል።"</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> በመጀመር ላይ።"</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"ማሳወቂያ ተወግዷል።"</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"በ GPS የተዘጋጀ ሥፍራ"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"የአካባቢ ጥያቄዎች ነቅተዋል"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"ሁሉንም ማሳወቂያዎች አጽዳ"</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"የማሳወቂያ ቅንብሮች"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"የ<xliff:g id="APP_NAME">%s</xliff:g> ቅንብሮች"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"ማያ ገጽ በራስ ሰር ይዞራል።"</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"ፈልግ"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g>ን መጀመር አልተቻለም።"</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> በጥንቃቄ ሁነታ ውስጥ ታግዷል።"</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"ታሪክ"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"ጥረግ"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"ሁሉንም አጽዳ"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"ይህ መተግበሪያ ብዝሃ-መስኮትን አይደግፍም"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"መተግበሪያው ብዝሃ-መስኮትን አይደግፍም"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"አግድም ክፈል"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"በማሳወቂያዎች ዝርዝር አናት ላይ አሳይ፣ ወደ ማያ ገጹ አሳይና ድምፅ ፍቀድ"</string>
<string name="notification_more_settings" msgid="816306283396553571">"ተጨማሪ ቅንብሮች"</string>
<string name="notification_done" msgid="5279426047273930175">"ተከናውኗል"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> ማሳወቂያ ቁጥጥሮች"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"ቀለም እና መልክ"</string>
<string name="night_mode" msgid="3540405868248625488">"የሌሊት ሁነታ"</string>
<string name="calibrate_display" msgid="5974642573432039217">"ማሳያን ይለኩ"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"ኃይል በሚሞላበት ጊዜ ባትሪ ቆጣቢ አይገኝም"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"ባትሪ ቆጣቢ"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"አፈጻጸምን እና የጀርባ ውሂብን ይቀንሳል"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"አዝራር <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"መነሻ"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"ተመለስ"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"ላይ"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"ወደታች"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"ግራ"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"ቀኝ"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"መሃል"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"ትር"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"ክፍተት"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"አስገባ"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"የኋሊት መደምሰሻ"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"አጫውት/ለአፍታ አቁም"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"አቁም"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"ቀጣይ"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"ቀዳሚ"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"ወደኋላ አጠንጥን"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"በፍጥነት አሳልፍ"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"ገጽ ወደ ላይ"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"ገጽ ወደ ታች"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"ሰርዝ"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"መነሻ"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"መጨረሻ"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"አስገባ"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"የቁጥር ሰሌዳ <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"ሥርዓት"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"መነሻ"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"የቅርብ ጊዜዎቹ"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"ተመለስ"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"ማሳወቂያዎች"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"የቁልፍ ሰሌዳ አቋራጮች"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"የግቤት ስልት ቀይር"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"መተግበሪያዎች"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"ረዳት"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"አሳሽ"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"እውቂያዎች"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"ኢሜይል"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"ፈጣን መልዕክት"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"ሙዚቃ"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"የቀን መቁጠሪያ"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"ከድምፅ መቆጣጠሪያዎች ጋር አሳይ"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"አትረብሽ"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"የድምፅ አዝራሮች አቋራጭ"</string>
@@ -540,8 +577,7 @@
<string name="select_keycode" msgid="7413765103381924584">"የቁልፍ ሰሌዳ አዝራር ይምረጡ"</string>
<string name="preview" msgid="9077832302472282938">"ቅድመ-እይታ"</string>
<string name="drag_to_add_tiles" msgid="7058945779098711293">"ሰቆችን ለማከል ይጎትቱ"</string>
- <!-- no translation found for drag_to_remove_tiles (3361212377437088062) -->
- <skip />
+ <string name="drag_to_remove_tiles" msgid="3361212377437088062">"ለማስወገድ ወደዚህ ይጎትቱ"</string>
<string name="qs_edit" msgid="2232596095725105230">"አርትዕ"</string>
<string name="tuner_time" msgid="6572217313285536011">"ሰዓት"</string>
<string-array name="clock_options">
@@ -560,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"ወደ ላይ ሂድ"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"ወደ ግራ ሂድ"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"ወደ ቀኝ ሂድ"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"መተግበሪያ ከብዝሃ-መስኮት ጋር ላይሠራ ይችላል"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"ቦታ <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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>። ለማከል ሁለቴ መታ ያድርጉ።"</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"ቦታ <xliff:g id="POSITION">%1$d</xliff:g>። ለመምረጥ ሁለቴ መታ ያድርጉ።"</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"<xliff:g id="TILE_NAME">%1$s</xliff:g>ን ይውሰዱ"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"<xliff:g id="TILE_NAME">%1$s</xliff:g>ን ያስወግዱ"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ወደ ቦታ <xliff:g id="POSITION">%2$d</xliff:g> ታክሏል"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ተወግዷል"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ወደ ቦታ <xliff:g id="POSITION">%2$d</xliff:g> ተወስዷል"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"የፈጣን ቅንብሮች አርታዒ።"</string>
</resources>
diff --git a/packages/SystemUI/res/values-am/strings_tv.xml b/packages/SystemUI/res/values-am/strings_tv.xml
index 19e27ca..9df1916 100644
--- a/packages/SystemUI/res/values-am/strings_tv.xml
+++ b/packages/SystemUI/res/values-am/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"PIPን ለመቆጣጠር "<b>"መነሻ"</b>"ን ይያዙ"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"PIPን ለመቆጣጠር የመነሻ አዝራሩን ተጭነው ይያዙ"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"ገባኝ"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"አሰናብት"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 5d859ac..82a772c 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -172,6 +172,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"إزالة <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"تمت إزالة <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"تم تجاهل كل التطبيقات المستخدمة مؤخرًا."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"جارٍ بدء <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"تم تجاهل الإشعار."</string>
@@ -240,8 +242,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"تم تعيين الموقع بواسطة GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"طلبات الموقع نشطة"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"محو جميع الإشعارات."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"و<xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"إعدادات الإشعارات"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"إعدادات <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"سيتم تدوير الشاشة تلقائيًا."</string>
@@ -314,8 +315,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"بحث"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"تعذر بدء <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"تم تعطيل <xliff:g id="APP">%s</xliff:g> في الوضع الآمن."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"السجلّ"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"محو"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"مسح الكل"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"لا يتيح هذا التطبيق النوافذ المتعددة."</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"لا يتيح التطبيق النوافذ المتعددة."</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"تقسيم أفقي"</string>
@@ -484,8 +484,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"عرض هذا الإشعار بأعلى قائمة الإشعارات وعرضه بسرعة على الشاشة مع السماح بإصدار تنبيه صوتي"</string>
<string name="notification_more_settings" msgid="816306283396553571">"المزيد من الإعدادات"</string>
<string name="notification_done" msgid="5279426047273930175">"تم"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"عناصر التحكم في إشعارات <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"اللون والمظهر"</string>
<string name="night_mode" msgid="3540405868248625488">"الوضع الليلي"</string>
<string name="calibrate_display" msgid="5974642573432039217">"معايرة الشاشة"</string>
@@ -505,10 +504,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"وضع توفير شحن البطارية غير متاح أثناء الشحن."</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"توفير شحن البطارية"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"لخفض مستوى الأداء وبيانات الخلفية"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"الزر <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Back"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"أعلى"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"أسفل"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"يسار"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"يمين"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"وسط"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"مسافة"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"تشغيل / إيقاف مؤقت"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"إيقاف"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"التالي"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"السابق"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"إرجاع"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"تقديم سريع"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"الرئيسية"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"لوحة الأرقام <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"النظام"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"الشاشة الرئيسية"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"الأحدث"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"رجوع"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"الإشعارات"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"اختصارات لوحة المفاتيح"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"تبديل أسلوب الإدخال"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"التطبيقات"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"التطبيق المساعد"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"المتصفح"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"جهات الاتصال"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"البريد الإلكتروني"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"الرسائل الفورية"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"الموسيقى"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"التقويم"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"عرض مع عناصر التحكم في مستوى الصوت"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"الرجاء عدم الإزعاج"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"اختصار أزرار مستوى الصوت"</string>
@@ -563,4 +600,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"نقل لأعلى"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"نقل لليسار"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"نقل لليمين"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"يمكن ألا يعمل التطبيق مع وضع النوافذ المتعددة."</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"الموضع <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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. انقر نقرًا مزدوجًا للإضافة."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"الموضع <xliff:g id="POSITION">%1$d</xliff:g>. انقر نقرًا مزدوجًا للتحديد."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"نقل <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"إزالة <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"تمت إضافة <xliff:g id="TILE_NAME">%1$s</xliff:g> إلى الموضع <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"تمت إزالة <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"تم نقل <xliff:g id="TILE_NAME">%1$s</xliff:g> إلى الموضع <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"برنامج تعديل الإعدادات السريعة."</string>
</resources>
diff --git a/packages/SystemUI/res/values-ar/strings_tv.xml b/packages/SystemUI/res/values-ar/strings_tv.xml
index db91b69..e6fbffc 100644
--- a/packages/SystemUI/res/values-ar/strings_tv.xml
+++ b/packages/SystemUI/res/values-ar/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"اضغط "<b>"الرئيسية"</b>" للتحكم في PIP"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"اضغط مع الاستمرار على زر الشاشة الرئيسية للتحكم في PIP"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"حسنًا"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"رفض"</string>
</resources>
diff --git a/packages/SystemUI/res/values-az-rAZ/strings.xml b/packages/SystemUI/res/values-az-rAZ/strings.xml
index ad106f4..59b3141 100644
--- a/packages/SystemUI/res/values-az-rAZ/strings.xml
+++ b/packages/SystemUI/res/values-az-rAZ/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> kənarlaşdırın."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> çıxarıldı."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Bütün son tətbiqlər kənarlaşdırıldı."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> başlanır."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Bildiriş uzaqlaşdırıldı."</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Yer GPS tərəfindən müəyyən edildi"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Məkan sorğuları arxivi"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Bütün bildirişləri sil."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Bildiriş ayarları"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> ayarları"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Ekran avtomatik döndəriləcək."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"axtarış"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> başlana bilmir."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> güvənli rejimdə deaktiv edildi."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Tarixçə"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Təmizləyin"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Hamısını silin"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Bu tətbiq çoxsaylı pəncərəni dəstəkləmir"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Bu tətbiq çoxsaylı pəncərəni dəstəkləyir"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Üfüqi Böl"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Bildirişlər siyahısında yuxarıda göstərin, ekrana nəzər salın və səsə icazə verin"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Daha çox ayar"</string>
<string name="notification_done" msgid="5279426047273930175">"Hazırdır"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> bildiriş nəzarəti"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Rəng və görünüş"</string>
<string name="night_mode" msgid="3540405868248625488">"Gecə rejimi"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Ekranı kalibrləyin"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Enerji Qənaəti doldurulma zamanı əlçatan deyil"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Enerji Qənaəti"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Performansı azaldır və arxa fon datasını məhdudlaşdırır"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Düymə <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Əsas səhifə"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Geri"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Yuxarı"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Aşağı"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Sol"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Sağ"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Mərkəz"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Boşluq"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Daxil olun"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Gerisil"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Oxut/Durdur"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Dayandırın"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Növbəti"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Öncəki"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Geri ötürmə"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"İrəli Ötürmə"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Yuxarı Səhifə"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Aşağı Səhifə"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Silin"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Əsas səhifə"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"Son"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Daxil edin"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Nömrələr"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Rəqəmli düymələr <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Sistem"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Əsas səhifə"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Sonuncular"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Geri"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Bildirişlər"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Klaviatura qısa yolları"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Daxiletmə metoduna keçin"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Tətbiqlər"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Yardım"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Brauzer"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontaktlar"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-poçt"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Musiqi"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Təqvim"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Həcm nəzarəti ilə göstərin"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Narahat etməyin"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Səs düymələri qısayolu"</string>
@@ -540,8 +577,7 @@
<string name="select_keycode" msgid="7413765103381924584">"Klaviatura Düyməsi Seçin"</string>
<string name="preview" msgid="9077832302472282938">"Önizləmə"</string>
<string name="drag_to_add_tiles" msgid="7058945779098711293">"Xanalar əlavə etmək üçün sürüşdürün"</string>
- <!-- no translation found for drag_to_remove_tiles (3361212377437088062) -->
- <skip />
+ <string name="drag_to_remove_tiles" msgid="3361212377437088062">"Silmək üçün bura sürüşdürün"</string>
<string name="qs_edit" msgid="2232596095725105230">"Redaktə edin"</string>
<string name="tuner_time" msgid="6572217313285536011">"Vaxt"</string>
<string-array name="clock_options">
@@ -560,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Yuxarıya keçin"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Sola köçürün"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Sağa köçürün"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Tətbiq çoxsaylı pəncərə ilə işləməyə bilər."</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"<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="8133209638023882667">"<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_position_label" msgid="5055306305919289819">"<xliff:g id="POSITION">%1$d</xliff:g> mövqeyi. Seçmək üçün iki dəfə tıklayın."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"<xliff:g id="TILE_NAME">%1$s</xliff:g> köçürün"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"<xliff:g id="TILE_NAME">%1$s</xliff:g> silin"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> <xliff:g id="POSITION">%2$d</xliff:g> mövqeyinə əlavə edildi"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> silindi"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> <xliff:g id="POSITION">%2$d</xliff:g> mövqeyinə köçürüldü"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Sürətli ayarlar redaktoru."</string>
</resources>
diff --git a/packages/SystemUI/res/values-az-rAZ/strings_tv.xml b/packages/SystemUI/res/values-az-rAZ/strings_tv.xml
index a7a6c5d..63fc9fd 100644
--- a/packages/SystemUI/res/values-az-rAZ/strings_tv.xml
+++ b/packages/SystemUI/res/values-az-rAZ/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"PIP idarı etmək üçün "<b>"Əsas səhifəni"</b>" tutub saxlayın"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"PİP nəzarət etmək üçün ƏSAS EKRAN düyməni basıb saxlayın"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"Anladım"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Rədd edin"</string>
</resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index edec332..0945885 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -169,6 +169,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Odbacite <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Aplikacija <xliff:g id="APP">%s</xliff:g> je odbačena."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Sve nedavno korišćene aplikacije su odbačene."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Pokrećemo <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Obaveštenje je odbačeno."</string>
@@ -237,8 +239,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Lokaciju je podesio GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Ima aktivnih zahteva za lokaciju"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Obriši sva obaveštenja."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"i još <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Podešavanja obaveštenja"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Podešavanja za <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Ekran će se automatski rotirati."</string>
@@ -311,8 +312,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"pretraži"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Pokretanje aplikacije <xliff:g id="APP">%s</xliff:g> nije uspelo."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"Aplikacija <xliff:g id="APP">%s</xliff:g> je onemogućena u bezbednom režimu."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Istorija"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Obriši"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Obriši sve"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Ova aplikacija ne podržava režim sa više prozora"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Aplikacija ne podržava režim sa više prozora"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Podeli horizontalno"</string>
@@ -481,8 +481,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Prikazuju se u vrhu liste obaveštenja, nakratko se prikazuju na ekranu i emituju zvuk"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Još podešavanja"</string>
<string name="notification_done" msgid="5279426047273930175">"Gotovo"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"Kontrole obaveštenja za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Boja i izgled"</string>
<string name="night_mode" msgid="3540405868248625488">"Noćni režim"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Kalibrišite ekran"</string>
@@ -502,10 +501,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Ušteda baterije nije dostupna tokom punjenja"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Ušteda baterije"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Smanjuje performanse i pozadinske podatke"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Dugme <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Taster Početna"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Taster Nazad"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Taster sa strelicom nagore"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Taster sa strelicom nadole"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Taster sa strelicom nalevo"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Taster sa strelicom nadesno"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Taster sa centralnom strelicom"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tabulator"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Taster za razmak"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Taster za brisanje unazad"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Taster za reprodukciju/pauziranje"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Taster za zaustavljanje"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Taster Sledeća"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Taster Prethodna"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Taster za premotavanje unazad"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Taster za premotavanje unapred"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Taster za stranicu nagore"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Taster za stranicu nadole"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Taster za brisanje"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Taster Početna"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"Taster za kraj"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Taster za umetanje"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Taster <xliff:g id="NAME">%1$s</xliff:g> na numeričkoj tastaturi"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Sistem"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Početni"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Nedavni sadržaj"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Nazad"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Obaveštenja"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Tasterske prečice"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Promeni metod unosa"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Aplikacije"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Aplikacija za pomoć"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Pregledač"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontakti"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Imejl"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Razmena trenutnih poruka"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Muzika"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalendar"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Prikaži sa kontrolama jačine zvuka"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Ne uznemiravaj"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Prečica za dugmad za jačinu zvuka"</string>
@@ -560,4 +597,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Pomeri nagore"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Pomeri ulevo"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Pomeri udesno"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Aplikacija možda neće funkcionisati sa više prozora"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"<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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Dvaput dodirnite da biste dodali."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"<xliff:g id="POSITION">%1$d</xliff:g>. pozicija. Dvaput dodirnite da biste izabrali."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Premesti pločicu <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Ukloni pločicu <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"Pločica <xliff:g id="TILE_NAME">%1$s</xliff:g> je dodata na <xliff:g id="POSITION">%2$d</xliff:g>. poziciju"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"Pločica <xliff:g id="TILE_NAME">%1$s</xliff:g> je uklonjena"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"Pločica <xliff:g id="TILE_NAME">%1$s</xliff:g> je premeštena na <xliff:g id="POSITION">%2$d</xliff:g>. poziciju"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Uređivač za Brza podešavanja."</string>
</resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings_tv.xml b/packages/SystemUI/res/values-b+sr+Latn/strings_tv.xml
index a285627..d026d2c 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings_tv.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109"><b>"POČETNI EKRAN"</b>" kont. PIP"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Pritisnite i zadržite dugme POČETNI EKRAN da biste kontrolisali PIP"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"Važi"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Odbaci"</string>
</resources>
diff --git a/packages/SystemUI/res/values-be-rBY/strings.xml b/packages/SystemUI/res/values-be-rBY/strings.xml
index e5771d7..726a0e7 100644
--- a/packages/SystemUI/res/values-be-rBY/strings.xml
+++ b/packages/SystemUI/res/values-be-rBY/strings.xml
@@ -172,6 +172,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Выдаліць <xliff:g id="APP">%s</xliff:g> са спіса апошніх."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> выдалены."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Усе апошнія праграмы адхілены."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Запускаецца <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Апавяшчэнне прапушчана."</string>
@@ -240,8 +242,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Месца задана праз GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Ёсць актыўныя запыты пра месцазнаходжанне"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Выдалiць усе апавяшчэннi."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Налады апавяшчэнняў"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Налады <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Экран паварочваецца аўтаматычна."</string>
@@ -314,8 +315,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"пошук"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Не атрымалася запусціць <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> адключана ў бяспечным рэжыме."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Гісторыя"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Ачысціць"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Ачысціць усё"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Гэта праграма не падтрымлівае функцыю некалькіх вокнаў"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Праграма не падтрымлівае функцыю некалькіх вокнаў"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Падзяліць гарызантальна"</string>
@@ -484,8 +484,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Паказваць уверсе спіса апавяшчэнняў, хутка паказаць на экране і дазволіць прайграванне гуку"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Дадатковыя налады"</string>
<string name="notification_done" msgid="5279426047273930175">"Гатова"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"Элементы кантролю апавяшчэнняў <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Колер і выгляд"</string>
<string name="night_mode" msgid="3540405868248625488">"Начны рэжым"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Каліброўка дысплэя"</string>
@@ -505,10 +504,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Эканомія зараду акумулятара недаступная падчас зарадкі"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Эканомія зараду"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Памяншае прадукцыйнасць і фонавую перадачу даных"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Кнопка <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Назад"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Уверх"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Уніз"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Улева"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Управа"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Цэнтр"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Прабел"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Прайграванне/Паўза"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Спыніць"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Наступны"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Папярэдні"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Перамотка назад"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Перамотка ўперад"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Лічбавая клавіятура: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Сістэмныя"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Галоўная"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Апошнія"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Назад"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Апавяшчэнні"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Спалучэнні клавіш для хуткага доступу"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Пераключэнне рэжыму ўводу"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Праграмы"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Памочнік"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Браўзер"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Кантакты"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Электронная пошта"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Iмгненныя паведамленнi"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Музыка"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Каляндар"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Паказаць з рэгулятарамі гучнасці"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Не турбаваць"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Доступ праз кнопкі рэгулявання гучнасці"</string>
@@ -544,8 +581,7 @@
<string name="select_keycode" msgid="7413765103381924584">"Выберыце клавішу клавіятуры"</string>
<string name="preview" msgid="9077832302472282938">"Папярэдні прагляд"</string>
<string name="drag_to_add_tiles" msgid="7058945779098711293">"Перацягніце, каб дадаць пліткі"</string>
- <!-- no translation found for drag_to_remove_tiles (3361212377437088062) -->
- <skip />
+ <string name="drag_to_remove_tiles" msgid="3361212377437088062">"Перацягніце сюды, каб выдаліць"</string>
<string name="qs_edit" msgid="2232596095725105230">"Рэдагаваць"</string>
<string name="tuner_time" msgid="6572217313285536011">"Час"</string>
<string-array name="clock_options">
@@ -564,4 +600,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Перамясціць уверх"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Перамясціць улева"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Перамясціць управа"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Праграма можа не працаваць у рэжыме некалькіх вокнаў"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"Месца: <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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Краніце двойчы, каб дадаць."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Месца: <xliff:g id="POSITION">%1$d</xliff:g>. Краніце двойчы, каб выбраць."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Перамясціць <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Выдаліць <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"Плітка <xliff:g id="TILE_NAME">%1$s</xliff:g> дададзена ў наступнае месца: <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"Плітка <xliff:g id="TILE_NAME">%1$s</xliff:g> выдалена"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> перамешчана ў наступнае месца: <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Рэдактар хуткіх налад."</string>
</resources>
diff --git a/packages/SystemUI/res/values-be-rBY/strings_tv.xml b/packages/SystemUI/res/values-be-rBY/strings_tv.xml
index 1c93d24..6138155 100644
--- a/packages/SystemUI/res/values-be-rBY/strings_tv.xml
+++ b/packages/SystemUI/res/values-be-rBY/strings_tv.xml
@@ -29,6 +29,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"Утрым. "<b>"HOME"</b>" для кір. PIP"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Націсніце і ўтрымлівайце кнопку HOME для кіравання PIP"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"Зразумела"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Адхіліць"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 6c65f9f..479087a 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Отхвърляне на <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Приложението <xliff:g id="APP">%s</xliff:g> е отхвърлено."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Всички скорошни приложения са отхвърлени."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> се стартира."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Известието е отхвърлено."</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Местоположението е зададено от GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Активни заявки за местоположение"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Изчистване на всички известия."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Настройки за известия"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Настройки за <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Екранът ще се завърта автоматично."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"търсене"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> не можа да стартира."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"Приложението <xliff:g id="APP">%s</xliff:g> е деактивирано в безопасния режим."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"История"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Изчистване"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Изчистване на всичко"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Това приложение не поддържа няколко прозореца"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Приложението не поддържа няколко прозореца"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Хоризонтално разделяне"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Показване най-горе в списъка с известия, както и на екрана и разрешаване на звуков сигнал"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Още настройки"</string>
<string name="notification_done" msgid="5279426047273930175">"Готово"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"Контроли за известията от <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Цвят и облик"</string>
<string name="night_mode" msgid="3540405868248625488">"Нощен режим"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Калибриране на дисплея"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Режимът за запазване на батерията не е налице при зареждане"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Режим за запазване на батерията"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Намалява ефективността и данните на заден план"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Бутон „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Начало"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Назад"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Нагоре"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Надолу"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Наляво"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Надясно"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Център"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Интервал"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Пускане/пауза"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Спиране"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Напред"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Назад"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Превъртане назад"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Превъртане напред"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Страница нагоре"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Страница надолу"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Изтриване"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Цифрова клавиатура – <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Системни"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Начало"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Скорошни"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Назад"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Известия"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Клавишни комбинации"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Превключване на метода на въвеждане"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Приложения"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Помощно приложение"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Браузър"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Контакти"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Електронна поща"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Незабавни съобщения"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Музика"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Календар"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Показване с контролите за силата на звука"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Не безпокойте"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Пряк път към бутоните за силата на звука"</string>
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Преместване нагоре"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Преместване наляво"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Преместване надясно"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Приложението може да не работи в режим за няколко прозореца"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"Позиция <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="8133209638023882667">"„<xliff:g id="TILE_NAME">%1$s</xliff:g>“. Докоснете двукратно, за да добавите."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Позиция <xliff:g id="POSITION">%1$d</xliff:g>. Докоснете двукратно, за да изберете."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Преместване на „<xliff:g id="TILE_NAME">%1$s</xliff:g>“"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Премахване на „<xliff:g id="TILE_NAME">%1$s</xliff:g>“"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"Добавихте „<xliff:g id="TILE_NAME">%1$s</xliff:g>“ към позиция <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"Премахнахте „<xliff:g id="TILE_NAME">%1$s</xliff:g>“"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"Преместихте „<xliff:g id="TILE_NAME">%1$s</xliff:g>“ на позиция <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Редактор за бързи настройки."</string>
</resources>
diff --git a/packages/SystemUI/res/values-bg/strings_tv.xml b/packages/SystemUI/res/values-bg/strings_tv.xml
index 9926ef8..38e251b 100644
--- a/packages/SystemUI/res/values-bg/strings_tv.xml
+++ b/packages/SystemUI/res/values-bg/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"Контр. на PIP: Задр. "<b>"HOME"</b></string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"За контролиране на PIP натиснете и задръжте бутона „HOME“"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"Разбрах"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Отхвърляне"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bn-rBD/strings.xml b/packages/SystemUI/res/values-bn-rBD/strings.xml
index 446d4af..ccbcb6e 100644
--- a/packages/SystemUI/res/values-bn-rBD/strings.xml
+++ b/packages/SystemUI/res/values-bn-rBD/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> খারিজ করুন।"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> খারিজ করা হয়েছে৷"</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"সমস্ত সাম্প্রতিক অ্যাপ্লিকেশন খারিজ করা হয়েছে।"</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> তারাঙ্কিত করা হচ্ছে।"</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"বিজ্ঞপ্তি খারিজ করা হয়েছে৷"</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"GPS এর দ্বারা সেট করা অবস্থান"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"অবস্থান অনুরোধ সক্রিয় রয়েছে"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"সমস্ত বিজ্ঞপ্তি সাফ করুন৷"</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>টি"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"বিজ্ঞপ্তির সেটিংস"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> সেটিংস"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"স্ক্রীন স্বয়ংক্রিয়ভাবে ঘুরে যাবে৷"</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"অনুসন্ধান"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> শুরু করা যায়নি৷"</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"নিরাপদ মোডে <xliff:g id="APP">%s</xliff:g> অক্ষম করা হয়েছে৷"</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"ইতিহাস"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"সাফ করুন"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"সবকিছু সাফ করুন"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"এই অ্যাপ্লিকেশানটি মাল্টি-উইন্ডো সমর্থন করে না"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"অ্যাপ্লিকেশানগুলি মাল্টি-উইন্ডো সমর্থন করে না"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"অনুভূমিক স্প্লিট"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"বিজ্ঞপ্তি তালিকার শীর্ষে দেখানো হয় এবং স্ক্রীনের উপরে প্রদর্শিত এবং শব্দ করার মঞ্জুরি দেয়"</string>
<string name="notification_more_settings" msgid="816306283396553571">"আরো সেটিংস"</string>
<string name="notification_done" msgid="5279426047273930175">"সম্পন্ন"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> বিজ্ঞপ্তির নিয়ন্ত্রণগুলি"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"রঙ এবং চেহারা"</string>
<string name="night_mode" msgid="3540405868248625488">"রাতের মোড"</string>
<string name="calibrate_display" msgid="5974642573432039217">"প্রদর্শন ক্যালিব্রেট করুন"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"চার্জ করার সময় ব্যাটারি সেভার উপলব্ধ নয়"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"ব্যাটারি সেভার"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"কার্য-সম্পাদনা ও পশ্চাদপট ডেটাকে কমিয়ে দেয়"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"<xliff:g id="NAME">%1$s</xliff:g> বোতাম"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"হোম"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"ফিরুন"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"উপরে"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"নীচে"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"বাম"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"ডান"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"কেন্দ্র"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"ট্যাব"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"স্পেস"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"এন্টার"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"ব্যাকস্পেস"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"প্লে/বিরতি"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"থামান"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"পরবর্তী"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"পূর্ববর্তী"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"পেছনের দিকে যান"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"দ্রুত ফরওয়ার্ড"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"পেজ আপ"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"পেজ ডাউন"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"মুছুন"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"হোম"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"শেষ"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"ঢোকান"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"সংখ্যা লক"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"সংখ্যাপ্যাড <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"সিস্টেম"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"হোম"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"সাম্প্রতিকগুলি"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"পিছনে"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"বিজ্ঞপ্তিগুলি"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"কীবোর্ড শর্টকাট"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"ইনপুট পদ্ধতি পাল্টান"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"অ্যাপ্লিকেশানগুলি"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"সহযোগিতা"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"ব্রাউজার"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"পরিচিতি"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"ইমেল"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"সংগীত"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"ক্যালেন্ডার"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"ভলিউম নিয়ন্ত্রণ সহ দেখান"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"বিরক্ত করবেন না"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"ভলিউম বোতামের শর্টকাট"</string>
@@ -544,8 +581,8 @@
<string name="qs_edit" msgid="2232596095725105230">"সম্পাদনা করুন"</string>
<string name="tuner_time" msgid="6572217313285536011">"সময়"</string>
<string-array name="clock_options">
- <item msgid="5965318737560463480">"ঘন্টা, মিনিট, এবং সেকেন্ড দেখান"</item>
- <item msgid="1427801730816895300">"ঘন্টা এবং মিনিট দেখান (ডিফল্ট)"</item>
+ <item msgid="5965318737560463480">"ঘণ্টা, মিনিট, এবং সেকেন্ড দেখান"</item>
+ <item msgid="1427801730816895300">"ঘণ্টা এবং মিনিট দেখান (ডিফল্ট)"</item>
<item msgid="3830170141562534721">"এই আইকনটি দেখাবেন না"</item>
</string-array>
<string-array name="battery_options">
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"উপরে সরান"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"বাঁয়ে সরান"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"ডানে সরান"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"একাধিক-উইন্ডো দিয়ে অ্যাপ কাজ নাও করতে পারে"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"<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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>৷ যোগ করতে দুবার আলতো চাপুন৷"</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"<xliff:g id="POSITION">%1$d</xliff:g> অবস্থান৷ নির্বাচন করতে দুবার আলতো চাপুন৷"</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"<xliff:g id="TILE_NAME">%1$s</xliff:g> সরান"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"<xliff:g id="TILE_NAME">%1$s</xliff:g> সরান"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="POSITION">%2$d</xliff:g> অবস্থানে <xliff:g id="TILE_NAME">%1$s</xliff:g> যোগ করা হয়েছে"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> মোছা হয়েছে"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> <xliff:g id="POSITION">%2$d</xliff:g> এ সরানো হয়েছে"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"দ্রুত সেটিংস সম্পাদক৷"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bn-rBD/strings_tv.xml b/packages/SystemUI/res/values-bn-rBD/strings_tv.xml
index cbed4a1..6fa2d5b 100644
--- a/packages/SystemUI/res/values-bn-rBD/strings_tv.xml
+++ b/packages/SystemUI/res/values-bn-rBD/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"PIP নিয়ন্ত্রণ করতে "<b>"হোম"</b>" কী ধরে রাখুন"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"PIP নিয়ন্ত্রণ করতে HOME বোতামটিকে টিপুন ও ধরে থাকুন"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"বুঝেছি"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"খারিজ করুন"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bs-rBA/strings.xml b/packages/SystemUI/res/values-bs-rBA/strings.xml
index 024bf70..c5398a2 100644
--- a/packages/SystemUI/res/values-bs-rBA/strings.xml
+++ b/packages/SystemUI/res/values-bs-rBA/strings.xml
@@ -169,6 +169,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Odbaci aplikaciju <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Aplikacija <xliff:g id="APP">%s</xliff:g> uklonjena."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Sve nedavno korištene aplikacije su odbačene."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Pokrećem aplikaciju <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Obavještenje je uklonjeno."</string>
@@ -237,8 +239,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Lokacija utvrđena GPS signalom"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Aktiviran je zahtjev za lokaciju"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Uklanjanje svih obavještenja."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Postavke obavještenja"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Postavke aplikacije <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Ekran će se automatski rotirati."</string>
@@ -311,8 +312,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"pretraga"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Aplikacija <xliff:g id="APP">%s</xliff:g> nije pokrenuta."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> je onemogućena u sigurnom načinu rada."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Historija"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Obriši"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Obriši sve"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Ova aplikacija ne podržava rad sa više prozora"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Aplikacija ne podržava rad sa više prozora"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Podjela po horizontali"</string>
@@ -481,8 +481,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Prikaži na vrhu liste obavještenja, kratko prikaži na ekranu i dozvoli zvuk"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Više postavki"</string>
<string name="notification_done" msgid="5279426047273930175">"Gotovo"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"Kontrole <xliff:g id="APP_NAME">%1$s</xliff:g> obavještenja"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Boja i izgled"</string>
<string name="night_mode" msgid="3540405868248625488">"Noćni način rada"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Kalibracija zaslona"</string>
@@ -502,10 +501,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Ušteda baterije je isključena prilikom punjenja"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Ušteda baterije"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Ograničava rad i prijenos podataka u pozadini"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Dugme <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Tipka za početak"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Nazad"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Gore"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Dolje"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Lijevo"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Desno"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Sredina"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tabulator"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Razmaknica"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Tipka za novi red"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Tipka za brisanje"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Pokreni/pauziraj"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Zaustavi"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Sljedeće"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Prethodno"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Premotaj"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Ubrzaj"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Tipka Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Tipka Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Tipka za brisanje"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Tipka za početak"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"Kraj"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Tipka za umetanje"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Tipka Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Numerička tastatura <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Sistem"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Početak"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Nedavni ekrani"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Nazad"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Obavještenja"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Skracenice tastature"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Promijeni način unosa"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Aplikacije"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Pomoć"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Preglednik"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontakti"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-pošta"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Muzika"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalendar"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Prikazati sa kontrolama jačine zvuka"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Ne ometaj"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Prečica za dugmad za Jačinu zvuka"</string>
@@ -541,8 +578,7 @@
<string name="select_keycode" msgid="7413765103381924584">"Odaberite dugme na tastaturi"</string>
<string name="preview" msgid="9077832302472282938">"Pregledaj"</string>
<string name="drag_to_add_tiles" msgid="7058945779098711293">"Povucite da biste dodali polja"</string>
- <!-- no translation found for drag_to_remove_tiles (3361212377437088062) -->
- <skip />
+ <string name="drag_to_remove_tiles" msgid="3361212377437088062">"Prevucite ovdje za uklanjanje"</string>
<string name="qs_edit" msgid="2232596095725105230">"Uredi"</string>
<string name="tuner_time" msgid="6572217313285536011">"Vrijeme"</string>
<string-array name="clock_options">
@@ -561,4 +597,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Pomjeri gore"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Pomjeri lijevo"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Pomjeri desno"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Aplikacija možda neće raditi s višestrukim prozorom"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"Pozicija <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Dvaput dodirnite za uređivanje."</string>
+ <string name="accessibility_qs_edit_add_tile_label" msgid="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g> Dvaput dodirnite za dodavanje."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Pozicija <xliff:g id="POSITION">%1$d</xliff:g>. Dvaput dodirnite za odabir."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Pomjeri <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Ukloni <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> je dodan na poziciju <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> je uklonjen"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> je premješten na poziciju <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Uređivanje brzih postavki"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bs-rBA/strings_tv.xml b/packages/SystemUI/res/values-bs-rBA/strings_tv.xml
index 5d20c8c..65c0982 100644
--- a/packages/SystemUI/res/values-bs-rBA/strings_tv.xml
+++ b/packages/SystemUI/res/values-bs-rBA/strings_tv.xml
@@ -29,6 +29,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"Za kontr. PIP držite "<b>"HOME"</b></string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Za kontrolu PIP, pritisnite i držite dugme POČETAK"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"Jasno mi je"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Odbaci"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 645c84f..b7fffb7 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Ignora <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"S\'ha omès <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"S\'han descartat totes les aplicacions recents."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"S\'està iniciant <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notificació omesa."</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"S\'ha establert la ubicació per GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Sol·licituds d\'ubicació actives"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Esborra totes les notificacions."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Configuració de les notificacions"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Configuració de l\'aplicació <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"La pantalla girarà automàticament."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"cerca"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"No s\'ha pogut iniciar <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"En mode segur, l\'aplicació <xliff:g id="APP">%s</xliff:g> està desactivada."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Historial"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Esborra"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Esborra-ho tot"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Aquesta aplicació no admet el mode multifinestra"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"L\'aplicació no admet el mode multifinestra"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Divisió horitzontal"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Les notificacions es mostren al capdamunt de la llista, apareixen a la pantalla i poden emetre sons"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Més opcions"</string>
<string name="notification_done" msgid="5279426047273930175">"Fet"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"Controls de notificació de l\'aplicació <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Color i aparença"</string>
<string name="night_mode" msgid="3540405868248625488">"Mode nocturn"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Calibra la pantalla"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"La funció Estalvi de bateria no està disponible durant la càrrega"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Estalvi de bateria"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Redueix el rendiment i les dades en segon pla"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Botó <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Inici"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Enrere"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Amunt"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Avall"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Esquerra"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Dreta"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Centre"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Pestanya"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Espai"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Retorn"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Retrocés"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Reprodueix/Pausa"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Atura"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Següent"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Anterior"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Rebobina"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Avança ràpidament"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Re Pàg"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Av Pàg"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Tecla de supressió"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Inici"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"Final"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Tecla d\'inserció"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Bloqueig de teclat numèric"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Teclat numèric <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Sistema"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Inici"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Recents"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Enrere"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Notificacions"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Tecles de drecera"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Canvia el mètode d\'introducció"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Aplicacions"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Assistència"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Navegador"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Contactes"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Correu electrònic"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"MI"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Music"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Calendari"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Mostra amb els controls de volum"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"No molesteu"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Drecera per als botons de volum"</string>
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Mou amunt"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Mou a l\'esquerra"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Mou a la dreta"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"És possible que l\'aplicació no funcioni amb el Mode multifinestra"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Fes doble toc per afegir-ho."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Posició <xliff:g id="POSITION">%1$d</xliff:g>. Fes doble toc per seleccionar-la."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Mou <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Suprimeix <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> s\'ha afegit a la posició <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> s\'ha suprimit"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> s\'ha mogut a la posició <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Editor de la configuració ràpida."</string>
</resources>
diff --git a/packages/SystemUI/res/values-ca/strings_tv.xml b/packages/SystemUI/res/values-ca/strings_tv.xml
index 35482d4..8876664 100644
--- a/packages/SystemUI/res/values-ca/strings_tv.xml
+++ b/packages/SystemUI/res/values-ca/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"Prem "<b>"INICI"</b>" per controlar PIP"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Mantén premut el botó INICI per controlar PIP"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"D\'acord"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Ignora"</string>
</resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 5ce6bd9..b707ece 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -170,6 +170,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Zavřít aplikaci <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Aplikace <xliff:g id="APP">%s</xliff:g> byla odebrána."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Všechny naposledy použité aplikace byly odstraněny."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Spouštění aplikace <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Oznámení je zavřeno."</string>
@@ -311,8 +313,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"vyhledat"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Aplikaci <xliff:g id="APP">%s</xliff:g> nelze spustit."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"Aplikace <xliff:g id="APP">%s</xliff:g> je v nouzovém režimu zakázána."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Historie"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Vymazat"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Vymazat vše"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Tato aplikace režim v několika oknech nepodporuje"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Aplikace režim v několika oknech nepodporuje"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Vodorovné rozdělení"</string>
@@ -501,10 +502,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Spořič baterie při nabíjení není k dispozici."</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Spořič baterie"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Omezuje výkon a data na pozadí"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Tlačítko <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Zpět"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Nahoru"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Dolů"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Vlevo"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Vpravo"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Střed"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tabulátor"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Mezerník"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Přehrát/Pozastavit"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Zastavit"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Další"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Předchozí"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Přetočit zpět"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Přetočit vpřed"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"<xliff:g id="NAME">%1$s</xliff:g> na numerické klávesnici"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Systém"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Plocha"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Poslední"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Zpět"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Oznámení"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Klávesové zkratky"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Přepnout metodu zadávání"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Aplikace"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Asistence"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Prohlížeč"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontakty"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-mail"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Chat"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Hudba"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalendář"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Zobrazit včetně ovládacích prvků hlasitosti"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Nerušit"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Zkratka tlačítek hlasitosti"</string>
@@ -559,4 +598,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Přesunout nahoru"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Přesunout vlevo"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Přesunout vpravo"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Aplikace nemusí v režimu několika oken fungovat."</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Dlaždici přidáte dvojitým klepnutím."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Pozice <xliff:g id="POSITION">%1$d</xliff:g>. Dvojitým klepnutím ji vyberete."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Přesunout dlaždici <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Odstranit dlaždici <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"Dlaždice <xliff:g id="TILE_NAME">%1$s</xliff:g> byla přidána na pozici <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"Dlaždice <xliff:g id="TILE_NAME">%1$s</xliff:g> byla odstraněna"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"Dlaždice <xliff:g id="TILE_NAME">%1$s</xliff:g> byla přesunuta na pozici <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Editor rychlého nastavení"</string>
</resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 0538bdb..7a25141 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Afvis <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> er annulleret."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Alle de seneste applikationer er lukket."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> startes."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Underretningen er annulleret."</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Placeringen er angivet ved hjælp af GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Aktive placeringsanmodninger"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Ryd alle underretninger."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"<xliff:g id="NUMBER">%s</xliff:g> mere"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Underretningsindstillinger"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Indstillinger for <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Skærmen roterer automatisk."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"søg"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> kunne ikke startes."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> er deaktiveret i sikker tilstand."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Historik"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Ryd"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Ryd alle"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Denne app understøtter ikke flere vinduer"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Appen understøtter ikke flere vinduer"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Opdel vandret"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Vis øverst på listen over underretninger, vis på skærmen, og tillad lyd"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Flere indstillinger"</string>
<string name="notification_done" msgid="5279426047273930175">"Færdig"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"Kontrolelementer til underretninger for <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Farve og udseende"</string>
<string name="night_mode" msgid="3540405868248625488">"Nattilstand"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Kalibrer skærmen"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Batterisparefunktionen er ikke tilgængelig under opladning"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Batterisparefunktion"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reducerer ydeevne og baggrundsdata"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"<xliff:g id="NAME">%1$s</xliff:g>-knap"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Tilbage"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Op"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Ned"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Venstre"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Højre"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Midtertast"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tabulatortast"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Mellemrumstast"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Tilbagetast"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Afspil/pause"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Stop"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Næste"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Forrige"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Spol tilbage"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Spol frem"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Numerisk tastatur <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"System"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Start"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Seneste"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Tilbage"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Underretninger"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Tastaturgenveje"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Skift indtastningsmetode"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Applikationer"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Assistance"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Browser"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontaktpersoner"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-mail"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Chat"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Musik"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalender"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Vis med lydstyrkeregulering"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Forstyr ikke"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Genvej til lydstyrkeknapper"</string>
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Flyt op"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Flyt til venstre"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Flyt til højre"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Appen fungerer muligvis ikke i Multivindue"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Tryk to gange for at tilføje."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Position <xliff:g id="POSITION">%1$d</xliff:g>. Tryk to gange for at vælge."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Flyt <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Fjern <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> føjes til position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> fjernes"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> blev flyttet til position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Redigeringsværktøj for Hurtige indstillinger."</string>
</resources>
diff --git a/packages/SystemUI/res/values-da/strings_tv.xml b/packages/SystemUI/res/values-da/strings_tv.xml
index 51b8582..45bba75 100644
--- a/packages/SystemUI/res/values-da/strings_tv.xml
+++ b/packages/SystemUI/res/values-da/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"Hold "<b>"HOME"</b>" nede for at styre PIP"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Tryk på HOME-knappen, og hold den nede for at styre PIP"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"OK"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Afvis"</string>
</resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 4cd0316..e10eb64 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> beenden"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> entfernt"</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Alle kürzlich verwendeten Apps wurden entfernt."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> wird gestartet."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Benachrichtigung geschlossen"</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Standort durch GPS festgelegt"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Standortanfragen aktiv"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Alle Benachrichtigungen löschen"</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Benachrichtigungseinstellungen"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Einstellungen von <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Bildschirm wird automatisch gedreht."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"Suche"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> konnte nicht gestartet werden."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> ist im abgesicherten Modus deaktiviert."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Verlauf"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Löschen"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Alle löschen"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Diese App unterstützt den Mehrfenstermodus nicht"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"App unterstützt Mehrfenstermodus nicht"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Geteilte Schaltfläche – horizontal"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Ganz oben in der Benachrichtigungsliste anzeigen, auf dem Display einblenden und Ton zulassen"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Weitere Einstellungen"</string>
<string name="notification_done" msgid="5279426047273930175">"Fertig"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g>-Benachrichtigungseinstellungen"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Farbe und Darstellung"</string>
<string name="night_mode" msgid="3540405868248625488">"Nachtmodus"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Bildschirm kalibrieren"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Der Energiesparmodus ist beim Aufladen nicht verfügbar."</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Energiesparmodus"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduzierung der Leistung und Hintergrunddaten"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Taste <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Pos1"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Zurück"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Nach oben"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Nach unten"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Nach links"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Nach rechts"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Zentrieren"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tabulatortaste"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Leertaste"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Eingabetaste"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Rücktaste"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Wiedergabe/Pause"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Stopp"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Weiter"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Zurück"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Zurückspulen"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Vorspulen"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Nach oben"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Nach unten"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Entf"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Pos1"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"Ende"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Einfg"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Ziffernblock <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"System"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Startseite"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Letzte"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Zurück"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Benachrichtigungen"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Tastenkombinationen"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Eingabemethode wechseln"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Apps"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Assistent"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Browser"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontakte"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-Mail"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Music"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalender"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Einschließlich Lautstärkeregler anzeigen"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Bitte nicht stören"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Tastenkombination für Lautstärketasten"</string>
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Nach oben verschieben"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Nach links verschieben"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Nach rechts verschieben"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"App funktioniert im Mehrfenstermodus möglicherweise nicht"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Zum Hinzufügen doppeltippen."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Position <xliff:g id="POSITION">%1$d</xliff:g>. Zum Auswählen doppeltippen."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"<xliff:g id="TILE_NAME">%1$s</xliff:g> verschieben"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"<xliff:g id="TILE_NAME">%1$s</xliff:g> entfernen"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ist auf Position <xliff:g id="POSITION">%2$d</xliff:g> hinzugefügt worden"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> wurde entfernt"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> an Position <xliff:g id="POSITION">%2$d</xliff:g> verschoben"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Editor für Schnelleinstellungen."</string>
</resources>
diff --git a/packages/SystemUI/res/values-de/strings_tv.xml b/packages/SystemUI/res/values-de/strings_tv.xml
index f448737..3d9c233 100644
--- a/packages/SystemUI/res/values-de/strings_tv.xml
+++ b/packages/SystemUI/res/values-de/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109"><b>"STARTBILDSCHIRMTASTE"</b>" drücken, um PIP zu steuern"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Halte die Taste für die Startseite gedrückt, um das Bild-in-Bild zu steuern"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"OK"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Beenden"</string>
</resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 59810c2..5ec3133 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Παράβλεψη <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Απορρίφθηκαν <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Έγινε παράβλεψη όλων των πρόσφατων εφαρμογών."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Έναρξη <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Η ειδοποίηση έχει απορριφθεί."</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Ρύθμιση τοποθεσίας με GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Τα αιτήματα τοποθεσίας έχουν ενεργοποιηθεί"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Εκκαθάριση όλων των ειδοποιήσεων."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Ρυθμίσεις ειδοποιήσεων"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Ρυθμίσεις <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Θα γίνεται αυτόματη περιστροφή της οθόνης."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"αναζήτηση"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Δεν ήταν δυνατή η εκκίνηση της εφαρμογής <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"Η εφαρμογή <xliff:g id="APP">%s</xliff:g> έχει απενεργοποιηθεί στην ασφαλή λειτουργία."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Ιστορικό"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Εκκαθάριση"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Διαγραφή όλων"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Η εφαρμογή αυτή δεν υποστηρίζει τη λειτουργία πολλαπλών παραθύρων"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Η εφαρμογή δεν υποστηρίζει τη λειτουργία πολλαπλών παραθύρων"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Οριζόντιος διαχωρισμός"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Να εμφανίζονται στην κορυφή της λίστας ειδοποιήσεων, να προβάλλονται στην οθόνη και να επιτρέπεται ο ήχος"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Περισσότερες ρυθμίσεις"</string>
<string name="notification_done" msgid="5279426047273930175">"Τέλος"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"Στοιχεία ελέγχου κοινοποίησης <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Χρώμα και εμφάνιση"</string>
<string name="night_mode" msgid="3540405868248625488">"Νυχτερινή λειτουργία"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Βαθμονόμηση οθόνης"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Η εξοικονόμηση μπαταρίας δεν είναι διαθέσιμη κατά τη διάρκεια της φόρτισης"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Εξοικονόμηση μπαταρίας"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Μειώνει την απόδοση και τα δεδομένα παρασκηνίου"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Κουμπί <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Πίσω"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Πάνω"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Κάτω"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Αριστερά"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Δεξιά"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Κέντρο"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Πλήκτρο διαστήματος"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Αναπαραγωγή/Παύση"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Διακοπή"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Επόμενο"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Προηγούμενο"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Επαναφορά"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Γρήγορη προώθηση"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Προηγούμενη σελίδα"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Επόμενη σελίδα"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"Λήξη"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Αριθμητικό πληκτρολόγιο <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Σύστημα"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Αρχική οθόνη"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Πρόσφατα"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Πίσω"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Ειδοποιήσεις"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Συντομεύσεις πληκτρολογίου"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Εναλλαγή μεθόδου εισαγωγής"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Εφαρμογές"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Υποβοήθηση"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Πρόγραμμα περιήγησης"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Επαφές"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Ηλεκτρονικό ταχυδρομείο"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Άμεσα μηνύματα (ΙΜ)"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Μουσική"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Ημερολόγιο"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Εμφάνιση με στοιχεία ελέγχου έντασης ήχου"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Μην ενοχλείτε"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Συντόμευση κουμπιών έντασης ήχου"</string>
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Μετακίνηση προς τα επάνω"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Μετακίνηση αριστερά"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Μετακίνηση δεξιά"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Η εφαρμογή ενδέχεται να μη λειτουργεί με τη λειτουργία πολλαπλών παραθύρων"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"Θέση <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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Πατήστε δύο φορές για προσθήκη."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Θέση <xliff:g id="POSITION">%1$d</xliff:g>. Πατήστε δύο φορές για επιλογή."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Μετακίνηση <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Κατάργηση <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"Το <xliff:g id="TILE_NAME">%1$s</xliff:g> προστέθηκε στη θέση <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"Το <xliff:g id="TILE_NAME">%1$s</xliff:g> καταργείται"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"Το <xliff:g id="TILE_NAME">%1$s</xliff:g> μετακινήθηκε στη θέση <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Επεξεργασία γρήγορων ρυθμίσεων."</string>
</resources>
diff --git a/packages/SystemUI/res/values-el/strings_tv.xml b/packages/SystemUI/res/values-el/strings_tv.xml
index 355c9b0..c54c7be 100644
--- a/packages/SystemUI/res/values-el/strings_tv.xml
+++ b/packages/SystemUI/res/values-el/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"Κρατήστε το πλήκτρο "<b>"HOME"</b>" πατημένο για έλεγχο του PIP"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Πιέστε παρατεταμένα το κουμπί HOME, για να ελέγξετε τη λειτουργία PIP"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"Κατάλαβα"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Παράβλεψη"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 9346188..f08894f 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Dismiss <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> dismissed."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"All recent applications dismissed."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Starting <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notification dismissed."</string>
@@ -309,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"search"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Could not start <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> is disabled in safe-mode."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"History"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Clear"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Clear all"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"This app does not support multi-window"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"App does not support multi-window"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Split Horizontal"</string>
@@ -499,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Battery Saver not available during charging"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Battery Saver"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduces performance and background data"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Button <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Back"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Up"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Down"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Left"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Right"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Centre"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Space"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Play/Pause"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Stop"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Next"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Previous"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Rewind"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Fast-Forward"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Numpad <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"System"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Home"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Recent"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Back"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Notifications"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Keyboard Shortcuts"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Switch input method"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Applications"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Assist"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Browser"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Contacts"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Email"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Music"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Calendar"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Show with volume controls"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Do not disturb"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Volume buttons shortcut"</string>
@@ -557,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Move up"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Move to the left"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Move to the right"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"App may not work with multi-window"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Double tap to add."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Position <xliff:g id="POSITION">%1$d</xliff:g>. Double tap to select."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Remove <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> is added to position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> is removed"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> moved to position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Quick settings editor."</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 9346188..f08894f 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Dismiss <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> dismissed."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"All recent applications dismissed."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Starting <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notification dismissed."</string>
@@ -309,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"search"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Could not start <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> is disabled in safe-mode."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"History"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Clear"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Clear all"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"This app does not support multi-window"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"App does not support multi-window"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Split Horizontal"</string>
@@ -499,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Battery Saver not available during charging"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Battery Saver"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduces performance and background data"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Button <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Back"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Up"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Down"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Left"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Right"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Centre"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Space"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Play/Pause"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Stop"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Next"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Previous"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Rewind"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Fast-Forward"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Numpad <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"System"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Home"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Recent"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Back"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Notifications"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Keyboard Shortcuts"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Switch input method"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Applications"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Assist"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Browser"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Contacts"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Email"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Music"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Calendar"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Show with volume controls"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Do not disturb"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Volume buttons shortcut"</string>
@@ -557,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Move up"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Move to the left"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Move to the right"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"App may not work with multi-window"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Double tap to add."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Position <xliff:g id="POSITION">%1$d</xliff:g>. Double tap to select."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Remove <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> is added to position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> is removed"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> moved to position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Quick settings editor."</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 9346188..f08894f 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Dismiss <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> dismissed."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"All recent applications dismissed."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Starting <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notification dismissed."</string>
@@ -309,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"search"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Could not start <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> is disabled in safe-mode."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"History"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Clear"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Clear all"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"This app does not support multi-window"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"App does not support multi-window"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Split Horizontal"</string>
@@ -499,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Battery Saver not available during charging"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Battery Saver"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduces performance and background data"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Button <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Back"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Up"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Down"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Left"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Right"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Centre"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Space"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Play/Pause"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Stop"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Next"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Previous"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Rewind"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Fast-Forward"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Numpad <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"System"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Home"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Recent"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Back"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Notifications"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Keyboard Shortcuts"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Switch input method"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Applications"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Assist"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Browser"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Contacts"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Email"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Music"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Calendar"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Show with volume controls"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Do not disturb"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Volume buttons shortcut"</string>
@@ -557,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Move up"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Move to the left"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Move to the right"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"App may not work with multi-window"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Double tap to add."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Position <xliff:g id="POSITION">%1$d</xliff:g>. Double tap to select."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Remove <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> is added to position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> is removed"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> moved to position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Quick settings editor."</string>
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 45b4020..b5a10e1 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Rechazar <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> descartada."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Se descartaron todas las aplicaciones recientes."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Iniciando <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notificación ignorada"</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"La ubicación se estableció por GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Solicitudes de ubicación activas"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Eliminar todas las notificaciones"</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"<xliff:g id="NUMBER">%s</xliff:g> más"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Configuración de notificaciones"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Configuración de <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"La pantalla girará automáticamente."</string>
@@ -304,14 +305,13 @@
<string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Advertencia de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Modo de trabajo"</string>
<string name="recents_empty_message" msgid="808480104164008572">"No hay elementos recientes"</string>
- <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Borraste todo"</string>
+ <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"Todo borrado"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Información de la aplicación"</string>
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"Fijar pantalla"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"buscar"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"No se pudo iniciar <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> está inhabilitada en modo seguro."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Historial"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Borrar"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Borrar todo"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Esta app no es compatible con el modo multiventana"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"La app no es compatible con el modo multiventana"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"División horizontal"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Mostrar en la parte superior de la lista de notificaciones, ver en la pantalla y permitir sonidos"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Más opciones de configuración"</string>
<string name="notification_done" msgid="5279426047273930175">"Listo"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"Controles de notificaciones de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Color y apariencia"</string>
<string name="night_mode" msgid="3540405868248625488">"Modo nocturno"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Calibrar pantalla"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Ahorro de batería no está disponible durante la carga"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Ahorro de batería"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduce el rendimiento y el uso de datos en segundo plano"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Botón <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Página principal"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Atrás"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Arriba"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Abajo"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Izquierda"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Derecha"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Centro"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tabulación"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Espacio"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Intro"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Retroceso"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Reproducir/pausar"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Detener"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Siguiente"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Anterior"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Retroceder"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Avanzar rápido"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Re Pág"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Av Pág"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Borrar"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Página principal"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"Fin"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insertar"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Bloqueo numérico"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Teclado numérico <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Sistema"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Pantalla principal"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Recientes"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Atrás"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Notificaciones"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Combinación de teclas"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Cambiar método de entrada"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Aplicaciones"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Asistencia"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Navegador"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Contactos"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Correo electrónico"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"MI"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Música"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Calendario"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Mostrar con controles de volumen"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"No interrumpir"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Combinación de teclas de botones de volumen"</string>
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Mover hacia arriba"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Mover a la izquierda"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Mover a la derecha"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Es posible que la app no se ejecute con la función Multiventana"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Presiona dos veces para agregarlo."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Posición <xliff:g id="POSITION">%1$d</xliff:g>. Presiona dos veces para seleccionarla."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Mover <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Quitar <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"Se agregó <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_removed" msgid="8584304916627913440">"Se quitó <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"Se movió <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_desc_quick_settings_edit" msgid="8073587401747016103">"Editor de Configuración rápida"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings_tv.xml b/packages/SystemUI/res/values-es-rUS/strings_tv.xml
index 98ad35b..72ea127 100644
--- a/packages/SystemUI/res/values-es-rUS/strings_tv.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"Mantén presionado "<b>"INICIO"</b>" para controlar PIP"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Mantén presionado el botón INICIO para controlar PIP"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"Entendido"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Descartar"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 5c84999..d8affc9 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Ignorar <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Se ha eliminado <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Se han ignorado todas las aplicaciones recientes."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Iniciando <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notificación ignorada"</string>
@@ -236,7 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Ubicación definida por GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Solicitudes de ubicación activas"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Borrar todas las notificaciones"</string>
- <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ de <xliff:g id="NUMBER">%s</xliff:g>"</string>
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"<xliff:g id="NUMBER">%s</xliff:g> más"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Ajustes de notificaciones"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Ajustes de <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"La pantalla girará automáticamente."</string>
@@ -309,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"buscar"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"No se ha podido iniciar <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"La aplicación <xliff:g id="APP">%s</xliff:g> se ha inhabilitado en modo seguro."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Historial"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Borrar"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Borrar todo"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Esta aplicación no admite el modo multiventana."</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"La aplicación no admite el modo multiventana"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"División horizontal"</string>
@@ -499,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Ahorro de batería no disponible mientras se carga el dispositivo"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Ahorro de batería"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduce el rendimiento y las conexiones automáticas"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Botón <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Inicio"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Atrás"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Arriba"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Abajo"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Izquierda"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Derecha"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Centro"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tabulador"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Espacio"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Intro"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Tecla de retroceso"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Reproducir/Pausa"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Detener"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Siguiente"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Anterior"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Rebobinar"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Avance rápido"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Re Pág"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Av Pág"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Supr"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Inicio"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"Fin"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Bloq Num"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Teclado numérico <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Sistema"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Inicio"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Recientes"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Atrás"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Notificaciones"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Combinaciones de teclas"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Cambiar método de introducción"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Aplicaciones"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Asistencia"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Navegador"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Contactos"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Correo electrónico"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"MI"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Música"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Calendario"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Mostrar con controles de volumen"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"No molestar"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Combinación de teclas para los botones de volumen"</string>
@@ -557,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Subir"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Mover a la izquierda"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Mover a la derecha"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Es posible que la aplicación no funcione con el modo multiventana"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Toca dos veces para añadirlo."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Posición <xliff:g id="POSITION">%1$d</xliff:g>. Toca dos veces para seleccionarla."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Mover <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Quitar <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> se ha añadido a la posición <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> se ha quitado"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> se ha movido a la posición <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Editor de ajustes rápidos."</string>
</resources>
diff --git a/packages/SystemUI/res/values-et-rEE/strings.xml b/packages/SystemUI/res/values-et-rEE/strings.xml
index 423166f..f18bf05 100644
--- a/packages/SystemUI/res/values-et-rEE/strings.xml
+++ b/packages/SystemUI/res/values-et-rEE/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Rakendusest <xliff:g id="APP">%s</xliff:g> loobumine."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Loobusite rakendusest <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Kõikidest hiljutistest rakendustest on loobutud"</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Rakenduse <xliff:g id="APP">%s</xliff:g> käivitamine."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g>, <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Märguandest on loobutud."</string>
@@ -309,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"otsing"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Rakendust <xliff:g id="APP">%s</xliff:g> ei saanud käivitada."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"Rakendus <xliff:g id="APP">%s</xliff:g> on turvarežiimis keelatud."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Ajalugu"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Kustuta"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Kustuta kõik"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"See rakendus ei toeta mitut akent"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Rakendus ei toeta mitut akent"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Horisontaalne poolitamine"</string>
@@ -499,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Akusäästja pole laadimise ajal saadaval"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Akusäästja"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Vähendab jõudlust ja taustaandmeid"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Nupp <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Avaekraan"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Tagasi"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Üles"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Alla"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Vasakule"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Paremale"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Keskele"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tabulaator"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Tühik"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Sisestusklahv"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Tagasilüke"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Esita/peata"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Peata"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Järgmine"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Eelmine"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Keri tagasi"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Keri edasi"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Lehe võrra üles"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Lehe võrra alla"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Kustuta"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Avaekraan"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"Lõpp"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Sisesta"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Numbrilukk"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Numbriklahvistik <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Süsteem"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Avaekraan"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Hiljutised"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Tagasi"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Märguanded"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Klaviatuuri otseteed"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Sisestusmeetodi vahetamine"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Rakendused"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Abi"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Brauser"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontaktid"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-post"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM (kiirsuhtlus)"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Muusika"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalender"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Kuva koos helitugevuse juhtnuppudega"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Mitte segada"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Helitugevuse nuppude otsetee"</string>
@@ -557,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Liigu üles"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Liigu vasakule"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Liigu paremale"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Rakendus ei pruugi mitme akna režiimis töötada"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Topeltpuudutage lisamiseks."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Asend <xliff:g id="POSITION">%1$d</xliff:g>. Topeltpuudutage valimiseks."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Paani <xliff:g id="TILE_NAME">%1$s</xliff:g> teisaldamine"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Paani <xliff:g id="TILE_NAME">%1$s</xliff:g> eemaldamine"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"Paan <xliff:g id="TILE_NAME">%1$s</xliff:g> lisati asendisse <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"Paan <xliff:g id="TILE_NAME">%1$s</xliff:g> eemaldati"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"Paan <xliff:g id="TILE_NAME">%1$s</xliff:g> teisaldati asendisse <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Kiirseadete redigeerija."</string>
</resources>
diff --git a/packages/SystemUI/res/values-eu-rES/strings.xml b/packages/SystemUI/res/values-eu-rES/strings.xml
index 435e821..575f8749 100644
--- a/packages/SystemUI/res/values-eu-rES/strings.xml
+++ b/packages/SystemUI/res/values-eu-rES/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Baztertu <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> baztertu da."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Azken aplikazio guztiak baztertu da."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> hasten."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Jakinarazpena baztertu da."</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Kokapena GPS bidez ezarri da"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Aplikazioen kokapen-eskaerak aktibo daude"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Garbitu jakinarazpen guztiak."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Jakinarazpen-ezarpenak"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> ezarpenak"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Pantaila automatikoki biratuko da."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"bilatu"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Ezin izan da hasi <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> desgaituta dago modu seguruan."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Historia"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Garbitu"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Garbitu guztiak"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Aplikazioak ez du onartzen leiho bat baino gehiago erabiltzea"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Aplikazioak ez du onartzen leiho bat baino gehiago erabiltzea"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Zatitze horizontala"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Erakutsi jakinarazpen hauek zerrendaren goialdean, agerrarazi pantailan eta egin soinua"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Ezarpen gehiago"</string>
<string name="notification_done" msgid="5279426047273930175">"Eginda"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioaren jakinarazpenak kontrolatzeko aukerak"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Kolorea eta itxura"</string>
<string name="night_mode" msgid="3540405868248625488">"Gau modua"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Kalibratu pantaila"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Bateria-aurrezlea ez dago erabilgarri gailua kargatzen ari denean"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Bateria-aurrezlea"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Errendimendua eta atzeko planoko datuen erabilera murrizten ditu"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"<xliff:g id="NAME">%1$s</xliff:g> botoia"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Hasiera"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Atzera"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Gora"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Behera"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Ezkerrera"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Eskuinera"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Erdiratu"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tabuladorea"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Zuriunea"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Sartu"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Atzera"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Erreproduzitu/Pausatu"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Gelditu"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Hurrengoa"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Aurrekoa"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Atzeratu"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Aurreratu"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Orria gora"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Orria behera"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Ezabatu"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Hasiera"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"Amaitu"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Txertatu"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Blok Zenb"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Zenbaki-teklatuko <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Sistema"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Hasierako pantaila"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Azkenak"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Atzera"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Jakinarazpenak"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Lasterbideak"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Aldatu idazketa-metodoa"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Aplikazioak"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Laguntzailea"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Arakatzailea"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontaktuak"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Helbide elektronikoa"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Istanteko mezularitza"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Musika"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Egutegia"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Erakutsi bolumena kontrolatzeko aukerekin"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Ez molestatu"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Bolumen-botoietarako lasterbidea"</string>
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Eraman gora"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Eraman ezkerrera"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Eraman eskuinera"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Baliteke aplikazioak ez funtzionatzea leiho anitzetan."</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"<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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Gehitzeko, sakatu birritan."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"<xliff:g id="POSITION">%1$d</xliff:g>. posizioa. Hautatzeko, sakatu birritan."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Mugitu <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Kendu <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> gehitu da <xliff:g id="POSITION">%2$d</xliff:g>. posizioan"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"Kendu da <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> <xliff:g id="POSITION">%2$d</xliff:g>. posiziora eraman da"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Ezarpen bizkorren editorea."</string>
</resources>
diff --git a/packages/SystemUI/res/values-eu-rES/strings_tv.xml b/packages/SystemUI/res/values-eu-rES/strings_tv.xml
index f28ec5e..b812143 100644
--- a/packages/SystemUI/res/values-eu-rES/strings_tv.xml
+++ b/packages/SystemUI/res/values-eu-rES/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109"><b>"HASIERA"</b>" PIP kontrolatzeko"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Eduki sakatuta hasierako botoia pantaila txikia kontrolatzeko"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"Ados"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Baztertu"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index f2e977c..322197e 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"رد کردن <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> نادیده گرفته شد."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"همه برنامههای اخیر رد شدند."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> در حال شروع به کار است."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"اعلان ردشد."</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"مکان تنظیم شده توسط GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"درخواستهای موقعیت مکانی فعال است"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"پاک کردن تمام اعلانها"</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"تنظیمات اعلان"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"تنظیمات <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"صفحه به صورت خودکار میچرخد."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"جستجو"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> شروع نشد."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> در حالت ایمن غیرفعال است."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"سابقه"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"پاک کردن"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"پاک کردن همه"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"این برنامه از چندپنجره پشتیبانی نمیکند"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"برنامه از چندپنجره پشتیبانی نمیکند"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"تقسیم افقی"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"در بالای فهرست اعلان نشان داده شوند، در صفحه نشان داده شوند و صدادار باشند"</string>
<string name="notification_more_settings" msgid="816306283396553571">"تنظیمات بیشتر"</string>
<string name="notification_done" msgid="5279426047273930175">"تمام"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"کنترلهای اعلان <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"رنگ و ظاهر"</string>
<string name="night_mode" msgid="3540405868248625488">"حالت شب"</string>
<string name="calibrate_display" msgid="5974642573432039217">"درجهبندی نمایشگر"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"هنگام شارژ شدن، «بهینهسازی باتری» در دسترس نیست"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"بهینهسازی باتری"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"عملکرد و اطلاعات پسزمینه را کاهش میدهد"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"دکمه <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"ابتدا"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"برگشت"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"بالا"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"پایین"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"چپ"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"راست"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"مرکز"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"جهش"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"فاصله"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"ورود"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"پسبر"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"پخش/مکث"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"توقف"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"بعدی"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"قبلی"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"عقب بردن"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"جلو بردن سریع"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"صفحه بعدی"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"صفحه قبلی"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"حذف"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"ابتدا"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"انتها"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"درج"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"قفل اعداد"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"صفحهکلید عددی <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"سیستم"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"صفحه اصلی"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"موارد اخیر"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"برگشت"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"اعلانها"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"میانبرهای صفحهکلید"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"تغییر روش ورودی"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"برنامهها"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"دستیار"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"مرورگر"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"مخاطبین"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"رایانامه"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"پیام فوری"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"موسیقی"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"تقویم"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"نمایش با کنترلهای صدا"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"مزاحم نشوید"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"میانبر دکمههای صدا"</string>
@@ -540,8 +577,7 @@
<string name="select_keycode" msgid="7413765103381924584">"کلید صفحهکلید را انتخاب کنید"</string>
<string name="preview" msgid="9077832302472282938">"پیشنمایش"</string>
<string name="drag_to_add_tiles" msgid="7058945779098711293">"کشیدن برای افزودن کاشیها"</string>
- <!-- no translation found for drag_to_remove_tiles (3361212377437088062) -->
- <skip />
+ <string name="drag_to_remove_tiles" msgid="3361212377437088062">"برای حذف، به اینجا بکشید"</string>
<string name="qs_edit" msgid="2232596095725105230">"ویرایش"</string>
<string name="tuner_time" msgid="6572217313285536011">"زمان"</string>
<string-array name="clock_options">
@@ -560,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"انتقال به بالا"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"انتقال به چپ"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"انتقال به راست"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"برنامه ممکن است با چندپنجره کار نکند"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"موقعیت <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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. برای افزودن دو ضربه سریع بزنید."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"موقعیت <xliff:g id="POSITION">%1$d</xliff:g>. برای انتخاب دو ضربه سریع بزنید."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"انتقال <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"حذف <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> به موقعیت <xliff:g id="POSITION">%2$d</xliff:g> اضافه میشود"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> حذف میشود"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> به موقعیت <xliff:g id="POSITION">%2$d</xliff:g> منتقل شد"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"ویرایشگر تنظیمات سریع."</string>
</resources>
diff --git a/packages/SystemUI/res/values-fa/strings_tv.xml b/packages/SystemUI/res/values-fa/strings_tv.xml
index 5177724..0d028d8 100644
--- a/packages/SystemUI/res/values-fa/strings_tv.xml
+++ b/packages/SystemUI/res/values-fa/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"کنترل PIP با نگهداشتن "<b>"HOME"</b></string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"برای کنترل PIP دکمه صفحه اصلی را فشار داده و نگهدارید"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"متوجه شدم"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"رد کردن"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 11cb91e..6db0a3e 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Hylätään <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> hylättiin."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Kaikki viimeisimmät sovellukset on hylätty."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Käynnistetään <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Ilmoitus hylätty."</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Sijainti määritetty GPS:n avulla"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Sijaintipyynnöt aktiiviset"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Tyhjennä kaikki ilmoitukset."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Ilmoitusasetukset"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Asetukset – <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Ruutu kääntyy automaattisesti."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"haku"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Sovelluksen <xliff:g id="APP">%s</xliff:g> käynnistäminen epäonnistui."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> on poistettu käytöstä vikasietotilassa."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Historia"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Tyhjennä"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Tyhjennä kaikki"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Tämä sovellus ei tue usean ikkunan tilaa."</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Sovellus ei tue usean ikkunan tilaa."</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Vaakasuuntainen jako"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Näytä ilmoitukset näytöllä ja ilmoitusluettelon yläosassa ja salli niiden äänet."</string>
<string name="notification_more_settings" msgid="816306283396553571">"Lisäasetukset"</string>
<string name="notification_done" msgid="5279426047273930175">"Valmis"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ilmoitusten hallinta"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Väri ja ulkoasu"</string>
<string name="night_mode" msgid="3540405868248625488">"Yötila"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Kalibroi näyttö"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Virransäästö ei ole käytettävissä latauksen aikana."</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Virransäästö"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Rajoittaa suorituskykyä ja taustatiedonsiirtoa."</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Painike <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Takaisin"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Ylös"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Alas"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Vasemmalle"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Oikealle"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Keskelle"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Sarkain"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Välilyönti"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Askelpalautin"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Toisto/keskeytys"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Pysäytä"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Seuraava"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Edellinen"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Kelaa taaksepäin"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Kelaa eteenpäin"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Numeronäppäimistö <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Järjestelmä"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Aloitusnäyttö"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Viimeaikaiset"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Takaisin"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Ilmoitukset"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Pikanäppäimet"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Vaihda syöttötapaa"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Sovellukset"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Apusovellus"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Selain"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Yhteystiedot"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Sähköposti"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Pikaviesti"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Musiikki"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalenteri"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Näytä äänenvoimakkuuden säätimien yhteydessä"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Älä häiritse"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Äänenvoimakkuuspainikkeiden pikanäppäin"</string>
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Siirrä ylöspäin"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Siirrä vasemmalle"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Siirrä oikealle"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Sovellus ei ehkä toimi usean ikkunan tilassa."</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Lisää kaksoisnapauttamalla."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Paikka <xliff:g id="POSITION">%1$d</xliff:g>. Valitse kaksoisnapauttamalla."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Siirrä <xliff:g id="TILE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Poista <xliff:g id="TILE_NAME">%1$s</xliff:g>."</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> lisättiin paikkaan <xliff:g id="POSITION">%2$d</xliff:g>."</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> poistettiin."</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> siirrettiin paikkaan <xliff:g id="POSITION">%2$d</xliff:g>."</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Pika-asetusten muokkausnäkymä"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fi/strings_tv.xml b/packages/SystemUI/res/values-fi/strings_tv.xml
index 642ed20..9124f67 100644
--- a/packages/SystemUI/res/values-fi/strings_tv.xml
+++ b/packages/SystemUI/res/values-fi/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"PIP: paina pitkään "<b>"aloituspain"</b>"."</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Hallinnoi PIP-tilaa painamalla ALOITUSNÄYTTÖ-painiketta pitkään."</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"Selvä"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Hylkää"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index c647cc9..89c6826 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Supprimer <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Application \"<xliff:g id="APP">%s</xliff:g>\" ignorée."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Toutes les applications récentes ont été supprimées."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Lancement de <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notification masquée"</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Position définie par GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Demandes de localisation actives"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Supprimer toutes les notifications"</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Paramètres de notification"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Paramètres de <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"L\'écran pivote automatiquement."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"rechercher"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Impossible de lancer <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> est désactivée en mode sécurisé."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Historique"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Effacer"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Effacer tout"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Cette application ne prend pas en charge le mode multifenêtre"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"L\'application ne prend pas en charge le mode multifenêtre"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Séparation horizontale"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Afficher en haut de la liste des notifications, afficher sur l\'écran et émettre un son"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Plus de paramètres"</string>
<string name="notification_done" msgid="5279426047273930175">"Terminé"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"Commandes de notification pour <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Couleur et apparence"</string>
<string name="night_mode" msgid="3540405868248625488">"Mode Nuit"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Calibrer l\'affichage"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Le mode Économie d\'énergie n\'est pas accessible pendant la charge"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Économie d\'énergie"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Réduit les performances et les données en arrière-plan"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Bouton <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Accueil"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Précédent"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Haut"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Bas"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Gauche"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Droite"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Centrer"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tabulation"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Espace"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Entrée"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Retour arrière"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Lecture/Pause"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Arrêter"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Suivant"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Précédent"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Reculer"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Avance rapide"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page précédente"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page suivante"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Supprimer"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Accueil"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"Fin"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insérer"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Verr num"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Pavé numérique <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Système"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Accueil"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Récents"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Précédent"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Notifications"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Raccourcis clavier"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Changer de méthode d\'entrée"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Applications"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Assistance"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Navigateur"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Contacts"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Courriel"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"MI"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Musique"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Agenda"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Afficher avec les commandes de volume"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Ne pas déranger"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Raccourci des boutons de volume"</string>
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Déplacer vers le haut"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Déplacer vers la gauche"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Déplacer vers la droite"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Il est possible que l\'application ne fonctionne pas en mode Multi-fenêtre."</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Touchez deux fois pour ajouter."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Position : <xliff:g id="POSITION">%1$d</xliff:g>. Touchez deux fois pour sélectionner."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Déplacer <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Supprimer <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> a été ajouté à la position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> a été supprimé"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> a été déplacé à la position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Éditeur de paramètres rapides."</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings_tv.xml b/packages/SystemUI/res/values-fr-rCA/strings_tv.xml
index 1ae3c5c..597a588 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings_tv.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"Maint. enf. "<b>"ACC."</b>" pr gér. mode IDI"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Maintenez enfoncé le bouton ACCUEIL pour gérer le mode IDI."</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"OK"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Fermer"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 7d91f6f..2f99b94 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Supprimer <xliff:g id="APP">%s</xliff:g>"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Application \"<xliff:g id="APP">%s</xliff:g>\" ignorée."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Toutes les applications récentes ont été supprimées."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Lancement de <xliff:g id="APP">%s</xliff:g>"</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> : <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notification masquée"</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Position définie par GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Demandes de localisation actives"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Supprimer toutes les notifications"</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"<xliff:g id="NUMBER">%s</xliff:g> autres"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Paramètres de notification"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Paramètres de <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"L\'écran pivote automatiquement."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"rechercher"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Impossible de lancer <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"L\'application <xliff:g id="APP">%s</xliff:g> est désactivée en mode sécurisé."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Historique"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Effacer"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Tout effacer"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Application incompatible avec le mode multifenêtre."</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Application incompatible avec le mode multifenêtre"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Séparation horizontale"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Afficher en haut de la liste des notifications, afficher sur l\'écran et émettre un son"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Plus de paramètres"</string>
<string name="notification_done" msgid="5279426047273930175">"Terminé"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"Commandes de notification de l\'application <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Couleur et apparence"</string>
<string name="night_mode" msgid="3540405868248625488">"Mode Nuit"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Calibrer l\'affichage"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"L\'économiseur de batterie n\'est pas disponible lorsque l\'appareil est en charge."</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Économiseur de batterie"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Limite les performances et les données en arrière-plan."</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Bouton <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Accueil"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Précédent"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Vers le haut"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Vers le bas"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Vers la gauche"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Vers la droite"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Centre"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tabulation"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Espace"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Entrée"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Retour arrière"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Lire ou suspendre la lecture"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Arrêter"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Suivant"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Précédent"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Retour arrière"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Avance rapide"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page précédente"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page suivante"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Supprimer"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Accueil"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"Fin"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insérer"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Verr Num"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Pavé numérique <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Système"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Accueil"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Récents"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Précédent"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Notifications"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Raccourcis clavier"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Changer le mode de saisie"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Applications"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Assistance"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Navigateur"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Contacts"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Messagerie"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Messagerie instantanée"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Musique"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Agenda"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Afficher avec les commandes de volume"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Ne pas déranger"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Raccourci des boutons de volume"</string>
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Déplacer vers le haut"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Déplacer vers la gauche"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Déplacer vers la droite"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Il est possible que l\'application ne fonctionne pas en mode multifenêtre."</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Appuyer deux fois pour ajouter."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Position <xliff:g id="POSITION">%1$d</xliff:g>. Appuyer deux fois pour sélectionner."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Déplacer \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\""</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Supprimer \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\""</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"Le bloc \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\" a bien été ajouté à la position <xliff:g id="POSITION">%2$d</xliff:g>."</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"Le bloc \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\" a bien été supprimé."</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"Le bloc \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\" a bien été déplacé à la position <xliff:g id="POSITION">%2$d</xliff:g>."</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Éditeur de configuration rapide."</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr/strings_tv.xml b/packages/SystemUI/res/values-fr/strings_tv.xml
index 68c4dcb..0478eea 100644
--- a/packages/SystemUI/res/values-fr/strings_tv.xml
+++ b/packages/SystemUI/res/values-fr/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"Appui long "<b>"ACCUEIL"</b>" pour contrôler PIP"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Appuyez de manière prolongée sur le bouton ACCUEIL pour contrôler le mode PIP."</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"OK"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Ignorer"</string>
</resources>
diff --git a/packages/SystemUI/res/values-gl-rES/strings.xml b/packages/SystemUI/res/values-gl-rES/strings.xml
index 1474acf..45c3626 100644
--- a/packages/SystemUI/res/values-gl-rES/strings.xml
+++ b/packages/SystemUI/res/values-gl-rES/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Rexeitar <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Rexeitouse <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Rexeitáronse todas as aplicacións recentes."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Iniciando <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notificación rexeitada"</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Localización establecida polo GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Solicitudes de localización activas"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Eliminar todas as notificacións."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Configuración das notificacións"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Configuración de <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"A pantalla xirará automaticamente."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"buscar"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Non foi posible iniciar <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"A aplicación <xliff:g id="APP">%s</xliff:g> está desactivada no modo seguro"</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Historial"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Borrar"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Borrar todo"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Esta aplicación non é compatible con varias ventás"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"A aplicación non é compatible con varias ventás"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Dividir en horizontal"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Mostrar na parte superior da lista de notificacións, amosar na pantalla e permitir que emita son"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Máis opcións"</string>
<string name="notification_done" msgid="5279426047273930175">"Feito"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"Controis de notificacións de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Cor e aspecto"</string>
<string name="night_mode" msgid="3540405868248625488">"Modo nocturno"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Calibrar pantalla"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"A función aforro de batería non está dispoñible durante a carga"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Aforro de batería"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduce o rendemento e os datos en segundo plano"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Botón <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Inicio"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Volver"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Arriba"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Abaixo"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Esquerda"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Dereita"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Centro"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tabulador"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Espazo"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Intro"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Retroceso"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Reproducir/Pausar"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Deter"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Seguinte"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Anterior"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Rebobinar"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Avance rápido"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Re Páx"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Av Páx"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Supr"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Inicio"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"Fin"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Inserir"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Bloqueo numérico"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Teclado numérico <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Sistema"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Inicio"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Recentes"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Volver"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Notificacións"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Atallos de teclado"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Cambiar de método de entrada"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Aplicacións"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Asistente"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Navegador"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Contactos"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Correo electrónico"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"MI"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Música"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Calendario"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Mostrar cos controis de volume"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Non molestar"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Atallo dos botóns de volume"</string>
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Subir"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Mover á esquerda"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Mover á dereita"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Pode que a aplicación non funcione con varias ventás."</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Toca dúas veces o elemento para engadilo"</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Posición <xliff:g id="POSITION">%1$d</xliff:g>. Toca dúas veces o elemento para seleccionalo."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Elimina <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> engadiuse á posición <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"Eliminouse <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> moveuse á posición <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Editor de configuración rápida."</string>
</resources>
diff --git a/packages/SystemUI/res/values-gl-rES/strings_tv.xml b/packages/SystemUI/res/values-gl-rES/strings_tv.xml
index 11d2d4c..d43d8cc 100644
--- a/packages/SystemUI/res/values-gl-rES/strings_tv.xml
+++ b/packages/SystemUI/res/values-gl-rES/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"Manter premido "<b>"INICIO"</b>" para controlar PIP"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Mantén premido o botón de INICIO para controlar PIP"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"De acordo"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Ignorar"</string>
</resources>
diff --git a/packages/SystemUI/res/values-gu-rIN/strings.xml b/packages/SystemUI/res/values-gu-rIN/strings.xml
index 7b64214..ef6a49c 100644
--- a/packages/SystemUI/res/values-gu-rIN/strings.xml
+++ b/packages/SystemUI/res/values-gu-rIN/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> કાઢી નાખો."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> કાઢી નાખી."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"તમામ તાજેતરની એપ્લિકેશનો કાઢી નાખી."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> પ્રારંભ કરી રહ્યું છે."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"સૂચના કાઢી નાખી."</string>
@@ -309,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"શોધ"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> પ્રારંભ કરી શકાયું નથી."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"સુરક્ષિત મોડમાં <xliff:g id="APP">%s</xliff:g> અક્ષમ કરેલ છે."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"ઇતિહાસ"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"સાફ કરો"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"બધું સાફ કરો"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"આ ઍપ્લિકેશન મલ્ટિ-વિંડોનું સમર્થન કરતી નથી"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"ઍપ્લિકેશન મલ્ટિ-વિંડોનું સમર્થન કરતી નથી"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"આડું વિભક્ત કરો"</string>
@@ -499,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"ચાર્જિંગ દરમિયાન બૅટરી બચતકર્તા ઉપલબ્ધ નથી"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"બૅટરી બચતકર્તા"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"પ્રદર્શન અને પૃષ્ઠભૂમિ ડેટા ઘટાડે છે"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"બટન <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Back"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Up"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Down"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Left"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Right"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Center"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Space"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Play/Pause"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Stop"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Next"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Previous"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Rewind"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Fast Forward"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Numpad <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"સિસ્ટમ"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"હોમ"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"તાજેતરના"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"પાછળ"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"સૂચનાઓ"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"કીબોર્ડ શૉર્ટકટ્સ"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"ઇનપુટ પદ્ધતિ સ્વિચ કરો"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"ઍપ્લિકેશનો"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"સહાય"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"બ્રાઉઝર"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"સંપર્કો"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"ઇમેઇલ"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"સંગીત"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"કૅલેન્ડર"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"વૉલ્યૂમ નિયંત્રણ સાથે બતાવો"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"ખલેલ પાડશો નહીં"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"વૉલ્યૂમ બટન્સ શૉર્ટકટ"</string>
@@ -557,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"ઉપર ખસેડો"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"ડાબે ખસેડો"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"જમણે ખસેડો"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"બહુ-વિંડો સાથે અૅપ્લિકેશન કદાચ કામ ન કરે"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"સ્થિતિ <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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. ઉમેરવા માટે બે વાર ટૅપ કરો."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"સ્થિતિ <xliff:g id="POSITION">%1$d</xliff:g>. પસંદ કરવા માટે બે વાર ટૅપ કરો."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ખસેડો"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"<xliff:g id="TILE_NAME">%1$s</xliff:g> દૂર કરો"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="POSITION">%2$d</xliff:g> સ્થિતિ પર <xliff:g id="TILE_NAME">%1$s</xliff:g> ઉમેર્યું છે"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> દૂર કરવામાં આવ્યું છે"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ને <xliff:g id="POSITION">%2$d</xliff:g> સ્થિતિ પર ખસેડ્યું"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"ઝડપી સેટિંગ્સ સંપાદક."</string>
</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 84c073d..1ba0caf 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> को ख़ारिज करें."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> खा़रिज कर दिया गया."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"हाल ही के सभी ऐप्लिकेशन ख़ारिज कर दिए गए."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> प्रारंभ हो रहा है."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"नोटिफिकेशन खारिज की गई."</string>
@@ -309,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"खोज"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> प्रारंभ नहीं किया जा सका."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> को सुरक्षित-मोड में अक्षम किया गया."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"इतिहास"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"साफ़ करें"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Clear all"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"यह ऐप्लिकेशन एकाधिक विंडो का समर्थन नहीं करता है"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"ऐप्लिकेशन एकाधिक विंडो का समर्थन नहीं करता है"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"क्षैतिज रूप से विभाजित करें"</string>
@@ -499,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"चार्ज किए जाने के दौरान बैटरी सेवर उपलब्ध नहीं है"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"बैटरी सेवर"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"निष्पादन और पृष्ठभूमि डेटा को कम करता है"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"बटन <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Back"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"ऊपर तीर"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"नीचे तीर"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"बायां तीर"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"दायां तीर"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"मध्य तीर"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Space"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Play/Pause"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Stop"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Next"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Previous"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Rewind"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Fast Forward"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Numpad <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"सिस्टम"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"होम"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"हाल ही के"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"वापस जाएं"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"नोटिफ़िकेशन"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"कीबोर्ड शॉर्टकट"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"इनपुट पद्धति बदलें"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"ऐप्लिकेशन"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"सहायक"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"ब्राउज़र"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"संपर्क"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"ईमेल"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"संगीत"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"कैलेंडर"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"वॉल्यूम नियंत्रणों के साथ दिखाएं"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"परेशान न करें"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"वॉल्यूम बटन का शॉर्टकट"</string>
@@ -557,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"ऊपर ले जाएं"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"बाएं ले जाएं"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"दाएं ले जाएं"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"हो सकता है कि ऐप्लिकेशन एकाधिक विंडो के साथ काम ना करे"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"स्थिति <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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. जोड़ने के लिए डबल टैप करें."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"स्थिति <xliff:g id="POSITION">%1$d</xliff:g>. चुनने के लिए डबल टैप करें."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"<xliff:g id="TILE_NAME">%1$s</xliff:g> को ले जाएं"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"<xliff:g id="TILE_NAME">%1$s</xliff:g> निकालें"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> को <xliff:g id="POSITION">%2$d</xliff:g> स्थिति में जोड़ा गया"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> निकाल दिया गया है"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> को <xliff:g id="POSITION">%2$d</xliff:g> स्थिति में ले जाया गया"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"त्वरित सेटिंग संपादक."</string>
</resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index e69ad31..81c6276 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -169,6 +169,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Odbacivanje aplikacije <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Aplikacija <xliff:g id="APP">%s</xliff:g> odbačena je."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Odbačene su sve nedavne aplikacije."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Pokretanje aplikacije <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Obavijest je odbačena."</string>
@@ -237,8 +239,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Lokaciju utvrdio GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Zahtjevi za lokaciju aktivni su"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Brisanje svih obavijesti."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"još <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Postavke obavijesti"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Postavke aplikacije <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Zaslon će se automatski zakrenuti."</string>
@@ -311,8 +312,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"pretraži"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Aplikacija <xliff:g id="APP">%s</xliff:g> nije pokrenuta."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"Aplikacija <xliff:g id="APP">%s</xliff:g> onemogućena je u sigurnom načinu."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Povijest"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Izbriši"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Izbriši sve"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Ta aplikacija ne podržava više prozora"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Aplikacija ne podržava više prozora"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Podijeli vodoravno"</string>
@@ -481,8 +481,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Prikaži pri vrhu popisa obavijesti, prikaži na zaslonu i dopusti zvuk"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Više postavki"</string>
<string name="notification_done" msgid="5279426047273930175">"Gotovo"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"Kontrole obavijesti za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Boja i izgled"</string>
<string name="night_mode" msgid="3540405868248625488">"Noćni način rada"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Kalibriranje zaslona"</string>
@@ -502,10 +501,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Štednja baterije nije dostupna tijekom punjenja"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Štednja baterije"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Smanjuje količinu rada i pozadinske podatke"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Tipka <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Početak"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Natrag"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Gore"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Dolje"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Lijevo"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Desno"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Sredina"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tabulator"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Razmaknica"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Unos"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Povratna tipka"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Reprodukcija/pauza"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Zaustavi"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Sljedeće"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Prethodno"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Unatrag"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Brzo naprijed"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Stranica prema gore"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Stranica prema dolje"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Izbriši"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Početak"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"Kraj"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Umetni"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Zaključavanje brojčane tipkovnice"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Brojčana tipkovnica <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Sustav"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Početni zaslon"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Najnovije"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Natrag"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Obavijesti"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Tipkovni prečaci"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Promjena načina unosa"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Aplikacije"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Pomoć"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Preglednik"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontakti"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-pošta"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Izravna poruka"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Glazba"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalendar"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Prikaži s kontrolama glasnoće"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Ne uznemiravaj"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Prečac tipki za glasnoću"</string>
@@ -560,4 +597,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Pomakni prema gore"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Pomakni ulijevo"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Pomakni udesno"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Aplikacija možda neće funkcionirati uz više prozora"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Dodirnite dvaput da biste dodali."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Položaj <xliff:g id="POSITION">%1$d</xliff:g>. Dodirnite dvaput da biste odabrali."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Premjesti <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Ukloni <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"Pločica <xliff:g id="TILE_NAME">%1$s</xliff:g> dodana je na položaj <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"Pločica <xliff:g id="TILE_NAME">%1$s</xliff:g> uklonjena"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"Pločica <xliff:g id="TILE_NAME">%1$s</xliff:g> premještena je na položaj <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Uređivač brzih postavki."</string>
</resources>
diff --git a/packages/SystemUI/res/values-hr/strings_tv.xml b/packages/SystemUI/res/values-hr/strings_tv.xml
index 3d5c8f8..340a613 100644
--- a/packages/SystemUI/res/values-hr/strings_tv.xml
+++ b/packages/SystemUI/res/values-hr/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"Držite "<b>"POČETNI"</b>" za PIP"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Pritisnite i zadržite tipku POČETNI ZASLON da biste upravljali slikom u slici"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"Shvaćam"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Odbaci"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index a7d62c3..b7ac8a4 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"A(z) <xliff:g id="APP">%s</xliff:g> elvetése."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> eltávolítva."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Az összes alkalmazás eltávolítva a nemrég használtak közül."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"A(z) <xliff:g id="APP">%s</xliff:g> indítása."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Értesítés elvetve."</string>
@@ -309,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"keresés"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Nem lehet elindítani a következőt: <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"A(z) <xliff:g id="APP">%s</xliff:g> csökkentett módban ki van kapcsolva."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Előzmények"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Törlés"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Összes törlése"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Ez az alkalmazás nem támogatja a többablakos nézetet"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Az alkalmazás nem támogatja a többablakos nézetet"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Osztott vízszintes"</string>
@@ -499,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Az Akkumulátorkímélő módot töltés közben nem lehet használni"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Akkumulátorkímélő mód"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Csökkenti a teljesítményt és a háttéradatok használatát"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"<xliff:g id="NAME">%1$s</xliff:g> gomb"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Kezdőképernyő"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Vissza"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Fel"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Le"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Balra"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Jobbra"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Középre"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Szóköz"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Visszatörlés"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Lejátszás/szünet"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Leállítás"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Következő"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Előző"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Visszatekerés"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Gyors előretekerés"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Kezdőképernyő"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Numerikus: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Rendszer"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Otthon"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Legutóbbiak"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Vissza"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Értesítések"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Billentyűkódok"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Beviteli mód váltása"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Alkalmazások"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Segédalkalmazás"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Böngésző"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Névjegyek"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-mail"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Azonnali üzenetküldés"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Zene"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Naptár"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Megjelenítés hangerőszabályzóval"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Ne zavarjanak"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"A hangerőgombok gyorsbillentyűk"</string>
@@ -557,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Mozgatás felfelé"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Mozgatás balra"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Mozgatás jobbra"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Lehet, hogy az alkalmazás nem működik többablakos nézetben"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"<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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Koppintson duplán a hozzáadáshoz."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"<xliff:g id="POSITION">%1$d</xliff:g>. pozíció. Koppintson duplán a kiválasztáshoz."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"A(z) <xliff:g id="TILE_NAME">%1$s</xliff:g> áthelyezése"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"A(z) <xliff:g id="TILE_NAME">%1$s</xliff:g> eltávolítása"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"A(z) <xliff:g id="TILE_NAME">%1$s</xliff:g> hozzáadva <xliff:g id="POSITION">%2$d</xliff:g>. pozícióban"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"A(z) <xliff:g id="TILE_NAME">%1$s</xliff:g> eltávolítva"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"A(z) <xliff:g id="TILE_NAME">%1$s</xliff:g> áthelyezve a(z) <xliff:g id="POSITION">%2$d</xliff:g>. pozícióba"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Gyorsbeállítások szerkesztője"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml
index 79e5268..7e4e01a 100644
--- a/packages/SystemUI/res/values-hy-rAM/strings.xml
+++ b/packages/SystemUI/res/values-hy-rAM/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Անտեսել <xliff:g id="APP">%s</xliff:g>-ը:"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g>-ը անտեսված է:"</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Բոլոր վերջին հավելվածները հեռացվել են ցուցակից:"</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Մեկնարկել <xliff:g id="APP">%s</xliff:g>-ը:"</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Ծանուցումը անտեսվեց:"</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Տեղադրությունը կարգավորվել է GPS-ի կողմից"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Տեղադրության հարցումներն ակտիվ են"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Մաքրել բոլոր ծանուցումները:"</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Ծանուցման կարգավորումներ"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g>-ի կարգավորումներ"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Էկրանը ինքնաշխատ կպտտվի:"</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"որոնել"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Հնարավոր չէ գործարկել <xliff:g id="APP">%s</xliff:g>-ը:"</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> հավելվածը անվտանգ ռեժիմում անջատված է:"</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Պատմություն"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Մաքրել"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Մաքրել բոլորը"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Այս հավելվածը չի աջակցում բազմապատուհան ռեժիմը"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Հավելվածը չի աջակցում բազմապատուհան ռեժիմը"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Հորիզոնական տրոհում"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Ցույց տալ ծանուցումների ցանկի վերևում, թռուցիկ ցուցադրել էկրանին և թույլատրել ձայնային ազդանշանը"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Այլ կարգավորումներ"</string>
<string name="notification_done" msgid="5279426047273930175">"Պատրաստ է"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի ծանուցումների կառավարներ"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Գույնը և արտաքին տեսքը"</string>
<string name="night_mode" msgid="3540405868248625488">"Գիշերային ռեժիմ"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Չափաբերել էկրանը"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Մարտկոցի տնտեսումը լիցքավորման ժամանակ հասանելի չէ"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Մարտկոցի տնտեսում"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Նվազեցնում է ծանրաբեռնվածությունը և ֆոնային տվյալները"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"<xliff:g id="NAME">%1$s</xliff:g> կոճակ"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Գլխավոր էջ"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Հետ"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Վերև"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Ներքև"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Ձախ"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Աջ"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Կենտրոն"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Բացատ"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Մուտք"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Հետշարժ"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Նվագարկում/դադար"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Դադարեցնել"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Հաջորդը"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Նախորդը"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Հետ անցնել"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Արագ առաջ անցնել"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Ջնջել"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Գլխավոր էջ"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"Վերջ"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Տեղադրել"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Numpad <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Համակարգ"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Գլխավոր էջ"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Վերջինները"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Հետ"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Ծանուցումներ"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Ստեղնային դյուրանցումներ"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Փոխարկել մուտքագրման եղանակը"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Գործադիրներ"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Օգնություն"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Դիտարկիչ"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Կոնտակտներ"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Էլփոստ"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Երաժշտություն"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Օրացույց"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Ցույց տալ ձայնի ուժգնության կառավարման տարրերի հետ"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Չընդհատել"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Ձայնի կոճակների դյուրանցում"</string>
@@ -540,8 +577,7 @@
<string name="select_keycode" msgid="7413765103381924584">"Ընտրեք ստեղնաշարի կոճակը"</string>
<string name="preview" msgid="9077832302472282938">"Նախադիտում"</string>
<string name="drag_to_add_tiles" msgid="7058945779098711293">"Քաշեք՝ սալիկներ ավելացնելու համար"</string>
- <!-- no translation found for drag_to_remove_tiles (3361212377437088062) -->
- <skip />
+ <string name="drag_to_remove_tiles" msgid="3361212377437088062">"Քաշեք այստեղ՝ հեռացնելու համար"</string>
<string name="qs_edit" msgid="2232596095725105230">"Փոփոխել"</string>
<string name="tuner_time" msgid="6572217313285536011">"Ժամ"</string>
<string-array name="clock_options">
@@ -560,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Տեղափոխել վերև"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Տեղափոխել ձախ"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Տեղափոխել աջ"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Հավելվածը չի կարող աշխատել բազմապատուհան ռեժիմում"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"Դիրք <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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>: Կրկնակի հպեք՝ ավելացնելու համար:"</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Դիրք <xliff:g id="POSITION">%1$d</xliff:g>: Կրկնակի հպեք՝ ընտրելու համար:"</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Տեղափոխել <xliff:g id="TILE_NAME">%1$s</xliff:g> սալիկը"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Հեռացնել <xliff:g id="TILE_NAME">%1$s</xliff:g> սալիկը"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> սալիկն ավելացվել է <xliff:g id="POSITION">%2$d</xliff:g> դիրքին"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> սալիկը հեռացվել է"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> սալիկը տեղափոխվել է դեպի <xliff:g id="POSITION">%2$d</xliff:g> դիրք"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Արագ կարգավորումների խմբագրիչ:"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hy-rAM/strings_tv.xml b/packages/SystemUI/res/values-hy-rAM/strings_tv.xml
index dc4c312..5f9c127 100644
--- a/packages/SystemUI/res/values-hy-rAM/strings_tv.xml
+++ b/packages/SystemUI/res/values-hy-rAM/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"PIP-ն կառավարելու համար սեղմած պահեք "<b>"HOME"</b>" կոճակը"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"PIP-ն կառավարելու համար սեղմեք և պահեք HOME կոճակը"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"Պարզ է"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Փակել"</string>
</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 3ac5904..2b51426 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Menyingkirkan <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> disingkirkan."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Semua aplikasi terbaru telah ditutup."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Memulai <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notifikasi disingkirkan."</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Lokasi yang disetel oleh GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Permintaan lokasi aktif"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Menghapus semua pemberitahuan."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Setelan pemberitahuan"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> setelan"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Layar akan diputar secara otomatis."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"telusuri"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Tidak dapat memulai <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> dinonaktifkan dalam mode aman."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Riwayat"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Hapus"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Hapus semua"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Aplikasi ini tidak mendukung multijendela"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Aplikasi tidak mendukung multijendela"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Pisahkan Horizontal"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Tampilkan di bagian atas daftar notifikasi, muncul di layar, dan izinkan suara"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Setelan lainnya"</string>
<string name="notification_done" msgid="5279426047273930175">"Selesai"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"Kontrol notifikasi <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Warna dan tampilan"</string>
<string name="night_mode" msgid="3540405868248625488">"Mode malam"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Kalibrasi layar"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Penghemat Baterai tidak tersedia selama pengisian daya"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Penghemat Baterai"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Mengurangi performa dan data latar belakang"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Tombol <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Back"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Up"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Down"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Left"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Right"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Center"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Space"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Play/Pause"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Stop"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Next"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Previous"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Rewind"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Fast Forward"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Numpad <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Sistem"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Layar Utama"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Terbaru"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Kembali"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Notifikasi"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Pintasan Keyboard"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Beralih metode masukan"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Aplikasi"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Bantuan"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Browser"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontak"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Email"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Musik"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalender"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Tampilkan dengan kontrol volume"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Jangan ganggu"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Pintasan tombol volume"</string>
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Naikkan"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Pindahkan ke kiri"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Pindahkan ke kanan"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Aplikasi mungkin tidak berfungsi dengan multi-jendela"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Ketuk dua kali untuk menambahkan."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Posisi <xliff:g id="POSITION">%1$d</xliff:g>. Ketuk dua kali untuk memilih."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Pindahkan <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Hapus <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ditambahkan ke posisi <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> dihapus"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> dpindahkan ke posisi <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Editor setelan cepat."</string>
</resources>
diff --git a/packages/SystemUI/res/values-in/strings_tv.xml b/packages/SystemUI/res/values-in/strings_tv.xml
index 8e3a5e0..7de1a78 100644
--- a/packages/SystemUI/res/values-in/strings_tv.xml
+++ b/packages/SystemUI/res/values-in/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"Tahan "<b>"LAYAR UTAMA"</b>" untuk mengontrol PIP"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Tekan dan tahan tombol LAYAR UTAMA untuk mengontrol PIP"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"Mengerti"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Tutup"</string>
</resources>
diff --git a/packages/SystemUI/res/values-is-rIS/strings.xml b/packages/SystemUI/res/values-is-rIS/strings.xml
index c063c73..1dc37df 100644
--- a/packages/SystemUI/res/values-is-rIS/strings.xml
+++ b/packages/SystemUI/res/values-is-rIS/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Hunsa <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> vísað frá."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Öll nýleg forrit fjarlægð."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Ræsir <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Tilkynningu lokað."</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Staðsetning valin með GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Staðsetningarbeiðnir virkar"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Hreinsa allar tilkynningar."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Tilkynningastillingar"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Stillingar fyrir <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Skjárinn snýst sjálfkrafa."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"leita"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Ekki var hægt að ræsa <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"Slökkt er á <xliff:g id="APP">%s</xliff:g> í öruggri stillingu."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Ferill"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Hreinsa"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Hreinsa allt"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Þetta forrit styður ekki marga glugga"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Forritið styður ekki marga glugga"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Lárétt skipting"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Sýna efst á tilkynningalistanum, birta á skjánum og leyfa hljóð"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Fleiri stillingar"</string>
<string name="notification_done" msgid="5279426047273930175">"Lokið"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"Tilkynningastýringar <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Litur og útlit"</string>
<string name="night_mode" msgid="3540405868248625488">"Næturstilling"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Kvarða skjáinn"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Ekki er hægt að nota rafhlöðusparnað meðan á hleðslu stendur"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Rafhlöðusparnaður"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Dregur úr afköstum og bakgrunnsgögnum"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Hnappur <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Til baka"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Upp"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Niður"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Vinstri"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Hægri"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Miðja"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Bilslá"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Bakklykill"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Spila/gera hlé"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Stöðva"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Áfram"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Fyrri"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Spóla til baka"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Spóla áfram"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Tölulykill <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Kerfi"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Heim"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Nýlegt"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Til baka"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Tilkynningar"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Flýtilyklar"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Skipta um innsláttaraðferð"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Forrit"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Aðstoð"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Vafri"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Tengiliðir"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Tölvupóstur"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Spjall"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Tónlist"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Dagatal"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Sýna með hljóðstyrksstillingum"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Ónáðið ekki"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Flýtihnappar fyrir hljóðstyrk"</string>
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Færa upp"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Færa til vinstri"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Færa til hægri"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Hugsanlegt er að forritið virki ekki í mörgum gluggum"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Ýttu tvisvar til að bæta við."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Staða <xliff:g id="POSITION">%1$d</xliff:g>. Ýttu tvisvar til að velja."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Færa <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Fjarlægja <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> er bætt við í stöðu <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> var fjarlægð"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> færð í stöðu <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Flýtistillingaritill."</string>
</resources>
diff --git a/packages/SystemUI/res/values-is-rIS/strings_tv.xml b/packages/SystemUI/res/values-is-rIS/strings_tv.xml
index 053beaf..09c67ec 100644
--- a/packages/SystemUI/res/values-is-rIS/strings_tv.xml
+++ b/packages/SystemUI/res/values-is-rIS/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"Haltu "<b>"HOME"</b>"-hnappinum inni til að stjórna innfelldri mynd"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Haltu „Home“-hnappinum inni til að stjórna innfelldri mynd"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"Ég skil"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Hunsa"</string>
</resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 5ccc39c..4a8ba99 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Elimina <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> eliminata."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Tutte le applicazioni recenti sono state rimosse."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Avvio di <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notifica eliminata."</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Posizione stabilita dal GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Richieste di accesso alla posizione attive"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Cancella tutte le notifiche."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Impostazioni di notifica"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Impostazioni di <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Lo schermo ruoterà automaticamente."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"cerca"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Impossibile avviare <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"L\'app <xliff:g id="APP">%s</xliff:g> è stata disattivata in modalità provvisoria."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Cronologia"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Cancella"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Cancella tutto"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Questa app non supporta la modalità multi-finestra"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"L\'app non supporta la modalità multi-finestra"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Divisione in orizzontale"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Mostra in cima all\'elenco delle notifiche, visualizza sullo schermo e attiva l\'audio"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Altre impostazioni"</string>
<string name="notification_done" msgid="5279426047273930175">"Fine"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"Controlli di notifica per <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Colore e aspetto"</string>
<string name="night_mode" msgid="3540405868248625488">"Modalità notturna"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Calibra display"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Risparmio energetico non disponibile durante la ricarica"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Risparmio energetico"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Riduce le prestazioni e i dati in background"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Pulsante <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home page"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Indietro"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Su"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Giù"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Sinistra"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Destra"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Al centro"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Scheda"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Spazio"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Invio"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Play/Pausa"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Interrompi"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Avanti"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Precedente"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Riavvolgi"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Avanza velocemente"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Pagina su"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Pagina giù"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Elimina"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home page"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"Fine"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"INS"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Bloc Num"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Tastierino numerico <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Sistema"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Home"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Recenti"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Indietro"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Notifiche"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Scorciatoie da tastiera"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Cambia metodo di immissione"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Applicazioni"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Assistenza"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Browser"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Contatti"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Email"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Chat"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Musica"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Calendario"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Mostra con controlli volume"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Non disturbare"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Pulsanti del volume come scorciatoia"</string>
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Sposta su"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Sposta a sinistra"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Sposta a destra"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"L\'app potrebbe non funzionare con la modalità multi-finestra"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Tocca due volte per aggiungere."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Posizione <xliff:g id="POSITION">%1$d</xliff:g>. Tocca due volte per selezionare."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Sposta <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Rimuovi <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"Il riquadro <xliff:g id="TILE_NAME">%1$s</xliff:g> è stato aggiunto alla posizione <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"Il riquadro <xliff:g id="TILE_NAME">%1$s</xliff:g> è stato rimosso"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"Il riquadro <xliff:g id="TILE_NAME">%1$s</xliff:g> è stato spostato nella posizione <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Editor di impostazioni rapide."</string>
</resources>
diff --git a/packages/SystemUI/res/values-it/strings_tv.xml b/packages/SystemUI/res/values-it/strings_tv.xml
index 5001b95..4ea019b 100644
--- a/packages/SystemUI/res/values-it/strings_tv.xml
+++ b/packages/SystemUI/res/values-it/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"Tieni premuto "<b>"HOME"</b>" per controllare PIP"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Tieni premuto il pulsante HOME per controllare PIP"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"OK"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Ignora"</string>
</resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 52c83ba..d41d50d 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -170,6 +170,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"סגור את <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> נדחה."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"כל האפליקציות האחרונות נסגרו."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"מפעיל את <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"הודעה נדחתה."</string>
@@ -238,8 +240,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"מיקום מוגדר על ידי GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"בקשות מיקום פעילות"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"נקה את כל ההתראות."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"הגדרות עבור הודעות"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"הגדרות <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"המסך יסתובב באופן אוטומטי."</string>
@@ -312,8 +313,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"חפש"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"לא ניתן היה להפעיל את <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> מושבת במצב בטוח."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"היסטוריה"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"נקה"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"נקה הכל"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"אפליקציה זו אינה תומכת בריבוי חלונות"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"האפליקציה אינה תומכת בריבוי חלונות"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"פיצול אופקי"</string>
@@ -482,8 +482,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"הצג בראש רשימת ההודעות, הצג לרגע על גבי המסך ואשר השמעת צליל"</string>
<string name="notification_more_settings" msgid="816306283396553571">"הגדרות נוספות"</string>
<string name="notification_done" msgid="5279426047273930175">"סיום"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> פקדי הודעות"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"צבע ומראה"</string>
<string name="night_mode" msgid="3540405868248625488">"מצב לילה"</string>
<string name="calibrate_display" msgid="5974642573432039217">"כיול תצוגה"</string>
@@ -503,10 +502,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"תכונת החיסכון בסוללה אינה זמינה בעת טעינת המכשיר"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"חיסכון בסוללה"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"מפחית את רמת הביצועים ואת נתוני הרקע"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"לחצן <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"דף הבית"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"הקודם"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"למעלה"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"למטה"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"שמאלה"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"ימינה"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"מרכז"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"כרטיסייה"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"רווח"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"BACKSPACE"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"הפעל/השהה"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"עצור"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"הבא"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"הקודם"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"הרץ אחורה"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"הרץ קדימה"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"דפדוף למעלה"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"דפדוף למטה"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"מחיקה"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"דף הבית"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"סיום"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"הזן"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"מקש Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"מקלדת נומרית <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"מערכת"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"דף הבית"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"אחרונים"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"הקודם"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"הודעות"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"מקשי קיצור במקלדת"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"החלפת שיטת קלט"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"אפליקציות"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"מסייע"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"דפדפן"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"אנשי קשר"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"אימייל"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"שליחת הודעות מיידיות"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"מוזיקה"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"יומן"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"הצג עם פקדי עוצמת הקול"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"נא לא להפריע"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"מקש קיצור ללחצני עוצמת קול"</string>
@@ -542,8 +579,7 @@
<string name="select_keycode" msgid="7413765103381924584">"בחירת לחצן מקלדת"</string>
<string name="preview" msgid="9077832302472282938">"תצוגה מקדימה"</string>
<string name="drag_to_add_tiles" msgid="7058945779098711293">"גרור כדי להוסיף משבצות"</string>
- <!-- no translation found for drag_to_remove_tiles (3361212377437088062) -->
- <skip />
+ <string name="drag_to_remove_tiles" msgid="3361212377437088062">"גרור לכאן כדי להסיר"</string>
<string name="qs_edit" msgid="2232596095725105230">"ערוך"</string>
<string name="tuner_time" msgid="6572217313285536011">"שעה"</string>
<string-array name="clock_options">
@@ -562,4 +598,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"הזז למעלה"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"הזז שמאלה"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"הזז ימינה"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"ייתכן שהאפליקציה לא תפעל עם ריבוי חלונות"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"מיקום <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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. הקש פעמיים כדי להוסיף."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"מיקום <xliff:g id="POSITION">%1$d</xliff:g>. הקש פעמיים כדי לבחור."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"הזזת <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"הסרת <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> התווסף למיקום <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> הוסר"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> הועבר למיקום <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"עורך הגדרות מהירות."</string>
</resources>
diff --git a/packages/SystemUI/res/values-iw/strings_tv.xml b/packages/SystemUI/res/values-iw/strings_tv.xml
index f4d4094..9130d53 100644
--- a/packages/SystemUI/res/values-iw/strings_tv.xml
+++ b/packages/SystemUI/res/values-iw/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"לחץ לחיצה ארוכה על "<b>"דף הבית"</b>" כדי לשלוט ב-PIP"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"לחץ לחיצה ממושכת על לחצן דף הבית כדי לשלוט ב-PIP"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"הבנתי"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"דחה"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index f7aacee..83a9888 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g>を削除します。"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g>は削除されました。"</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"最近のアプリケーションをすべて消去しました。"</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g>を開始しています。"</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"通知が削除されました。"</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"GPSにより現在地が設定されました"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"現在地リクエストがアクティブ"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"通知をすべて消去。"</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"他 <xliff:g id="NUMBER">%s</xliff:g> 件"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"通知設定"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g>の設定"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"画面は自動的に回転します。"</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"検索"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g>を開始できません。"</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"「<xliff:g id="APP">%s</xliff:g>」はセーフモードでは無効になります。"</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"履歴"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"消去"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"すべて消去"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"このアプリはマルチウィンドウに対応していません"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"アプリはマルチウィンドウに対応していません"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"横に分割"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"通知リストの先頭に表示し、画面に数秒間表示し、音声でも知らせる"</string>
<string name="notification_more_settings" msgid="816306283396553571">"詳細設定"</string>
<string name="notification_done" msgid="5279426047273930175">"完了"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」の通知の管理"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"色と表示"</string>
<string name="night_mode" msgid="3540405868248625488">"夜間モード"</string>
<string name="calibrate_display" msgid="5974642573432039217">"表示の調整"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"充電中はバッテリー セーバーは利用できません"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"バッテリー セーバー"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"パフォーマンスとバックグラウンド データを制限します"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"<xliff:g id="NAME">%1$s</xliff:g> ボタン"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"戻る"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"上"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"下"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"左"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"右"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"中央"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Space"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"再生 / 一時停止"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"停止"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"次へ"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"前へ"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"巻き戻し"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"早送り"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"PageUp"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"PageDown"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"NumLock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"テンキーの <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"システム"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"ホーム"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"最近"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"戻る"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"通知"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"キーボード ショートカット"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"入力方法の切り替え"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"アプリ"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"アシスト"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"ブラウザ"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"連絡先"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"メール"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"音楽"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"カレンダー"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"音量調節を表示"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"通知の非表示"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"音量ボタンのショートカット"</string>
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"上に移動"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"左に移動"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"右に移動"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"アプリはマルチウィンドウでは動作しないことがあります"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"ポジション <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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g> を追加するにはダブルタップします。"</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"ポジション <xliff:g id="POSITION">%1$d</xliff:g> に配置します。選択するにはダブルタップします。"</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"<xliff:g id="TILE_NAME">%1$s</xliff:g> を移動します"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"<xliff:g id="TILE_NAME">%1$s</xliff:g> を削除します"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> をポジション <xliff:g id="POSITION">%2$d</xliff:g> に追加しました"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> を削除しました"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> をポジション <xliff:g id="POSITION">%2$d</xliff:g> に移動しました"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"クイック設定エディタ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ja/strings_tv.xml b/packages/SystemUI/res/values-ja/strings_tv.xml
index a4c49b3..0f0032f 100644
--- a/packages/SystemUI/res/values-ja/strings_tv.xml
+++ b/packages/SystemUI/res/values-ja/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"PIP を管理するには ["<b>"ホーム"</b>"] を押し続けます"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"PIP を管理するには [ホーム] ボタンを押し続けます"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"閉じる"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"閉じる"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml
index 9730323..5008fe0 100644
--- a/packages/SystemUI/res/values-ka-rGE/strings.xml
+++ b/packages/SystemUI/res/values-ka-rGE/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g>-ის უგულებელყოფა."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> ამოშლილია სიიდან."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"ყველა ბოლო აპლიკაცია გაუქმდა."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> იწყება."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"შეტყობინება წაიშალა."</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"GPS-ით დადგენილი მდებარეობა"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"მდებარეობის მოთხოვნები აქტიურია"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"ყველა შეტყობინების წაშლა"</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"შეტყობინების პარამეტრები"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> პარამეტრები"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"ეკრანი შეტრიალდება ავტომატურად."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"ძიება"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g>-ის გამოძახება ვერ მოხერხდა."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> გათიშულია უსაფრთხო რეჟიმში."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"ისტორია"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"გასუფთავება"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"ყველას გასუფთავება"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"მრავალი ფანჯრის რეჟიმი არ არის მხარდაჭერილი ამ აპის მიერ"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"მრავალი ფანჯრის რეჟიმი არ არის მხარდაჭერილი აპის მიერ"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"ჰორიზონტალური გაყოფა"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"შეტყობინებების სიის თავში ჩვენება, პირდაპირ ეკრანზე გამოჩენა და ხმის დაშვება"</string>
<string name="notification_more_settings" msgid="816306283396553571">"დამატებითი პარამეტრები"</string>
<string name="notification_done" msgid="5279426047273930175">"მზადაა"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> შეტყობინებების მართვის საშუალებები"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"ფერი და იერსახე"</string>
<string name="night_mode" msgid="3540405868248625488">"ღამის რეჟიმი"</string>
<string name="calibrate_display" msgid="5974642573432039217">"ეკრანის კალიბრაცია"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"ბატარეის დამზოგი დატენვისას მიწვდომელია"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"ბატარეის დამზოგი"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"ამცირებს წარმადობას და ფონურ მონაცემებს"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"ღილაკი „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"უკან"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"ზემოთ"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"ქვემოთ"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"მარცხნივ"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"მარჯვნივ"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"ცენტრში"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"შორისი"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"დაკვრა/პაუზა"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"შეწყვეტა"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"შემდეგი"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"წინა"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"უკან გადახვევა"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"წინ გადახვევა"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"რიცხვთა პანელი <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"სისტემა"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"მთავარი"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"ბოლოს გამოყენებული"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"უკან"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"შეტყობინებები"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"კლავიატურის მალსახმობები"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"შეყვანის მეთოდის გადართვა"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"აპლიკაციები"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"დახმარება"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"ბრაუზერი"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"კონტაქტები"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"ელფოსტა"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"მუსიკა"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"კალენდარი"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"ხმის მართვის საშუალებების ჩვენება"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"არ შემაწუხოთ"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"ხმის ღილაკების მალსახმობი"</string>
@@ -540,8 +577,7 @@
<string name="select_keycode" msgid="7413765103381924584">"აირჩიეთ კლავიატურის ღილაკი"</string>
<string name="preview" msgid="9077832302472282938">"გადახედვა"</string>
<string name="drag_to_add_tiles" msgid="7058945779098711293">"ფილების დასამატებლად, გადაიტანეთ ჩავლებით"</string>
- <!-- no translation found for drag_to_remove_tiles (3361212377437088062) -->
- <skip />
+ <string name="drag_to_remove_tiles" msgid="3361212377437088062">"ამოსაშლელად, ჩავლებით გადმოიტანეთ აქ"</string>
<string name="qs_edit" msgid="2232596095725105230">"რედაქტირება"</string>
<string name="tuner_time" msgid="6572217313285536011">"დრო"</string>
<string-array name="clock_options">
@@ -560,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"ზემოთ გადატანა"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"მარცხნივ გადატანა"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"მარჯვნივ გადატანა"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"აპმა შეიძლება არ იმუშაოს მრავალი ფანჯრის რეჟიმში"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"პოზიცია <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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. დასამატებლად, შეეხეთ ორმაგად."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"პოზიცია <xliff:g id="POSITION">%1$d</xliff:g>. ასარჩევად, შეეხეთ ორმაგად."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"<xliff:g id="TILE_NAME">%1$s</xliff:g>-ის გადატანა"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"<xliff:g id="TILE_NAME">%1$s</xliff:g>-ის წაშლა"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> დამატებულია პოზიციაზე <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ამოიშალა"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> გადატანილია პოზიციაზე <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"სწრაფი პარამეტრების რედაქტორი."</string>
</resources>
diff --git a/packages/SystemUI/res/values-ka-rGE/strings_tv.xml b/packages/SystemUI/res/values-ka-rGE/strings_tv.xml
index 3a556ae..7d615ba 100644
--- a/packages/SystemUI/res/values-ka-rGE/strings_tv.xml
+++ b/packages/SystemUI/res/values-ka-rGE/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"PIP-ის სამართავად, გეჭიროთ "<b>"მთავარ ღილაკზე"</b></string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"PIP-ის სამართავად, ხანგრძლივად დააჭირეთ მთავარ ღილაკს"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"გასაგებია"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"დახურვა"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kk-rKZ/strings.xml b/packages/SystemUI/res/values-kk-rKZ/strings.xml
index f6c1152..ff33f05 100644
--- a/packages/SystemUI/res/values-kk-rKZ/strings.xml
+++ b/packages/SystemUI/res/values-kk-rKZ/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> қолданбасынан бас тарту."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> алынып тасталған."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Барлық жақындағы қабылданбаған қолданбалар."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> іске қосылуда."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Хабар алынып тасталды."</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Орын GPS арқылы орнатылған"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Орын өтініштері қосылған"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Барлық хабарларды жойыңыз."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Хабарландыру параметрлері"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> параметрлері"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Экран автоматты түрде бұрылады."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"іздеу"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> іске қосу мүмкін болмады."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> қауіпсіз режимде өшіріледі."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Тарих"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Тазалау"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Барлығын тазалау"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Бұл қолданба көп терезені қолдамайды"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Қолданба көп терезені қолдамайды"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Бөлінген көлденең"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Хабарландырулар тізімінің жоғарғы жағында көрсету, экранда көрсету және дыбысқа рұқсат ету"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Қосымша параметрлер"</string>
<string name="notification_done" msgid="5279426047273930175">"Дайын"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> хабарландыруларды басқару элементтері"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Түс және сыртқы түрі"</string>
<string name="night_mode" msgid="3540405868248625488">"Түнгі режим"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Дисплейді калибрлеу"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Зарядтау кезінде Батарея үнемдегіш қол жетімді емес"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Батарея үнемдегіш"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Өнімділікті және фондық деректерді азайтады"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"<xliff:g id="NAME">%1$s</xliff:g> түймесі"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Артқа"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Жоғары"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Төмен"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Сол"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Оң"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Орталық"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Бос орын"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Ойнату/кідірту"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Тоқтату"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Келесі"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Алдыңғы"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Кері айналдыру"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Алға айналдыру"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Сандық пернетақта <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Жүйе"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Негізгі бет"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Жақындағылар"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Артқа"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Хабарландырулар"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Пернелер тіркесімдері"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Енгізу әдісін ауыстыру"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Қолданбалар"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Көмекші"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Браузер"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Контактілер"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Электрондық пошта"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Mузыка"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Күнтізбе"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Дыбыс деңгейін басқару элементтерімен бірге көрсету"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Мазаламау"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Дыбыс деңгейі түймелерінің төте жолы"</string>
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Жоғары қарай жылжыту"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Солға жылжыту"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Оңға жылжыту"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Қолданба көп тереземен жұмыс істемеуі мүмкін"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"<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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Қосу үшін екі рет түртіңіз."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"<xliff:g id="POSITION">%1$d</xliff:g> орны. Таңдау үшін екі рет түртіңіз."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"<xliff:g id="TILE_NAME">%1$s</xliff:g> жылжыту"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"<xliff:g id="TILE_NAME">%1$s</xliff:g> жою"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> <xliff:g id="POSITION">%2$d</xliff:g> орнына қосылды"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> жойылды"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> <xliff:g id="POSITION">%2$d</xliff:g> орнына жылжытылды"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Жылдам параметрлер өңдегіші."</string>
</resources>
diff --git a/packages/SystemUI/res/values-kk-rKZ/strings_tv.xml b/packages/SystemUI/res/values-kk-rKZ/strings_tv.xml
index ef930b0..06c84a8 100644
--- a/packages/SystemUI/res/values-kk-rKZ/strings_tv.xml
+++ b/packages/SystemUI/res/values-kk-rKZ/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"PIP басқару үшін "<b>"HOME"</b>" басып тұрыңыз"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"PIP функциясын басқару үшін HOME түймесін басып тұрыңыз"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"Түсіндім"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Жабу"</string>
</resources>
diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml
index 4a306da..b69f893 100644
--- a/packages/SystemUI/res/values-km-rKH/strings.xml
+++ b/packages/SystemUI/res/values-km-rKH/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"បោះបង់ <xliff:g id="APP">%s</xliff:g> ។"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> បដិសេធ។"</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"កម្មវិធីថ្មីៗទាំងអស់ត្រូវបានបោះបង់។"</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"ចាប់ផ្ដើម <xliff:g id="APP">%s</xliff:g> ។"</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"បានបដិសេធការជូនដំណឹង"</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"ទីតាំងកំណត់ដោយ GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"សំណើទីតាំងសកម្ម"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"សម្អាតការជូនដំណឹងទាំងអស់។"</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"កំណត់ការជូនដំណឹង"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"ការកំណត់ <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"នឹងបង្វិលអេក្រង់ស្វ័យប្រវត្តិ។"</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"ស្វែងរក"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"មិនអាចចាប់ផ្ដើម <xliff:g id="APP">%s</xliff:g> ទេ។"</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> ត្រូវបានបិទដំណើរការក្នុងរបៀបសុវត្ថិភាព"</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"ប្រវត្តិ"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"សម្អាត"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"ជម្រះទាំងអស់"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"កម្មវិធីនេះមិនគាំទ្រផ្ទាំងវិដូច្រើនទេ"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"កម្មវិធីមិនគាំទ្រផ្ទាំងវិដូច្រើនទេ"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"បំបែកផ្តេក"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"បង្ហាញនៅផ្នែកខាងលើបញ្ជីនៃការជូនដំណឹង លោតបង្ហាញនៅលើអេក្រង់ និងអនុញ្ញាតឲ្យបន្លឺសំឡេង"</string>
<string name="notification_more_settings" msgid="816306283396553571">"ការកំណត់ច្រើនទៀត"</string>
<string name="notification_done" msgid="5279426047273930175">"រួចរាល់"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"អង្គគ្រប់គ្រងការជូនដំណឹង <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"ពណ៌ និងរូបរាង"</string>
<string name="night_mode" msgid="3540405868248625488">"របៀបពេលយប់"</string>
<string name="calibrate_display" msgid="5974642573432039217">"ការបង្ហាញក្រិត"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"កម្មវិធីសន្សំថ្មមិនអាចប្រើបានអំឡុងពេលសាកថ្មទេ"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"កម្មវិធីសន្សំថ្ម"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"កាត់បន្ថយប្រតិបត្តិការ និងទិន្នន័យផ្ទៃខាងក្រោយ"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"ប៊ូតុង <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Back"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Up"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Down"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Left"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Right"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Center"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Space"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Play/Pause"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Stop"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Next"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Previous"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Rewind"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Fast Forward"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Numpad <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"ប្រព័ន្ធ"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"ដើម"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"ថ្មីៗ"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"ថយក្រោយ"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"ការជូនដំណឹង"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"ផ្លូវកាត់ក្ដារចុច"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"ប្ដូរវិធីសាស្ត្របញ្ចូល"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"កម្មវិធី"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"ជំនួយ"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"កម្មវិធីរុករក"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"ទំនាក់ទំនង"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"អ៊ីមែល"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"តន្ត្រី"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"ប្រតិទិន"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"បង្ហាញជាមួយការគ្រប់គ្រងកម្រិតសំឡេង"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"កុំរំខាន"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"ផ្លូវកាត់ប៊ូតុងកម្រិតសំឡេង"</string>
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"ផ្លាស់ទីឡើងលើ"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"ផ្លាស់ទីទៅឆ្វេង"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"ផ្លាស់ទីទៅស្តាំ"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"កម្មវិធីអាចនឹងមិនដំណើរការជាមួយពហុវិនដូទេ"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"ទីតាំង <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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>, ប៉ះពីរដងដើម្បីបន្ថែម"</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"ទីតាំង <xliff:g id="POSITION">%1$d</xliff:g>, ប៉ះពីរដងដើម្បីជ្រើស"</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"ផ្លាស់ទី <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"យក <xliff:g id="TILE_NAME">%1$s</xliff:g> ចេញ"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ត្រូវបានបន្ថែមទៅទីតាំង <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ត្រូវបានយកចេញ"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> បានផ្លាស់ទីទៅទីតាំង <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"កម្មវិធីកែការកំណត់រហ័ស"</string>
</resources>
diff --git a/packages/SystemUI/res/values-km-rKH/strings_tv.xml b/packages/SystemUI/res/values-km-rKH/strings_tv.xml
index 8076010..123cb76 100644
--- a/packages/SystemUI/res/values-km-rKH/strings_tv.xml
+++ b/packages/SystemUI/res/values-km-rKH/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"សង្កត់ប៊ូតុង "<b>"ដើម"</b>" ដើម្បីគ្រប់គ្រង PIP"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"ចុច និងសង្កត់ប៊ូតុង ដើម ដើម្បីគ្រប់គ្រង PIP"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"យល់ហើយ"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"បដិសេធ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kn-rIN/strings.xml b/packages/SystemUI/res/values-kn-rIN/strings.xml
index 046d5d8..1ce14dd5 100644
--- a/packages/SystemUI/res/values-kn-rIN/strings.xml
+++ b/packages/SystemUI/res/values-kn-rIN/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> ವಜಾಗೊಳಿಸು."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> ವಜಾಗೊಳಿಸಲಾಗಿದೆ."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"ಇತ್ತೀಚಿನ ಎಲ್ಲಾ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ವಜಾಗೊಳಿಸಲಾಗಿದೆ."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> ಪ್ರಾರಂಭಿಸಲಾಗುತ್ತಿದೆ."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"ಅಧಿಸೂಚನೆ ವಜಾಗೊಂಡಿದೆ."</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"ಸ್ಥಾನವನ್ನು GPS ಮೂಲಕ ಹೊಂದಿಸಲಾಗಿದೆ"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"ಸ್ಥಾನ ವಿನಂತಿಗಳು ಸಕ್ರಿಯವಾಗಿವೆ"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"ಎಲ್ಲಾ ಅಧಿಸೂಚನೆಗಳನ್ನು ತೆರವುಗೊಳಿಸು."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"ಅಧಿಸೂಚನೆ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"ಪರದೆಯು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ತಿರುಗುತ್ತದೆ."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"ಹುಡುಕಾಟ"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> ಪ್ರಾರಂಭಿಸಲು ಸಾದ್ಯವಿಲ್ಲ."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> ಅನ್ನು ಸುರಕ್ಷಿತ ಮೋಡ್ನಲ್ಲಿ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"ಇತಿಹಾಸ"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"ತೆರವುಗೊಳಿಸು"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"ಎಲ್ಲವನ್ನೂ ತೆರವುಗೊಳಿಸು"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"ಈ ಅಪ್ಲಿಕೇಶನ್ ಬಹು-ವಿಂಡೊಗಳನ್ನು ಬೆಂಬಲಿಸುವುದಿಲ್ಲ"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"ಅಪ್ಲಿಕೇಶನ್ ಬಹು-ವಿಂಡೊಗಳನ್ನು ಬೆಂಬಲಿಸುವುದಿಲ್ಲ"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"ಅಡ್ಡಲಾಗಿ ವಿಭಜಿಸಿದ"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"ಅಧಿಸೂಚನೆಗಳ ಪಟ್ಟಿಯ ಮೇಲ್ಭಾಗದಲ್ಲಿ ತೋರಿಸು, ಪರದೆಯನ್ನು ವೀಕ್ಷಿಸಿ ಮತ್ತು ಧ್ವನಿ ಅನುಮತಿಸಿ"</string>
<string name="notification_more_settings" msgid="816306283396553571">"ಹೆಚ್ಚಿನ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
<string name="notification_done" msgid="5279426047273930175">"ಮುಗಿದಿದೆ"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಅಧಿಸೂಚನೆ ನಿಯಂತ್ರಣಗಳು"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"ಬಣ್ಣ ಮತ್ತು ಗೋಚರತೆ"</string>
<string name="night_mode" msgid="3540405868248625488">"ರಾತ್ರಿ ಮೋಡ್"</string>
<string name="calibrate_display" msgid="5974642573432039217">"ಅಣಿಗೊಳಿಸುವ ಪ್ರದರ್ಶನ"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"ಚಾರ್ಜಿಂಗ್ ಸಮಯದಲ್ಲಿ ಬ್ಯಾಟರಿ ಸೇವರ್ ಲಭ್ಯವಿರುವುದಿಲ್ಲ"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"ಬ್ಯಾಟರಿ ಸೇವರ್"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"ಕಾರ್ಯಕ್ಷಮತೆ ಮತ್ತು ಹಿನ್ನೆಲೆ ಡೇಟಾವನ್ನು ಕಡಿಮೆ ಮಾಡುತ್ತದೆ"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"<xliff:g id="NAME">%1$s</xliff:g> ಬಟನ್"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"ಹಿಂದೆ"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"ಮೇಲೆ"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"ಕೆಳಗೆ"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"ಎಡ"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"ಬಲ"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"ಮಧ್ಯ"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"ಸ್ಪೇಸ್"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Play/Pause"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Stop"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"ಮುಂದೆ"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"ಹಿಂದೆ"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Rewind"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"ಫಾಸ್ಟ್ ಫಾರ್ವರ್ಡ್"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"<xliff:g id="NAME">%1$s</xliff:g> ಸಂಖ್ಯೆಪ್ಯಾಡ್"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"ಸಿಸ್ಟಂ"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"ಮುಖಪುಟ"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"ಇತ್ತೀಚಿನವುಗಳು"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"ಹಿಂದೆ"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"ಅಧಿಸೂಚನೆಗಳು"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"ಕೀಬೋರ್ಡ್ ಶಾರ್ಟ್ಕಟ್ಗಳು"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"ಇನ್ಪುಟ್ ವಿಧಾನ ಬದಲಿಸಿ"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"ಅಪ್ಲಿಕೇಶನ್ಗಳು"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"ಸಹಾಯ ಮಾಡು"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"ಬ್ರೌಸರ್"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"ಸಂಪರ್ಕಗಳು"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"ಇಮೇಲ್"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"ಸಂಗೀತ"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"ಕ್ಯಾಲೆಂಡರ್"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"ವಾಲ್ಯೂಮ್ ನಿಯಂತ್ರಣಗಳ ಜೊತೆಗೆ ತೋರಿಸು"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"ಅಡಚಣೆ ಮಾಡಬೇಡಿ"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"ವಾಲ್ಯೂಮ್ ಬಟನ್ಗಳ ಶಾರ್ಟ್ಕಟ್"</string>
@@ -540,8 +577,7 @@
<string name="select_keycode" msgid="7413765103381924584">"ಕೀಬೋರ್ಡ್ ಬಟನ್ ಆಯ್ಕೆಮಾಡಿ"</string>
<string name="preview" msgid="9077832302472282938">"ಪೂರ್ವವೀಕ್ಷಣೆ"</string>
<string name="drag_to_add_tiles" msgid="7058945779098711293">"ಟೈಲ್ಗಳನ್ನು ಸೇರಿಸಲು ಡ್ರ್ಯಾಗ್ ಮಾಡಿ"</string>
- <!-- no translation found for drag_to_remove_tiles (3361212377437088062) -->
- <skip />
+ <string name="drag_to_remove_tiles" msgid="3361212377437088062">"ತೆಗೆದುಹಾಕಲು ಇಲ್ಲಿ ಡ್ರ್ಯಾಗ್ ಮಾಡಿ"</string>
<string name="qs_edit" msgid="2232596095725105230">"ಸಂಪಾದಿಸು"</string>
<string name="tuner_time" msgid="6572217313285536011">"ಸಮಯ"</string>
<string-array name="clock_options">
@@ -560,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"ಮೇಲೆ ಸರಿಸಿ"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"ಎಡಕ್ಕೆ ಸರಿಸಿ"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"ಬಲಕ್ಕೆ ಸರಿಸಿ"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"ಬಹು ವಿಂಡೋದಲ್ಲಿ ಅಪ್ಲಿಕೇಶನ್ ಕೆಲಸ ಮಾಡದೇ ಇರಬಹುದು"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"ಸ್ಥಾನ <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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. ಸೇರಿಸಲು ಡಬಲ್ ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"ಸ್ಥಾನ <xliff:g id="POSITION">%1$d</xliff:g>. ಆಯ್ಕೆಮಾಡಲು ಡಬಲ್ ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ಸರಿಸಿ"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ತೆಗೆದುಹಾಕಿ"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="POSITION">%2$d</xliff:g> ಸ್ಥಾನಕ್ಕೆ <xliff:g id="TILE_NAME">%1$s</xliff:g> ಸೇರಿಸಲಾಗಿದೆ"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ತೆಗೆದುಹಾಕಲಾಗಿದೆ"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="POSITION">%2$d</xliff:g> ಸ್ಥಾನಕ್ಕೆ <xliff:g id="TILE_NAME">%1$s</xliff:g> ಸೇರಿಸಲಾಗಿದೆ"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್ಗಳ ಸಂಪಾದಕ."</string>
</resources>
diff --git a/packages/SystemUI/res/values-kn-rIN/strings_tv.xml b/packages/SystemUI/res/values-kn-rIN/strings_tv.xml
index d275de6..6491587 100644
--- a/packages/SystemUI/res/values-kn-rIN/strings_tv.xml
+++ b/packages/SystemUI/res/values-kn-rIN/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"PIP ನಿಯಂತ್ರಿಸಲು "<b>"HOME"</b>" ಕೀಯನ್ನು ಹಿಡಿದುಕೊಳ್ಳಿ"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"PIP ನಿಯಂತ್ರಿಸಲು HOME ಬಟನ್ ಒತ್ತಿರಿ ಮತ್ತು ಹಿಡಿದುಕೊಳ್ಳಿ"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"ಅರ್ಥವಾಯಿತು"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"ವಜಾಗೊಳಿಸಿ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 5e16258..a8a2a0a 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g>을(를) 숨깁니다."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g>이(가) 제거되었습니다."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"최근 사용한 애플리케이션을 모두 닫았습니다."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g>을(를) 시작하는 중입니다."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"알림이 제거되었습니다."</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"GPS에서 위치 설정"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"위치 요청 있음"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"모든 알림 지우기"</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"<xliff:g id="NUMBER">%s</xliff:g>개 더보기"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"알림 설정"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> 설정"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"화면이 자동으로 회전됩니다."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"검색"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g>을(를) 시작할 수 없습니다."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g>은(는) 안전 모드에서 사용 중지됩니다."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"기록"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"삭제"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"모두 지우기"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"이 앱은 다중 창을 지원하지 않습니다."</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"앱이 다중 창을 지원하지 않습니다."</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"수평 분할"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"알림 목록 맨 위에 표시, 화면에 표시하고 소리로 알림"</string>
<string name="notification_more_settings" msgid="816306283396553571">"설정 더보기"</string>
<string name="notification_done" msgid="5279426047273930175">"완료"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> 알림 관리"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"색상 및 모양"</string>
<string name="night_mode" msgid="3540405868248625488">"야간 모드"</string>
<string name="calibrate_display" msgid="5974642573432039217">"디스플레이 보정"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"충전하는 동안 배터리 세이버는 사용할 수 없습니다."</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"배터리 세이버"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"성능 및 백그라운드 데이터를 줄입니다."</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"<xliff:g id="NAME">%1$s</xliff:g> 버튼"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"뒤로"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"위쪽"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"아래쪽"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"왼쪽"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"오른쪽"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"중앙"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Space"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"재생/일시중지"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"중지"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"다음"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"이전"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"되감기"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"빨리 감기"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"숫자 패드 <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"시스템"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"홈"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"최근"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"뒤로"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"알림"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"단축키"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"입력 방법 전환"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"애플리케이션"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"지원"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"브라우저"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"주소록"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"이메일"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"메신저"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"음악"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"캘린더"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"볼륨 컨트롤과 함께 표시"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"알림 일시중지"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"볼륨 버튼 단축키"</string>
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"위로 이동"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"왼쪽으로 이동"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"오른쪽으로 이동"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"앱이 멀티 윈도우에서 작동하지 않을 수 있습니다."</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"위치 <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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. 추가하려면 두 번 탭하세요."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"위치 <xliff:g id="POSITION">%1$d</xliff:g>. 선택하려면 두 번 탭하세요."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"<xliff:g id="TILE_NAME">%1$s</xliff:g> 이동"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"<xliff:g id="TILE_NAME">%1$s</xliff:g> 삭제"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> 타일이 위치 <xliff:g id="POSITION">%2$d</xliff:g>에 추가됩니다."</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> 타일이 삭제되었습니다."</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> 타일을 위치 <xliff:g id="POSITION">%2$d</xliff:g>(으)로 이동함"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"빠른 설정 편집기"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ko/strings_tv.xml b/packages/SystemUI/res/values-ko/strings_tv.xml
index 52747b4..da3ab9e 100644
--- a/packages/SystemUI/res/values-ko/strings_tv.xml
+++ b/packages/SystemUI/res/values-ko/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109"><b>"HOME"</b>"을 눌러 PIP 제어"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"홈 버튼을 길게 눌러 PIP 제어"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"확인"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"닫기"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ky-rKG/strings.xml b/packages/SystemUI/res/values-ky-rKG/strings.xml
index 60db439..6a2d77f 100644
--- a/packages/SystemUI/res/values-ky-rKG/strings.xml
+++ b/packages/SystemUI/res/values-ky-rKG/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> этибарга албоо."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> жок болду."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Акыркы колдонмолордун баары көз жаздымда калтырылды."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> иштеп баштоодо."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Эскертме жок кылынды."</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"GPS боюнча аныкталган жайгашуу"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Жайгаштыруу талаптары иштелүүдө"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Бардык эскертмелерди тазалоо."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Эскертме жөндөөлөрү"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> жөндөөлөрү"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Экран автоматтык түрдө бурулат."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"издөө"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> баштай алган жок."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> коопсуз режиминде өчүрүлдү."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Таржымал"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Тазалоо"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Баарын тазалоо"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Бул колдонмодо бир нече терезе режими колдоого алынбайт"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Колдонмодо бир нече терезе режими колдоого алынбайт"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Туурасынан бөлүү"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Эскертмелер тизмесинин эң башында көрсөтүлүп, үн менен коштолуп, экранга чыгарылсын"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Дагы жөндөөлөр"</string>
<string name="notification_done" msgid="5279426047273930175">"Аткарылды"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> эскертмесин башкаруу каражаттары"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Түсү жана көрүнүшү"</string>
<string name="night_mode" msgid="3540405868248625488">"Түнкү режим"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Дисплейди калибрлөө"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Батареяны үнөмдөгүч түзмөк кубатталып жатканда иштебейт"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Батареяны үнөмдөгүч"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Иштин майнаптуулугун начарлатып, фондук дайындарды чектейт"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"<xliff:g id="NAME">%1$s</xliff:g> баскычы"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Башкы бет"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Артка"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Өйдө"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Төмөн"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Солго"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Оңго"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Ортолотуу"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Өтмөк"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Боштук"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Киргизүү"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Артка өчүрүү"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Ойнотуу/Тындыруу"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Токтотуу"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Кийинки"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Мурунку"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Артка түрүү"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Алдыга түрүү"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Жок кылуу"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Башкы бет"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"Бүтүрүү"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Numpad <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Тутум"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Башкы бет"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Акыркылар"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Артка"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Эскертмелер"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Баскычтоптун кыска жолдору"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Киргизүү ыкмасын которуштуруу"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Колдонмолор"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Көмөкчү"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Серепчи"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Байланыштар"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Электрондук почта"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Музыка"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Жылнаама"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Үн көзөмөлдөгүчтөрү менен көрсөтүлсүн"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Тынчымды алба"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Үндү көзөмөлдөөчү баскычтардын кыска жолдору"</string>
@@ -540,8 +577,7 @@
<string name="select_keycode" msgid="7413765103381924584">"Баскычтоптогу баскычты тандоо"</string>
<string name="preview" msgid="9077832302472282938">"Алдын ала көрүү"</string>
<string name="drag_to_add_tiles" msgid="7058945779098711293">"Керектүү нерселерди сүйрөп кошуңуз"</string>
- <!-- no translation found for drag_to_remove_tiles (3361212377437088062) -->
- <skip />
+ <string name="drag_to_remove_tiles" msgid="3361212377437088062">"Алып салуу үчүн бул жерге сүйрөңүз"</string>
<string name="qs_edit" msgid="2232596095725105230">"Түзөтүү"</string>
<string name="tuner_time" msgid="6572217313285536011">"Убакыт"</string>
<string-array name="clock_options">
@@ -560,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Жогору жылдыруу"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Солго жылдыруу"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Оңго жылдыруу"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Бир нече терезе режиминде колдонмо иштебей калышы мүмкүн"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"Орду - <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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Кошуу үчүн эки жолу таптаңыз."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Орду - <xliff:g id="POSITION">%1$d</xliff:g>. Тандоо үчүн эки жолу таптаңыз."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"<xliff:g id="TILE_NAME">%1$s</xliff:g> дегенди жылдыруу"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"<xliff:g id="TILE_NAME">%1$s</xliff:g> дегенди алып салуу"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> деген <xliff:g id="POSITION">%2$d</xliff:g>-орунга кошулду"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> алынып салынды"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> деген <xliff:g id="POSITION">%2$d</xliff:g>-орунга жылдырылды"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Ыкчам жөндөөлөр түзөткүчү."</string>
</resources>
diff --git a/packages/SystemUI/res/values-ky-rKG/strings_tv.xml b/packages/SystemUI/res/values-ky-rKG/strings_tv.xml
index e54aae2..b030542 100644
--- a/packages/SystemUI/res/values-ky-rKG/strings_tv.xml
+++ b/packages/SystemUI/res/values-ky-rKG/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109"><b>"БАШКЫ БЕТ"</b>" басып туруп PIP\'ти башкарыңыз"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"PIP башкаруу үчүн БАШКЫ БЕТ баскычын басып, кармап туруңуз"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"Түшүндүм"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Көз жаздымда калтыруу"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lo-rLA/strings.xml b/packages/SystemUI/res/values-lo-rLA/strings.xml
index ecb1eb3..601cc2a 100644
--- a/packages/SystemUI/res/values-lo-rLA/strings.xml
+++ b/packages/SystemUI/res/values-lo-rLA/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"ປິດ <xliff:g id="APP">%s</xliff:g> ໄວ້."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"ປິດ <xliff:g id="APP">%s</xliff:g> ແລ້ວ."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"ທຸກແອັບພລິເຄຊັນບໍ່ດົນມານີ້ຖືກປ່ອຍໄປ."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"ກຳລັງເປີດ <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"ປິດການແຈ້ງເຕືອນແລ້ວ."</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"ສະຖານທີ່ກຳນົດໂດຍ GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"ການຮ້ອງຂໍສະຖານທີ່ທີ່ເຮັດວຽກຢູ່"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"ລຶບການແຈ້ງເຕືອນທັງໝົດ."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"ການຕັ້ງຄ່າການແຈ້ງເຕືອນ"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"ການຕັ້ງຄ່າ <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"ໜ້າຈໍຈະໝຸນໂດຍອັດຕະໂນມັດ."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"ຊອກຫາ"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"ບໍ່ສາມາດເລີ່ມ <xliff:g id="APP">%s</xliff:g> ໄດ້."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> ຖືກປິດໃຊ້ໃນໂໝດຄວາມມປອດໄພ."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"ປະຫວັດ"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"ລຶບ"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"ລຶບລ້າງທັງໝົດ"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"ແອັບນີ້ບໍ່ຮອງຮັບຫຼາຍໜ້າຈໍ"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"ແອັບບໍ່ຮອງຮັບຫຼາຍໜ້າຈໍ"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"ການແຍກລວງຂວາງ"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"ສະແດງຢູ່ເທິງສຸດຂອງລາຍການແຈ້ງເຕືອນ, ແຈ້ງໄປໜ້າຈໍ ແລະ ອະນຸຍາດໃຫ້ໃຊ້ສຽງໄດ້"</string>
<string name="notification_more_settings" msgid="816306283396553571">"ການຕັ້ງຄ່າເພີ່ມເຕີມ"</string>
<string name="notification_done" msgid="5279426047273930175">"ສຳເລັດແລ້ວ"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"ການຄວບຄຸມການແຈ້ງເຕືອນ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"ສີ ແລະ ລັກສະນະ"</string>
<string name="night_mode" msgid="3540405868248625488">"ໂໝດກາງຄືນ"</string>
<string name="calibrate_display" msgid="5974642573432039217">"ປັບໜ້າຈໍ"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"ຕົວປະຢັດແບັດເຕີຣີບໍ່ມີໃຫ້ນຳໃຊ້ໃນລະຫວ່າງການສາກ"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"ຕົວປະຢັດແບັດເຕີຣີ"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"ຫຼຸດປະສິທິພາບການໃຊ້ງານ ແລະ ຂໍ້ມູນພື້ນຫຼັງ"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"ປຸ່ມ <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"ກັບຄືນ"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"ຂຶ້ນ"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"ລົງ"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"ຊ້າຍ"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"ຂວາ"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"ເຄິ່ງກາງ"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Space"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"ຫຼິ້ນ/ຢຸດຊົ່ວຄາວ"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"ຢຸດ"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"ຕໍ່ໄປ"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"ກ່ອນໜ້າ"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"ຣີວາຍກັບ"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"ເລື່ອນໄປໜ້າ"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Numpad <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"ລະບົບ"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"ໜ້າຫຼັກ"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"ຫາກໍໃຊ້"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"ກັບຄືນ"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"ການແຈ້ງເຕືອນ"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"ປຸ່ມລັດແປ້ນພິມ"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"ສະລັບຮູບແບບການປ້ອນຂໍ້ມູນ"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"ແອັບພລິເຄຊັນ"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"ຕົວຊ່ວຍ"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"ໂປຣແກຣມທ່ອງເວັບ"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"ລາຍຊື່ຜູ້ຕິດຕໍ່"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"ອີເມວ"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"ດົນຕີ"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"ປະຕິທິນ"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"ສະແດງສ່ວນຄວບຄຸມສຽງ"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"ຫ້າມລົບກວນ"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"ທາງລັດປຸ່ມສຽງ"</string>
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"ເລື່ອນຂຶ້ນ"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"ເລື່ອນຊ້າຍ"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"ເລື່ອນຂວາ"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"ແອັບອາດບໍ່ສາມາດໃຊ້ໄດ້ກັບຫຼາຍໜ້າຈໍ"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"ຕຳແໜ່ງ <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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. ແຕະສອງເທື່ອເພື່ອເພີ່ມ."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"ຕຳແໜ່ງ <xliff:g id="POSITION">%1$d</xliff:g>. ແຕະສອງເທື່ອເພື່ອເລືອກ."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"ຍ້າຍ <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"ລຶບ <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ຖືກເພີ່ມໃສ່ຕຳແໜ່ງ <xliff:g id="POSITION">%2$d</xliff:g> ແລ້ວ"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"ລຶບ <xliff:g id="TILE_NAME">%1$s</xliff:g> ອອກແລ້ວ"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ຍ້າຍໄປຕຳແໜ່ງ <xliff:g id="POSITION">%2$d</xliff:g> ແລ້ວ"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"ຕົວແກ້ໄຂການຕັ້ງຄ່າດ່ວນ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lo-rLA/strings_tv.xml b/packages/SystemUI/res/values-lo-rLA/strings_tv.xml
index 911dcf9..6e36d3f 100644
--- a/packages/SystemUI/res/values-lo-rLA/strings_tv.xml
+++ b/packages/SystemUI/res/values-lo-rLA/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"ກົດ "<b>"HOME"</b>" ຄ້າງໄວ້ເພື່ອຄວບຄຸມ PIP"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"ແຕະປຸ່ມ HOME ຄ້າງໄວ້ເພື່ອຄວບຄຸມຮູບນ້ອຍ"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"ເຂົ້າໃຈແລ້ວ"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"ປິດໄວ້"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 4de42c0..866abf3 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -170,6 +170,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Atsisakyti <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Atsisakyta programos „<xliff:g id="APP">%s</xliff:g>“."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Atsisakyta visų naujausių programų."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Paleidžiama <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"„<xliff:g id="APP">%1$s</xliff:g>“ <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Pranešimo atsisakyta."</string>
@@ -238,8 +240,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"GPS nustatyta vieta"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Vietovės užklausos aktyvios"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Išvalyti visus pranešimus."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"Dar <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Pranešimų nustatymai"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"„<xliff:g id="APP_NAME">%s</xliff:g>“ nustatymai"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Ekranas bus sukamas automatiškai."</string>
@@ -312,8 +313,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"paieška"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Nepavyko paleisti <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"Programa „<xliff:g id="APP">%s</xliff:g>“ išjungta saugos režimu."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Istorija"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Išvalyti"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Išvalyti viską"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Ši programa nepalaiko kelių langų režimo"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Programa nepalaiko kelių langų režimo"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Horizontalus skaidymas"</string>
@@ -482,8 +482,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Rodyti pranešimų sąrašo viršuje, rodyti ekrane ir leisti skambėti"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Daugiau nustatymų"</string>
<string name="notification_done" msgid="5279426047273930175">"Atlikta"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ pranešimų valdikliai"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Spalva ir išvaizda"</string>
<string name="night_mode" msgid="3540405868248625488">"Naktinis režimas"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Kalibruoti ekraną"</string>
@@ -503,10 +502,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Akumuliatoriaus tausojimo priemonė nepasiekiama įkraunant"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Akumuliatoriaus tausojimo priemonė"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Sumažinamas našumas ir foninių duomenų naudojimas"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Mygtukas <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Pagrindinis"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Atgal"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Aukštyn"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Žemyn"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Kairėn"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Dešinėn"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Centras"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tabuliavimo klavišas"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Tarpas"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Įvesti"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Naikinimo klavišas"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Leisti / pristabdyti"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Sustabdyti"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Kitas"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Ankstesnis"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Sukti atgal"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Sukti pirmyn"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Ankstesnis puslapis"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Tolesnis puslapis"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Ištrinti"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Pagrindinis"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"Baigti"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Įterpti"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Skaičių režimas"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Klaviatūros skaitmenų sritis <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Sistema"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Pagrindinis ekranas"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Naujausios veiklos ekranas"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Atgal"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Pranešimai"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Spartieji klavišai"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Perjungti įvesties metodą"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Programos"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Pagalbinė programa"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Naršyklė"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontaktai"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"El. paštas"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"TP"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Muzika"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalendorius"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Rodyti su garsumo valdikliais"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Netrukdymo režimas"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Garsumo mygtukų spartusis klavišas"</string>
@@ -561,4 +598,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Perkelti aukštyn"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Perkelti kairėn"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Perkelti dešinėn"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Naudojant kelių langų funkciją programa gali neveikti"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"<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="8133209638023882667">"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_position_label" msgid="5055306305919289819">"<xliff:g id="POSITION">%1$d</xliff:g> padėtis. Dukart palieskite, kad pasirinktumėte."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Perkelti išklotinės elementą „<xliff:g id="TILE_NAME">%1$s</xliff:g>“"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Pašalinti išklotinės elementą „<xliff:g id="TILE_NAME">%1$s</xliff:g>“"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"Išklotinės elementas „<xliff:g id="TILE_NAME">%1$s</xliff:g>“ pridėtas prie <xliff:g id="POSITION">%2$d</xliff:g> padėties"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"Išklotinės elementas „<xliff:g id="TILE_NAME">%1$s</xliff:g>“ pašalintas"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"Išklotinės elementas „<xliff:g id="TILE_NAME">%1$s</xliff:g>“ perkeltas į <xliff:g id="POSITION">%2$d</xliff:g> padėtį"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Sparčiųjų nustatymų redagavimo priemonė."</string>
</resources>
diff --git a/packages/SystemUI/res/values-lt/strings_tv.xml b/packages/SystemUI/res/values-lt/strings_tv.xml
index 928b4c7..c8fce8a 100644
--- a/packages/SystemUI/res/values-lt/strings_tv.xml
+++ b/packages/SystemUI/res/values-lt/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"Kad vald. PIP, pal. pasp. m. "<b>"PAGRINDINIS"</b></string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Jei norite valdyti PIP, paspauskite ir palaikykite pagrindinio puslapio mygtuką"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"Supratau"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Atsisakyti"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index f473971..9c32b31 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -169,6 +169,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Nerādīt lietotni <xliff:g id="APP">%s</xliff:g>"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Lietotne <xliff:g id="APP">%s</xliff:g> vairs netiek rādīta."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Visas nesen izmantotās lietojumprogrammas tika noņemtas."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Notiek lietotnes <xliff:g id="APP">%s</xliff:g> palaišana."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Paziņojums netiek rādīts."</string>
@@ -310,8 +312,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"Meklēt"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Nevarēja palaist lietotni <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"Lietotne <xliff:g id="APP">%s</xliff:g> ir atspējota drošajā režīmā."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Vēsture"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Notīrīt"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Notīrīt visu"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Šajā lietotnē netiek atbalstīts vairāku logu režīms."</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Lietotnē netiek atbalstīts vairāku logu režīms"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Horizontāls dalījums"</string>
@@ -500,10 +501,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Akumulatora jaudas taupīšanas režīms uzlādes laikā nav pieejams."</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Akumulatora jaudas taupīšanas režīms"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Samazina veiktspēju un fona datus."</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Poga <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Sākumvietas taustiņš"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Atpakaļ"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Uz augšu"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Uz leju"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Pa kreisi"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Pa labi"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Centrā"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tabulēšanas taustiņš"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Atstarpe"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Ievadīšanas taustiņš"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Atpakaļatkāpes taustiņš"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Atskaņot/apturēt"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Apturēt"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Nākamais"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Iepriekšējais"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Attīt atpakaļ"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Pārtīt uz priekšu"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Lapa uz augšu"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Lapa uz leju"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Dzēšanas taustiņš"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Sākumvietas taustiņš"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"Beigvietas taustiņš"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Ievietošanas taustiņš"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Ciparslēga taustiņš"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Cipartastatūra <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Sistēma"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Sākums"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Pēdējie"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Atpakaļ"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Paziņojumi"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Īsinājumtaustiņi"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Pārslēgt ievades metodi"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Lietojumprogrammas"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Palīgs"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Pārlūkprogramma"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontaktpersonas"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-pasts"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Tūlītējā ziņojumapmaiņa"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Mūzika"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalendārs"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Rādīt ar skaļuma vadīklām"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Netraucēt"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Skaļuma pogu saīsne"</string>
@@ -558,4 +597,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Pārvietot uz augšu"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Pārvietot pa kreisi"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Pārvietot pa labi"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Iespējams, lietotnē nedarbosies vairāku logu režīms."</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"<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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Lai pievienotu, veiciet dubultskārienu."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"<xliff:g id="POSITION">%1$d</xliff:g>. pozīcija. Lai atlasītu, veiciet dubultskārienu."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Pārvietot elementu <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Noņemt elementu <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"Elements <xliff:g id="TILE_NAME">%1$s</xliff:g> ir pievienots <xliff:g id="POSITION">%2$d</xliff:g>. pozīcijā"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"Elements <xliff:g id="TILE_NAME">%1$s</xliff:g> ir noņemts"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"Elements <xliff:g id="TILE_NAME">%1$s</xliff:g> ir pārvietots uz <xliff:g id="POSITION">%2$d</xliff:g>. pozīciju"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Ātro iestatījumu redaktors."</string>
</resources>
diff --git a/packages/SystemUI/res/values-mk-rMK/strings.xml b/packages/SystemUI/res/values-mk-rMK/strings.xml
index 1666273..7f2fc15 100644
--- a/packages/SystemUI/res/values-mk-rMK/strings.xml
+++ b/packages/SystemUI/res/values-mk-rMK/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Отфрли <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> е отфрлена."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Сите неодамнешни апликации се отфрлени."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Се стартува <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Известувањето е отфрлено."</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Локацијата е поставена со ГПС"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Активни барања за локација"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Исчисти ги сите известувања."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Поставки на известувања"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Поставки на <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Екранот ќе ротира автоматски."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"пребарај"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> не може да се вклучи."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> е оневозможен во безбеден режим."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Историја"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Исчисти"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Исчисти ги сите"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Апликацијава не поддржува повеќе прозорци"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Апликацијата не поддржува повеќе прозорци"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Раздели хоризонтално"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Прикажувај ги на врвот на списокот со известувања, ѕиркање на екранот и овозможи звук"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Повеќе поставки"</string>
<string name="notification_done" msgid="5279426047273930175">"Готово"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"Контроли за известувања на <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Боја и изглед"</string>
<string name="night_mode" msgid="3540405868248625488">"Ноќен режим"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Калибрирај го екранот"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Штедачот на батерија не е достапен при полнење"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Штедач на батерија"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Ја намалува изведбата и податоците во заднина"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Копче <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Почетна страница"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Назад"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Стрелка нагоре"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Стрелка надолу"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Стрелка налево"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Стрелка надесно"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Центар"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Картичка"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Празно место"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Внеси"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Бришење наназад"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Пушти/Паузирај"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Сопри"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Следно"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Претходно"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Премотување наназад"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Брзо премотување нанапред"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Страница нагоре"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Страница надолу"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Избриши"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Почетна страница"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"Крај"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Вметни"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Numpad <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Систем"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Почетна страница"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Неодамнешни"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Назад"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Известувања"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Кратенки на тастатурата"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Префрли метод за внесување"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Апликации"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Помош"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Прелистувач"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Контакти"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Е-пошта"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Музика"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Календар"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Прикажи со контроли за јачина на звук"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Не вознемирувај"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Кратенка за копчињата за јачина на звук"</string>
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Преместете нагоре"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Преместете налево"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Преместете надесно"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Апликацијата може да не работи со повеќе прозорци"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"Место <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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Допрете двапати за додавање."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Место <xliff:g id="POSITION">%1$d</xliff:g>. Допрете двапати за избирање."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Преместете <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Отстранете <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> е додадена на место <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> е отстранета"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> е преместена на место <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Уредник за брзи поставки."</string>
</resources>
diff --git a/packages/SystemUI/res/values-mk-rMK/strings_tv.xml b/packages/SystemUI/res/values-mk-rMK/strings_tv.xml
index 10bf41f..2d6da0c 100644
--- a/packages/SystemUI/res/values-mk-rMK/strings_tv.xml
+++ b/packages/SystemUI/res/values-mk-rMK/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"Задржете "<b>"ДОМА"</b>" за кон. PIP"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Притиснете и задржете го копчето ДОМА за контролирање PIP"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"Разбрав"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Отфрли"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ml-rIN/strings.xml b/packages/SystemUI/res/values-ml-rIN/strings.xml
index 882f189..dfe992c 100644
--- a/packages/SystemUI/res/values-ml-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ml-rIN/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> നിരസിക്കുക."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> നിരസിച്ചു."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"അടുത്തിടെയുള്ള എല്ലാ അപ്ലിക്കേഷനും നിരസിച്ചു."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> ആരംഭിക്കുന്നു."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"അറിയിപ്പ് നിരസിച്ചു."</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"ലൊക്കേഷൻ സജ്ജീകരിച്ചത് GPS ആണ്"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"ലൊക്കേഷൻ അഭ്യർത്ഥനകൾ സജീവമാണ്"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"എല്ലാ വിവരങ്ങളും മായ്ക്കുക."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"അറിയിപ്പ് ക്രമീകരണങ്ങൾ"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> ക്രമീകരണങ്ങൾ"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"സ്ക്രീൻ യാന്ത്രികമായി തിരിയും."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"തിരയുക"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> ആരംഭിക്കാനായില്ല."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"സുരക്ഷിത മോഡിൽ <xliff:g id="APP">%s</xliff:g> പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"ചരിത്രം"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"മായ്ക്കുക"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"എല്ലാം മായ്ക്കുക"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"ഒന്നിലധികം വിൻഡോകളെ ഈ ആപ്പ് പിന്തുണയ്ക്കുന്നില്ല"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"ഒന്നിലധികം വിൻഡോകളെ ആപ്പ് പിന്തുണയ്ക്കുന്നില്ല"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"തിരശ്ചീനമായി വേർതിരിക്കുക"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"അറിയിപ്പ് ലിസ്റ്റിന്റെ ഏറ്റവും മുകളിൽ കാണിക്കുക, ശബ്ദമുണ്ടാക്കുക"</string>
<string name="notification_more_settings" msgid="816306283396553571">"കൂടുതൽ ക്രമീകരണം"</string>
<string name="notification_done" msgid="5279426047273930175">"പൂർത്തിയായി"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> അറിയിപ്പ് നിയന്ത്രണങ്ങൾ"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"വർണ്ണവും രൂപഭാവവും"</string>
<string name="night_mode" msgid="3540405868248625488">"നൈറ്റ് മോഡ്"</string>
<string name="calibrate_display" msgid="5974642573432039217">"ഡിസ്പ്ലേ കാലിബ്രേറ്റുചെയ്യുക"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"ചാർജുചെയ്യുന്ന സമയത്ത് ബാറ്ററി സേവർ ലഭ്യമല്ല"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"ബാറ്ററി സേവർ"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"പ്രവർത്തനവും പശ്ചാത്തല ഡാറ്റയും കുറയ്ക്കുന്നു"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"ബട്ടൺ <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"ഹോം"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"ബാക്ക്"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"മുകളിലേക്ക്"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"താഴേക്ക്"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"ഇടത്"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"വലത്"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"മധ്യം"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"ടാബ്"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"സ്പെയ്സ്"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"എന്റർ"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"ബാക്ക്സ്പെയ്സ്"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"പ്ലേ ചെയ്യുക/താൽക്കാലികമായി നിർത്തുക"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"നിർത്തുക"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"അടുത്തത്"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"മുമ്പത്തേത്"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"റിവൈൻഡ്"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"ഫാസ്റ്റ് ഫോർവേഡ്"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"പേജ് അപ്പ്"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"പേജ് ഡൗൺ"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"ഡിലീറ്റ്"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"ഹോം"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"എൻഡ്"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"ഇൻസേർട്ട്"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"നം ലോക്ക്"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"നംപാഡ് <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"സിസ്റ്റം"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"വീട്"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"പുതിയവ"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"മടങ്ങുക"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"അറിയിപ്പുകൾ"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"കീബോർഡ് കുറുക്കുവഴികൾ"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"ടൈപ്പിംഗ് രീതി മാറുക"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"അപ്ലിക്കേഷനുകൾ"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"അസിസ്റ്റ്"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"ബ്രൗസർ"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"കോൺടാക്റ്റുകൾ"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"ഇമെയിൽ"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"സംഗീതം"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"കലണ്ടർ"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"വോളിയം നിയന്ത്രണങ്ങളോടൊപ്പം കാണിക്കുക"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"ശല്യപ്പെടുത്തരുത്"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"വോളിയം ബട്ടൺ കുറുക്കുവഴി"</string>
@@ -540,8 +577,7 @@
<string name="select_keycode" msgid="7413765103381924584">"കീബോർഡ് ബട്ടൺ തിരഞ്ഞെടുക്കൂ"</string>
<string name="preview" msgid="9077832302472282938">"പ്രിവ്യു നടത്തുക"</string>
<string name="drag_to_add_tiles" msgid="7058945779098711293">"ടൈലുകൾ ചേർക്കുന്നതിന് വലിച്ചിടുക"</string>
- <!-- no translation found for drag_to_remove_tiles (3361212377437088062) -->
- <skip />
+ <string name="drag_to_remove_tiles" msgid="3361212377437088062">"നീക്കംചെയ്യുന്നതിന് ഇവിടെ വലിച്ചിടുക"</string>
<string name="qs_edit" msgid="2232596095725105230">"എഡിറ്റുചെയ്യുക"</string>
<string name="tuner_time" msgid="6572217313285536011">"സമയം"</string>
<string-array name="clock_options">
@@ -560,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"മുകളിലേക്ക് നീക്കുക"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"ഇടത്തേക്ക് നീക്കുക"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"വലത്തേക്ക് നീക്കുക"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"മൾട്ടി-വിൻഡോയിൽ ആപ്പ് പ്രവർത്തിച്ചേക്കില്ല"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"സ്ഥാനം <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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. ചേർക്കാൻ രണ്ടുതവണ ടാപ്പുചെയ്യുക."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"സ്ഥാനം <xliff:g id="POSITION">%1$d</xliff:g>. തിരഞ്ഞെടുക്കാൻ രണ്ടുതവണ ടാപ്പുചെയ്യുക."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"<xliff:g id="TILE_NAME">%1$s</xliff:g> നീക്കുക"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"<xliff:g id="TILE_NAME">%1$s</xliff:g> നീക്കംചെയ്യുക"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"സ്ഥാനം <xliff:g id="POSITION">%2$d</xliff:g>-ലേക്ക് <xliff:g id="TILE_NAME">%1$s</xliff:g> ചേർക്കുന്നു"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> നീക്കംചെയ്യുന്നു"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"സ്ഥാനം <xliff:g id="POSITION">%2$d</xliff:g>-ലേക്ക് <xliff:g id="TILE_NAME">%1$s</xliff:g> നീക്കി"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"ദ്രുത ക്രമീകരണ എഡിറ്റർ."</string>
</resources>
diff --git a/packages/SystemUI/res/values-ml-rIN/strings_tv.xml b/packages/SystemUI/res/values-ml-rIN/strings_tv.xml
index c054eed..09fe4ce 100644
--- a/packages/SystemUI/res/values-ml-rIN/strings_tv.xml
+++ b/packages/SystemUI/res/values-ml-rIN/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"PIP നിയന്ത്രിക്കാൻ "<b>"ഹോം"</b>" പിടിക്കുക"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"PIP നിയന്ത്രിക്കാൻ ഹോം ബട്ടൺ അമർത്തിപ്പിടിക്കുക"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"മനസ്സിലായി"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"ഡിസ്മിസ് ചെയ്യുക"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mn-rMN/strings.xml b/packages/SystemUI/res/values-mn-rMN/strings.xml
index b9ddad7..80cb8f2 100644
--- a/packages/SystemUI/res/values-mn-rMN/strings.xml
+++ b/packages/SystemUI/res/values-mn-rMN/strings.xml
@@ -166,6 +166,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g>-г хаах."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> байхгүй."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Хамгийн сүүлийн бүх програмыг арилгасан байна."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g>-г эхлүүлж байна."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Мэдэгдэл хаагдсан."</string>
@@ -234,8 +236,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"GPS байршил"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Байршлын хүсэлтүүд идэвхтэй"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Бүх мэдэгдлийг цэвэрлэх."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Мэдэгдлийн тохиргоо"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> тохиргоо"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Дэлгэц автоматаар эргэнэ."</string>
@@ -308,8 +309,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"хайх"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g>-г эхлүүлж чадсангүй."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g>-г аюулгүй горимд идэвхгүй болгосон."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Түүх"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Устгах"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Бүгдийг арилгах"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Энэ апп олон цонхыг дэмждэггүй"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Апп олон цонхыг дэмждэггүй"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Хэвтээ чиглэлд хуваах"</string>
@@ -478,8 +478,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Мэдэгдлийг жагсаалтын эхэнд яаралтай дуутай харуулах"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Бусад тохиргоо"</string>
<string name="notification_done" msgid="5279426047273930175">"Дууссан"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> мэдэгдлийн хяналт"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Өнгө, харагдах байдал"</string>
<string name="night_mode" msgid="3540405868248625488">"Шөнийн горим"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Дэлгэцийг тохируулах"</string>
@@ -499,10 +498,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Цэнэглэх үед тэжээл хэмнэгч ажиллахгүй"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Тэжээл хэмнэгч"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Гүйцэтгэл болон дэвсгэрийн датаг багасгадаг"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"<xliff:g id="NAME">%1$s</xliff:g> товчлуур"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Нүүр хуудас"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Буцах"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Дээш"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Доош"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Зүүн"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Баруун"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Гол хэсэг"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Чихтэй хуудас"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Зай"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Оруулах"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Арилгах"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Тоглуулах/Түр зогсоох"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Зогсоох"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Дараах"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Өмнөх"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Буцааж хураах"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Хурдан урагшлуулах"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Хуудас дээш"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Хуудас доош"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Устгах"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Нүүр хуудас"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"Төгсгөл"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Оруулах"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Тоо бичих горим"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Тоо бичих товчлуур <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Систем"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Нүүр хуудас"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Саяхны"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Буцах"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Мэдэгдэл"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Гарын товчлол"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Оролтын аргыг солих"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Апп"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Дэмжлэг"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Хөтөч"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Харилцагчид"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Имэйл"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Хөгжим"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Хуанли"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Түвшний хяналттай харуулах"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Бүү саад бол"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Түвшний товчлуурын товчлол"</string>
@@ -557,4 +594,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Дээш зөөх"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Зүүн тийш зөөх"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Баруун тийш зөөх"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Олон цонхтой үед апп ажиллахгүй байж болзошгүй"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"Байршил <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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Нэмэхийн тулд 2 удаа дарна уу."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Албан тушаал <xliff:g id="POSITION">%1$d</xliff:g>. Сонгохын тулд 2 удаа дарна уу."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"<xliff:g id="TILE_NAME">%1$s</xliff:g>-г зөөх"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"<xliff:g id="TILE_NAME">%1$s</xliff:g>-г устгах"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g>-г <xliff:g id="POSITION">%2$d</xliff:g> байршилд нэмсэн"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g>-г устгасан"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g>-г <xliff:g id="POSITION">%2$d</xliff:g> байршилд зөөсөн"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Түргэн тохиргоо засварлагч."</string>
</resources>
diff --git a/packages/SystemUI/res/values-mn-rMN/strings_tv.xml b/packages/SystemUI/res/values-mn-rMN/strings_tv.xml
index 54f2282..ca522d3 100644
--- a/packages/SystemUI/res/values-mn-rMN/strings_tv.xml
+++ b/packages/SystemUI/res/values-mn-rMN/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"PIP-г удирдахын тулд "<b>"HOME"</b>" товчлуурыг дарна уу"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"PIP-г удирдахын тулд НҮҮР ХУУДАС товчлуурыг дараад хүлээнэ үү"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"Ойлголоо"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Хаах"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mr-rIN/strings.xml b/packages/SystemUI/res/values-mr-rIN/strings.xml
index e65897c..76dfcd5 100644
--- a/packages/SystemUI/res/values-mr-rIN/strings.xml
+++ b/packages/SystemUI/res/values-mr-rIN/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> डिसमिस करा."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> डिसमिस केला."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"अलीकडील सर्व अनुप्रयोग डिसमिस झाले."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> प्रारंभ करीत आहे."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"सूचना डिसमिस केल्या."</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"GPS द्वारे स्थान सेट केले"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"स्थान विनंत्या सक्रिय"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"सर्व सूचना साफ करा."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"सूचना सेटिंग्ज"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> सेटिंग्ज"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"स्क्रीन स्वयंचलितपणे फिरेल."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"शोधा"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> प्रारंभ करणे शक्य झाले नाही."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> सुरक्षित-मोडमध्ये अक्षम केला आहे."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"इतिहास"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"साफ करा"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"सर्व साफ करा"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"हा अॅप एकाधिक-विंडोला समर्थन देत नाही"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"अॅप एकाधिक-विंडोला समर्थन देत नाही"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"क्षैतिज विभाजित करा"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"सूचनांच्या शीर्षस्थानी दर्शवा, स्क्रीनवर पहा आणि ध्वनीस अनुमती द्या"</string>
<string name="notification_more_settings" msgid="816306283396553571">"अधिक सेटिंग्ज"</string>
<string name="notification_done" msgid="5279426047273930175">"पूर्ण झाले"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> सूचना नियंत्रणे"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"रंग आणि स्वरूप"</string>
<string name="night_mode" msgid="3540405868248625488">"रात्र मोड"</string>
<string name="calibrate_display" msgid="5974642573432039217">"प्रदर्शनाचे मापन करा"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"चार्ज करताना बॅटरी बचतकर्ता उपलब्ध नाही"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"बॅटरी बचतकर्ता"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"कार्यप्रदर्शन आणि पार्श्वभूमी डेटा कमी करते"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"बटण <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"परत"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"वर"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"खाली"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"डावा"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"उजवा"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"मध्यवर्ती"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"टॅब"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Space"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"प्ले करा/विराम द्या"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"थांबा"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"पुढील"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"मागील"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"मागे न्या"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"पुढे करा"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"हटवा"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"घाला"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Numpad <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"सिस्टीम"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"मुख्यपृष्ठ"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"अलीकडील"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"परत"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"सूचना"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"कीबोर्ड शॉर्टकट"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"इनपुट पद्धत स्विच करा"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"अनुप्रयोग"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"सहाय्य"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"ब्राउझर"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"संपर्क"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"ईमेल"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"संगीत"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"कॅलेंडर"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"आवाज नियंत्रणांसह दर्शवा"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"व्यत्यय आणू नका"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"आवाजाच्या बटणांचा शार्टकट"</string>
@@ -540,8 +577,7 @@
<string name="select_keycode" msgid="7413765103381924584">"कीबोर्ड बटण निवडा"</string>
<string name="preview" msgid="9077832302472282938">"पूर्वावलोकन"</string>
<string name="drag_to_add_tiles" msgid="7058945779098711293">"टाइल जोडण्यासाठी ड्रॅग करा"</string>
- <!-- no translation found for drag_to_remove_tiles (3361212377437088062) -->
- <skip />
+ <string name="drag_to_remove_tiles" msgid="3361212377437088062">"काढण्यासाठी येथे ड्रॅग करा"</string>
<string name="qs_edit" msgid="2232596095725105230">"संपादित करा"</string>
<string name="tuner_time" msgid="6572217313285536011">"वेळ"</string>
<string-array name="clock_options">
@@ -560,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"वर हलवा"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"डावीकडे हलवा"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"उजवीकडे हलवा"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"एकाधिक-विंडो सह अॅप कार्य करू शकत नाही"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"स्थिती <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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g> . जोडण्यासाठी दोनदा टॅप करा."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"स्थिती <xliff:g id="POSITION">%1$d</xliff:g>. निवडण्यासाठी दोनदा टॅप करा."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"<xliff:g id="TILE_NAME">%1$s</xliff:g> हलवा"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"<xliff:g id="TILE_NAME">%1$s</xliff:g> काढा"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ला <xliff:g id="POSITION">%2$d</xliff:g> स्थितीवर जोडले आहे"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ला काढले आहे"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ला <xliff:g id="POSITION">%2$d</xliff:g> स्थितीवर हलविले"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"द्रुत सेटिंग्ज संपादक."</string>
</resources>
diff --git a/packages/SystemUI/res/values-mr-rIN/strings_tv.xml b/packages/SystemUI/res/values-mr-rIN/strings_tv.xml
index 6a29867..318e3e9 100644
--- a/packages/SystemUI/res/values-mr-rIN/strings_tv.xml
+++ b/packages/SystemUI/res/values-mr-rIN/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"PIP नियंत्रित करण्यासाठी "<b>"मुख्यपृष्ठ"</b>" धरून ठेवा"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"PIP नियंत्रित करण्यासाठी मुख्यपृष्ठ बटण दाबा आणि धरून ठेवा"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"समजले"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"डिसमिस करा"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml
index 432110c..9401198 100644
--- a/packages/SystemUI/res/values-ms-rMY/strings.xml
+++ b/packages/SystemUI/res/values-ms-rMY/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Ketepikan <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> ditolak."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Semua aplikasi terbaharu diketepikan."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Memulakan <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Pemberitahuan diketepikan."</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Lokasi ditetapkan oleh GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Permintaan lokasi aktif"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Padamkan semua pemberitahuan."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Tetapan pemberitahuan"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> tetapan"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Skrin akan berputar secara automatik."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"cari"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Tidak dapat memulakan <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> dilumpuhkan dalam mod selamat."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Sejarah"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Kosongkan"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Kosongkan semua"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Apl ini tidak menyokong berbilang tetingkap"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Apl tidak menyokong berbilang tetingkap"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Mendatar Terpisah"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Tunjukkan pada bahagian atas senarai pemberitahuan, intai pada skrin dan benarkan bunyi"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Lagi tetapan"</string>
<string name="notification_done" msgid="5279426047273930175">"Selesai"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"Kawalan pemberitahuan <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Warna dan penampilan"</string>
<string name="night_mode" msgid="3540405868248625488">"Mod malam"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Tentukur paparan"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Penjimat Bateri tidak tersedia semasa mengecas"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Penjimat Bateri"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Mengurangkan prestasi dan data latar belakang"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Butang <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Skrin Utama"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Kembali"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Ke atas"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Ke bawah"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Ke kiri"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Ke kanan"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Tengah"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Space"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Undur ruang"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Main/Jeda"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Berhenti"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Seterusnya"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Sebelumnya"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Mandir"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Mara Laju"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Pad nombor <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Sistem"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Skrin Utama"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Terbaharu"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Kembali"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Pemberitahuan"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Pintasan Papan Kekunci"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Tukar kaedah input"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Aplikasi"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Bantu"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Penyemak imbas"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kenalan"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-mel"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Muzik"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalendar"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Tunjukkan dengan kawalan kelantangan"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Jangan ganggu"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Pintasan butang kelantangan"</string>
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Alih ke atas"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Alih ke kiri"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Alih ke kanan"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Apl mungkin tidak berfungsi dengan berbilang tetingkap"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Dwiketik untuk menambah."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Kedudukan <xliff:g id="POSITION">%1$d</xliff:g>. Dwiketik untuk memilih."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Alihkan <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Alih keluar <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ditambahkan pada kedudukan <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> dialih keluar"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> dialihkan ke kedudukan <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Editor tetapan pantas."</string>
</resources>
diff --git a/packages/SystemUI/res/values-ms-rMY/strings_tv.xml b/packages/SystemUI/res/values-ms-rMY/strings_tv.xml
index d358cc0..eb5af9e3 100644
--- a/packages/SystemUI/res/values-ms-rMY/strings_tv.xml
+++ b/packages/SystemUI/res/values-ms-rMY/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"Thn "<b>"SKRN UTMA"</b>" utk kwl PIP"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Tekan dan tahan butang SKRIN UTAMA untuk mengawal PIP"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"OK"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Ketepikan"</string>
</resources>
diff --git a/packages/SystemUI/res/values-my-rMM/strings.xml b/packages/SystemUI/res/values-my-rMM/strings.xml
index 8994499..6964750 100644
--- a/packages/SystemUI/res/values-my-rMM/strings.xml
+++ b/packages/SystemUI/res/values-my-rMM/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g>ကို ပယ်လိုက်ရန်"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> ထုတ်ထားသည်။"</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"မကြာသေးမီက အပလီကေးရှင်းများအားလုံး ဖယ်ထုတ်ပြီးပါပြီ။"</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g>ကို စတင်နေသည်။"</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g><xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"အကြောင်းကြားချက်ကိုဖယ်ရှားပြီး"</string>
@@ -309,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"ရှာဖွေရန်"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> ကို မစနိုင်ပါ။"</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> ကို ဘေးကင်းလုံခြုံသည့်မုဒ်တွင် ပိတ်ထားပါသည်။"</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"မှတ်တမ်း"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"ရှင်းလင်းပါ"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"အားလုံး ရှင်းလင်းပါ"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"ဤအက်ပ်သည် ဝင်းဒိုးများစွာဖွင့်ခြင်းကို ပံ့ပိုးမထားပါ"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"အက်ပ်သည် ဝင်းဒိုးများစွာဖွင့်ခြင်းကို ပံ့ပိုးမထားပါ"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"ရေပြင်ညီ ပိုင်းမည်"</string>
@@ -499,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"အားသွင်းနေချိန်မှာ Battery Saver ကို သုံးမရပါ"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Battery Saver"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"လုပ်ဆောင်မှု နှင့် နောက်ခံ ဒေတာကို လျော့နည်းစေပါသည်"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"ခလုတ် <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"ပင်မ"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"နောက်သို့"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"အပေါ်"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"အောက်"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"ဘယ်"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"ညာ"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"ဌာန"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"တဘ်"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"နေရာခြားပါ"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter ခလုတ်"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"နောက်ပြန်ဖျက်ပါ"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"ဖွင့်ပါ/ခဏရပ်ပါ"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"ရပ်ပါ"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"ရှေ့သို့"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"ယခင်"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"နောက်သို့ရစ်ပါ"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"ရှေ့သို့ရစ်ပါ"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"အပေါ်စာမျက်နှာသို့သွားပါ"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"အောက်စာမျက်နှာသို့သွားပါ"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"ဖျက်ပါ"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"ပင်မ"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"ပြီးပါပြီ"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"ထည့်ပါ"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"ဂဏန်းကွက်<xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"စနစ်"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"ပင်မ"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"မကြာသေးခင်က"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"နောက်သို့"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"အကြောင်းကြားချက်များ"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"ကီးဘုတ် ဖြတ်လမ်းများ"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"ထည့်သွင်းမှုနည်းလမ်းကို ပြောင်းလဲပါ"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"အက်ပ်များ"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"အထောက်အကူ"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"ဘရောင်ဇာ"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"အဆက်အသွယ်များ"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"အီးမေးလ်"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"အမြန်စာတိုစနစ်"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Music"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"ပြက္ခဒိန်"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"အသံထိန်းချုပ်သည့်ခလုတ်များဖြင့် ပြပါ"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"မနှောက်ယှက်ပါနှင့်"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"အသံထိန်းချုပ်သည့်ခလုတ် ဖြတ်လမ်း"</string>
@@ -557,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"အပေါ်သို့ရွှေ့ပါ"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"ဘယ်ဘက်သို့ရွှေ့ပါ"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"ညာဘက်သို့ရွှေ့ပါ"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"အက်ပ်သည် ဝင်းဒိုးများတပြိုင်နက်ဖွင့်ခြင်းကို မပံ့ပိုးပါ"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"<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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>။ ပေါင်းထည့်ရန် နှစ်ချက်တို့ပါ။"</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"<xliff:g id="POSITION">%1$d</xliff:g> နေရာ။ ရွေးချယ်ရန် နှစ်ချက်တို့ပါ။"</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ကိုရွှေ့ပါ"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ကိုဖယ်ရှားပါ"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ကို <xliff:g id="POSITION">%2$d</xliff:g> နေရာသို့ ပေါင်းထည့်ထားပါသည်"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ကိုဖယ်ရှားလိုက်ပါပြီ"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ကို <xliff:g id="POSITION">%2$d</xliff:g> နေရာသို့ ရွှေ့လိုက်ပါပြီ"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"မြန်ဆန်သည့် ဆက်တင်တည်းဖြတ်စနစ်"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 9ee29ab..eed5f36 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Avvis <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> avvist."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Alle nylig brukte apper er avvist."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Starter <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Varselet ble skjult."</string>
@@ -309,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"Søk"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Kunne ikke starte <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> er slått av i sikker modus."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Logg"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Tøm"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Tøm alt"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Denne appen har ikke støtte for flervindusmodus"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Appen har ikke støtte for flervindusmodus"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Del horisontalt"</string>
@@ -401,7 +402,7 @@
<string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Enheten forblir låst til du låser den opp manuelt"</string>
<string name="hidden_notifications_title" msgid="7139628534207443290">"Motta varsler raskere"</string>
<string name="hidden_notifications_text" msgid="2326409389088668981">"Se dem før du låser opp"</string>
- <string name="hidden_notifications_cancel" msgid="3690709735122344913">"Nei, takk"</string>
+ <string name="hidden_notifications_cancel" msgid="3690709735122344913">"Nei takk"</string>
<string name="hidden_notifications_setup" msgid="41079514801976810">"Konfigurer"</string>
<string name="zen_mode_and_condition" msgid="4462471036429759903">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="volume_zen_end_now" msgid="3179845345429841822">"Avslutt nå"</string>
@@ -410,7 +411,7 @@
<string name="screen_pinning_title" msgid="3273740381976175811">"Skjermen er låst"</string>
<string name="screen_pinning_description" msgid="3577937698406151604">"På denne måten blir skjermen synlig frem til du låser den opp. Trykk og hold inne Tilbake for å låse opp."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Skjønner"</string>
- <string name="screen_pinning_negative" msgid="3741602308343880268">"Nei, takk"</string>
+ <string name="screen_pinning_negative" msgid="3741602308343880268">"Nei takk"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Vil du skjule <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Den vises igjen neste gang du slår den på i innstillingene."</string>
<string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Skjul"</string>
@@ -499,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Batterisparing er ikke tilgjengelig under lading"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Batterisparing"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduserer ytelsen og begrenser bakgrunnsdataene"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"<xliff:g id="NAME">%1$s</xliff:g>-knappen"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Startskjerm"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Tilbake"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Opp"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Ned"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Venstre"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Høyre"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Midttasten"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Mellomrom"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Tilbaketasten"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Spill av / sett på pause"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Stopp"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Neste"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Forrige"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Spol tilbake"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Spol fremover"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Startskjerm"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"<xliff:g id="NAME">%1$s</xliff:g> på talltastaturet"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"System"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Startside"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Nylige"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Tilbake"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Varsler"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Hurtigtaster"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Bytt inndatametode"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Apper"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Assist"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Nettleser"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontakter"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-post"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Chat"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Musikk"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalender"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Vis med volumkontrollene"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Ikke forstyrr"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Hurtigtast for volumknappene"</string>
@@ -557,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Flytt opp"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Flytt mot venstre"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Flytt mot høyre"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Appen fungerer kanskje ikke i Flervindusmodus"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Dobbelttrykk for å legge til."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Plassering <xliff:g id="POSITION">%1$d</xliff:g>. Dobbelttrykk for å velge."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Flytt <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Fjern <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> er lagt til i plassering <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> er fjernet"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> er flyttet til plassering <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Redigeringsvindu for hurtiginnstillinger."</string>
</resources>
diff --git a/packages/SystemUI/res/values-ne-rNP/strings.xml b/packages/SystemUI/res/values-ne-rNP/strings.xml
index 5b139d1..836dc4b 100644
--- a/packages/SystemUI/res/values-ne-rNP/strings.xml
+++ b/packages/SystemUI/res/values-ne-rNP/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> खारेज गर्नुहोस्।"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> खारेज गरिएको छ।"</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"सबै हालका अनुप्रयोगहरू खारेज गरियो।"</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g>सुरु गर्दै।"</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"सूचना खारेज।"</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"GPS द्वारा स्थान सेट गरिएको"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"स्थान अनुरोधहरू सक्रिय"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"सबै सूचनाहरू हटाउनुहोस्।"</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"अधिसूचना सेटिङ्हरू"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> सेटिङ्हरू"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"स्क्रिन स्वतः घुम्ने छ।"</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"खोजी गर्नुहोस्"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"सुरु गर्न सकिएन <xliff:g id="APP">%s</xliff:g>।"</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> लाई सुरक्षित-मोडमा असक्षम गरिएको छ।"</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"इतिहास"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"मेटाउनुहोस्"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"सबै हटाउनुहोस्"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"यस अनुप्रयोगले बहु-विन्डोलाई समर्थन गर्दैन"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"अनुप्रयोगले बहु-विन्डोलाई समर्थन गर्दैन"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"तेर्सो रूपमा विभाजन गर्नुहोस्"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"सूचना सूचीको शीर्षमा देखाउने, स्क्रिनमा चियाउने र ध्वनि निकाल्न अनुमति दिने"</string>
<string name="notification_more_settings" msgid="816306283396553571">"थप सेटिङहरू"</string>
<string name="notification_done" msgid="5279426047273930175">"सम्पन्न भयो"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> का सूचनाका लागि नियन्त्रणहरू"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"रंग र रूप"</string>
<string name="night_mode" msgid="3540405868248625488">"रात्री मोड"</string>
<string name="calibrate_display" msgid="5974642573432039217">"प्रदर्शनको स्तर मिलाउनुहोस्"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"चार्ज गर्ने समयमा ब्याट्री सेभर उपलब्ध छैन"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"ब्याट्री सेभर"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"कार्यसम्पादन र पृष्ठभूमि डेटा घटाउँछ"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"<xliff:g id="NAME">%1$s</xliff:g> बटन"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"पछाडि"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"माथि"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"तल"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"बायाँ"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"दायाँ"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"केन्द्र"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"स्पेस"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"ब्याकस्पेस"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"प्ले गर्नुहोस्/पज गर्नुहोस्"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"रोक्नुहोस्"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"अर्को"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"अघिल्लो"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"रिवाइन्ड गर्नुहोस्"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"फास्ट फर्वार्ड"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"नमप्याड <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"प्रणाली"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"गृह"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"हालैका"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"पछाडि"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"सूचनाहरू"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"किबोर्ड सर्टकटहरू"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"इनपुट विधिलाई स्विच गर्नुहोस्"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"अनुप्रयोगहरू"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"सहायता"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"ब्राउजर"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"सम्पर्कहरू"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"इमेल"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"संगीत"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"पात्रो"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"भोल्युम नियन्त्रणसहित देखाउनुहोस्"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"बाधा नपुर्याउनुहोस्"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"भोल्युम बटनका सर्टकट"</string>
@@ -540,8 +577,7 @@
<string name="select_keycode" msgid="7413765103381924584">"किबोर्ड बटन चयन गर्नुहोस्"</string>
<string name="preview" msgid="9077832302472282938">"पूर्वावलोकन"</string>
<string name="drag_to_add_tiles" msgid="7058945779098711293">"टाइलहरू थप्न तान्नुहोस्"</string>
- <!-- no translation found for drag_to_remove_tiles (3361212377437088062) -->
- <skip />
+ <string name="drag_to_remove_tiles" msgid="3361212377437088062">"हटाउनका लागि यहाँ तान्नुहोस्"</string>
<string name="qs_edit" msgid="2232596095725105230">"सम्पादन गर्नुहोस्"</string>
<string name="tuner_time" msgid="6572217313285536011">"समय"</string>
<string-array name="clock_options">
@@ -560,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"माथि सार्नुहोस्"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"बाँया सार्नुहोस्"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"दायाँ सार्नुहोस्"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"अनुप्रयोग बहु-विन्डोमा काम नगर्न सक्छ"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"स्थिति <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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>। थप्नका लागि डबल ट्याप गर्नुहोस्।"</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"स्थिति <xliff:g id="POSITION">%1$d</xliff:g>। चयन गर्नका लागि डबल ट्याप गर्नुहोस्।"</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"<xliff:g id="TILE_NAME">%1$s</xliff:g> लाई सार्नुहोस्"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"<xliff:g id="TILE_NAME">%1$s</xliff:g> लाई हटाउनुहोस्"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> लाई स्थिति <xliff:g id="POSITION">%2$d</xliff:g> मा थपियो"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> लाई हटाइयो"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> लाई स्थिति <xliff:g id="POSITION">%2$d</xliff:g> मा सारियो"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"द्रुत सेटिङ सम्पादक।"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ne-rNP/strings_tv.xml b/packages/SystemUI/res/values-ne-rNP/strings_tv.xml
index 7234d3b..d9245d2 100644
--- a/packages/SystemUI/res/values-ne-rNP/strings_tv.xml
+++ b/packages/SystemUI/res/values-ne-rNP/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"PIP लाई नियन्त्रण गर्न "<b>"गृह"</b>" कुञ्जीलाई थिचिरहनुहोस्"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"IP लाई नियन्त्रण गर्न गृह बटनलाई थिची होल्ड गर्नुहोस्"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"बुझेँ"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"खारेज गर्नुहोस्"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index b49cf66..41075e39 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> sluiten."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> verwijderd."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Alle recente apps gesloten."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> starten."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Melding verwijderd."</string>
@@ -309,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"zoeken"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Kan <xliff:g id="APP">%s</xliff:g> niet starten."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> is uitgeschakeld in de veilige modus"</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Geschiedenis"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Wissen"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Alles wissen"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Deze app ondersteunt de modus voor meerdere vensters niet"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"App ondersteunt meerdere vensters niet"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Horizontaal splitsen"</string>
@@ -499,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Accubesparing niet beschikbaar tijdens opladen"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Accubesparing"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Vermindert de prestaties en achtergrondgegevens"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Knop <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Terug"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Omhoog"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Omlaag"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Links"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Rechts"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Midden"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Spatiebalk"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Afspelen/Onderbreken"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Stoppen"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Volgende"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Vorige"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Terugspoelen"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Vooruitspoelen"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"<xliff:g id="NAME">%1$s</xliff:g> op numeriek toetsenblok"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Systeem"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Startpagina"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Recent"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Terug"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Meldingen"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Sneltoetsen"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Invoermethode schakelen"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Apps"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Assistentie"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Browser"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Contacten"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-mail"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Chat"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Muziek"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Agenda"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Weergeven met volumeknoppen"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Niet storen"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Volumeknoppen als sneltoets"</string>
@@ -557,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Omhoog"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Naar links"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Naar rechts"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"De app werkt mogelijk niet met meerdere vensters"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Dubbeltik om toe te voegen."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Positie <xliff:g id="POSITION">%1$d</xliff:g>. Dubbeltik om te selecteren."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"<xliff:g id="TILE_NAME">%1$s</xliff:g> verplaatsen"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"<xliff:g id="TILE_NAME">%1$s</xliff:g> verwijderen"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> is toegevoegd op positie <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> is verwijderd"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> is verplaatst naar positie <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Editor voor \'Snelle instellingen\'."</string>
</resources>
diff --git a/packages/SystemUI/res/values-pa-rIN/strings.xml b/packages/SystemUI/res/values-pa-rIN/strings.xml
index f67c51a..1703a2f 100644
--- a/packages/SystemUI/res/values-pa-rIN/strings.xml
+++ b/packages/SystemUI/res/values-pa-rIN/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> ਨੂੰ ਰੱਦ ਕਰੋ।"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> ਰੱਦ ਕੀਤਾ।"</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"ਸਾਰੀਆਂ ਹਾਲੀਆ ਐਪਲੀਕੇਸ਼ਨਾਂ ਰੱਦ ਕੀਤੀਆਂ।"</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> ਚਾਲੂ ਕਰ ਰਿਹਾ ਹੈ।"</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"ਸੂਚਨਾ ਰੱਦ ਕੀਤੀ।"</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"GPS ਵੱਲੋਂ ਸੈਟ ਕੀਤਾ ਨਿਰਧਾਰਿਤ ਸਥਾਨ"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਸੇਵਾ ਬੇਨਤੀਆਂ ਸਕਿਰਿਆ"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"ਸਾਰੀਆਂ ਸੂਚਨਾਵਾਂ ਹਟਾਓ।"</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"ਸੂਚਨਾ ਸੈਟਿੰਗਾਂ"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> ਸੈਟਿੰਗਾਂ"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"ਸਕ੍ਰੀਨ ਆਟੋਮੈਟਿਕਲੀ ਰੋਟੇਟ ਕਰੇਗੀ।"</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"ਖੋਜੋ"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> ਨੂੰ ਚਾਲੂ ਨਹੀਂ ਕਰ ਸਕਿਆ।"</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> ਨੂੰ ਸੁਰੱਖਿਅਤ-ਮੋਡ ਵਿੱਚ ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ ਹੈ।"</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"ਇਤਿਹਾਸ"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"ਸਾਫ਼ ਕਰੋ"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"ਸਭ ਸਾਫ਼ ਕਰੋ"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"ਇਹ ਐਪ ਮਲਟੀ-ਵਿੰਡੋ ਨੂੰ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦੀ"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"ਐਪ ਮਲਟੀ-ਵਿੰਡੋ ਨੂੰ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦੀ"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"ਹੌਰੀਜ਼ੌਂਟਲ ਸਪਲਿਟ"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"ਸੂਚਨਾਵਾਂ ਸੂਚੀ ਦੇ ਸਿਖਰ \'ਤੇ ਵਿਖਾਓ, ਸਕ੍ਰੀਨ \'ਤੇ ਝਲਕ ਵਿਖਾਉਣ ਅਤੇ ਧੁਨੀ ਦੀ ਮਨਜ਼ੂਰੀ ਦਿਓ"</string>
<string name="notification_more_settings" msgid="816306283396553571">"ਹੋਰ ਸੈਟਿੰਗਾਂ"</string>
<string name="notification_done" msgid="5279426047273930175">"ਹੋ ਗਿਆ"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਸੂਚਨਾ ਕੰਟਰੋਲ"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"ਰੰਗ ਅਤੇ ਵਿਖਾਲਾ"</string>
<string name="night_mode" msgid="3540405868248625488">"ਰਾਤ ਮੋਡ"</string>
<string name="calibrate_display" msgid="5974642573432039217">"ਡਿਸਪਲੇ ਨੂੰ ਕੈਲੀਬ੍ਰੇਟ ਕਰੋ"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"ਬੈਟਰੀ ਸੇਵਰ ਚਾਰਜਿੰਗ ਦੌਰਾਨ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"ਬੈਟਰੀ ਸੇਵਰ"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"ਕਾਰਗੁਜ਼ਾਰੀ ਅਤੇ ਬੈਕਗ੍ਰਾਊਂਡ ਡੈਟੇ ਨੂੰ ਘਟਾਉਂਦਾ ਹੈ"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"ਬਟਨ <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Back"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Up"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Down"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Left"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Right"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Center"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Space"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Play/Pause"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Stop"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Next"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Previous"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Rewind"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Fast Forward"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Numpad <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"ਸਿਸਟਮ"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"ਮੁੱਖ ਸਕ੍ਰੀਨ"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"ਹਾਲੀਆ"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"ਪਿੱਛੇ"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"ਸੂਚਨਾਵਾਂ"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"ਕੀ-ਬੋਰਡ ਸ਼ਾਰਟਕੱਟ"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"ਵਾਪਸ ਇਨਪੁੱਟ ਵਿਧੀ \'ਤੇ ਬਦਲੋ"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"ਐਪਲੀਕੇਸ਼ਨਾਂ"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"ਸਹਾਇਕ"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"ਬ੍ਰਾਊਜ਼ਰ"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"ਸੰਪਰਕ"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"ਈਮੇਲ"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"ਸੰਗੀਤ"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"ਕੈਲੰਡਰ"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"ਵੌਲਯੂਮ ਕੰਟਰੋਲਾਂ ਨਾਲ ਵਿਖਾਓ"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"ਮੈਨੂੰ ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"ਵੌਲਯੂਮ ਬਟਨ ਸ਼ਾਰਟਕੱਟ"</string>
@@ -540,8 +577,7 @@
<string name="select_keycode" msgid="7413765103381924584">"ਕੀ-ਬੋਰਡ ਬਟਨ ਚੁਣੋ"</string>
<string name="preview" msgid="9077832302472282938">"ਝਲਕ"</string>
<string name="drag_to_add_tiles" msgid="7058945779098711293">"ਟਾਇਲਾਂ ਨੂੰ ਸ਼ਾਮਲ ਕਰਨ ਲਈ ਘਸੀਟੋ"</string>
- <!-- no translation found for drag_to_remove_tiles (3361212377437088062) -->
- <skip />
+ <string name="drag_to_remove_tiles" msgid="3361212377437088062">"ਹਟਾਉਣ ਲਈ ਇੱਥੇ ਘਸੀਟੋ"</string>
<string name="qs_edit" msgid="2232596095725105230">"ਸੰਪਾਦਨ ਕਰੋ"</string>
<string name="tuner_time" msgid="6572217313285536011">"ਸਮਾਂ"</string>
<string-array name="clock_options">
@@ -560,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"ਉੱਪਰ ਲੈ ਜਾਓ"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"ਖੱਬੇ ਲੈ ਜਾਓ"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"ਸੱਜੇ ਲੈ ਜਾਓ"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਐਪ ਮਲਟੀ-ਵਿੰਡੋ ਨਾਲ ਕੰਮ ਨਾ ਕਰੇ"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"ਸਥਿਤੀ <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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>। ਸ਼ਾਮਲ ਕਰਨ ਲਈ ਦੋ ਵਾਰ ਟੈਪ ਕਰੋ।"</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"ਸਥਿਤੀ <xliff:g id="POSITION">%1$d</xliff:g>। ਚੁਣਨ ਲਈ ਦੋ ਵਾਰ ਟੈਪ ਕਰੋ।"</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ਨੂੰ ਤਬਦੀਲ ਕਰੋ"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ਹਟਾਓ"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ਨੂੰ <xliff:g id="POSITION">%2$d</xliff:g> ਸਥਿਤੀ \'ਤੇ ਸ਼ਾਮਲ ਕੀਤਾ ਗਿਆ"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ਨੂੰ ਹਟਾਇਆ ਗਿਆ"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ਨੂੰ <xliff:g id="POSITION">%2$d</xliff:g> ਸਥਿਤੀ \'ਤੇ ਤਬਦੀਲ ਕੀਤਾ ਗਿਆ"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ ਸੰਪਾਦਕ।"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pa-rIN/strings_tv.xml b/packages/SystemUI/res/values-pa-rIN/strings_tv.xml
index 78a5322..7cbda258 100644
--- a/packages/SystemUI/res/values-pa-rIN/strings_tv.xml
+++ b/packages/SystemUI/res/values-pa-rIN/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"PIP ਕੰਟਰੋਲ ਕਰਨ ਲਈ "<b>"ਹੋਮ"</b>" ਦਬਾਈ ਰੱਖੋ"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"PIP ਨੂੰ ਕੰਟਰੋਲ ਕਰਨ ਲਈ ਹੋਮ ਬਟਨ ਨੂੰ ਦੱਬੋ ਅਤੇ ਦਬਾਈ ਰੱਖੋ"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"ਸਮਝ ਲਿਆ"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"ਖ਼ਾਰਜ ਕਰੋ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 12452f6..297b849 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -170,6 +170,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Usuń stąd <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g>: zamknięto."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Wszystkie ostatnie aplikacje zostały zamknięte."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Uruchamiam <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Zamknięto powiadomienie."</string>
@@ -238,8 +240,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Lokalizacja z GPSa"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Prośby o lokalizację są aktywne"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Usuń wszystkie powiadomienia."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Ustawienia powiadomień"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Ustawienia aplikacji <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Ekran zostanie obrócony automatycznie."</string>
@@ -312,8 +313,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"szukaj"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Nie udało się uruchomić aplikacji <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"Aplikacja <xliff:g id="APP">%s</xliff:g> została wyłączona w trybie bezpiecznym."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Historia"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Wyczyść"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Wyczyść wszystko"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Ta aplikacja nie obsługuje trybu wielu okien"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Aplikacja nie obsługuje trybu wielu okien"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Podziel poziomo"</string>
@@ -482,8 +482,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Pokazuj na początku listy powiadomień, wyświetlaj na ekranie i sygnalizuj dźwiękiem"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Więcej ustawień"</string>
<string name="notification_done" msgid="5279426047273930175">"Gotowe"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> – ustawienia powiadomień"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Kolor i wygląd"</string>
<string name="night_mode" msgid="3540405868248625488">"Tryb nocny"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Kalibracja wyświetlacza"</string>
@@ -503,10 +502,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Oszczędzanie baterii nie jest dostępne podczas ładowania"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Oszczędzanie baterii"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Zmniejsza wydajność i ogranicza dane w tle"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Przycisk <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Wstecz"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"W górę"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"W dół"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"W lewo"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"W prawo"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Do środka"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Spacja"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Odtwórz/wstrzymaj"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Zatrzymaj"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Następny"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Poprzedni"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Przewiń do tyłu"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Przewiń do przodu"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Klawiatura numeryczna <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"System"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Ekran główny"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Ostatnie"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Wstecz"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Powiadomienia"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Skróty klawiszowe"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Przełącz metodę wprowadzania"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Aplikacje"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Pomoc"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Przeglądarka"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontakty"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-mail"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Komunikator"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Muzyka"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalendarz"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Pokazuj z regulacją głośności"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Nie przeszkadzać"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Wł./wył. przyciskami głośności"</string>
@@ -561,4 +598,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Przesuń w górę"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Przesuń w lewo"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Przesuń w prawo"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Aplikacja może nie działać w trybie wielu okien"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"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="8133209638023882667">"Kafelek <xliff:g id="TILE_NAME">%1$s</xliff:g>. Kliknij dwukrotnie, by dodać."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Położenie <xliff:g id="POSITION">%1$d</xliff:g>. Kliknij dwukrotnie, by wybrać."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Przenieś kafelek <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Usuń kafelek <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"Kafelek <xliff:g id="TILE_NAME">%1$s</xliff:g> został dodany w położeniu <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"Kafelek <xliff:g id="TILE_NAME">%1$s</xliff:g> został usunięty"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"Kafelek <xliff:g id="TILE_NAME">%1$s</xliff:g> przeniesiony w położenie <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Edytor szybkich ustawień."</string>
</resources>
diff --git a/packages/SystemUI/res/values-pl/strings_tv.xml b/packages/SystemUI/res/values-pl/strings_tv.xml
index 78bb18e..70be3d9 100644
--- a/packages/SystemUI/res/values-pl/strings_tv.xml
+++ b/packages/SystemUI/res/values-pl/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"Przytrzymaj "<b>"EKRAN GŁÓWNY"</b>", by sterować PIP"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Przytrzymaj przycisk EKRAN GŁÓWNY, by sterować PIP"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"OK"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Zamknij"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 10d3181..eb08d99 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Descartar <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> descartado."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Todos os apps recentes foram dispensados."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Iniciando <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notificação dispensada."</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Local definido por GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Solicitações de localização ativas"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Limpar todas as notificações."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"Mais <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Configurações de notificação"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Configurações de <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"A tela girará automaticamente."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"pesquisar"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Não foi possível iniciar <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"O app <xliff:g id="APP">%s</xliff:g> está desativado no modo de segurança."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Histórico"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Limpar"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Limpar tudo"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Este app não é compatível com o modo de várias janelas"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"O app não é compatível com o modo de várias janelas"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Divisão horizontal"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Mostrar na parte superior da lista de notificações, mostrar parcialmente na tela e permitir sons"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Mais configurações"</string>
<string name="notification_done" msgid="5279426047273930175">"Concluído"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"Controles de notificação do <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Cor e aparência"</string>
<string name="night_mode" msgid="3540405868248625488">"Modo noturno"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Calibrar tela"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"A Economia de bateria não fica disponível durante o carregamento"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Economia de bateria"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduz o desempenho e os dados em segundo plano"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Botão <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Voltar"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Para cima"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Para baixo"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Para a esquerda"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Para a direita"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Centralizar"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Space"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Barra de espaço"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Reproduzir/pausar"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Parar"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Avançar"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Anterior"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Retroceder"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Avançar rapidamente"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Numpad <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Sistema"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Início"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Recentes"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Voltar"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Notificações"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Atalhos do teclado"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Alterar o método de entrada"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Aplicativos"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Assistente"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Navegador"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Contatos"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-mail"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Mensagens instantâneas"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Música"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Agenda"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Mostrar com controles de volume"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Não perturbe"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Atalho de botões de volume"</string>
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Mover para cima"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Mover para a esquerda"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Mover para a direita"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"É possível que o app não funcione com várias janelas"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Toque duas vezes para adicionar."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Posição <xliff:g id="POSITION">%1$d</xliff:g>. Toque duas vezes para selecionar."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Remove <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> é adicionado à posição <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> é removido"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> movido para a posição <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Editor de configurações rápidas."</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings_tv.xml b/packages/SystemUI/res/values-pt-rBR/strings_tv.xml
index 6061af3..0827f9c7 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings_tv.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"Mantenha "<b>"INÍCIO"</b>" pressionado para controlar o PIP"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Mantenha a tecla \"HOME\" pressionada para controlar o PIP"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"Entendi"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Dispensar"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 7a2866d..bba2f3e 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Ignorar <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> ignorado."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Todas as aplicações recentes foram ignoradas."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"A iniciar <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notificação ignorada."</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Localização definida por GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Pedidos de localização ativos"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Limpar todas as notificações."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Definições de notificação"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Definições do <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"O ecrã será rodado automaticamente."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"pesquisar"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Não foi possível iniciar o <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"O <xliff:g id="APP">%s</xliff:g> está desativado no modo de segurança."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Histórico"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Limpar"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Limpar tudo"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Esta aplicação não é compatível com várias janelas"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"A aplicação não é compatível com várias janelas"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Divisão horizontal"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Mostrar na parte superior da lista de notificações, mostrar no ecrã e permitir som"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Mais definições"</string>
<string name="notification_done" msgid="5279426047273930175">"Concluído"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"Controlos de notificações do <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Cor e aspeto"</string>
<string name="night_mode" msgid="3540405868248625488">"Modo noturno"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Calibrar ecrã"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Poupança de bateria não disponível durante o carregamento"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Poupança de bateria"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduz o desempenho e os dados de segundo plano"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Botão <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Início"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Anterior"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Para cima"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Para baixo"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Para a esquerda"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Para a direita"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Ao centro"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tabulação"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Espaço"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Retrocesso"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Reproduzir/interromper"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Parar"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Seguinte"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Anterior"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Recuar"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Avançar"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Página para cima"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Página para baixo"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Eliminar"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Início"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"Fim"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Inserir"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Teclado numérico <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Sistema"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Página inicial"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Recentes"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Anterior"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Notificações"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Atalhos de teclado"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Alternar o método de introdução"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Aplicações"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Assistência"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Navegador"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Contactos"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Email"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Música"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Calendário"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Mostrar com controlos de volume"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Não incomodar"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Atalho dos botões de volume"</string>
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Mover para cima"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Mover para a esquerda"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Mover para a direita"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"A aplicação pode não funcionar com multijanelas"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Toque duas vezes para adicionar."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Posição <xliff:g id="POSITION">%1$d</xliff:g>. Toque duas vezes para selecionar."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Mover <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Remover <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> adicionado à posição <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> removido"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> movido para a posição <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Editor de definições rápidas."</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings_tv.xml b/packages/SystemUI/res/values-pt-rPT/strings_tv.xml
index 78d1352..2f465d2 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings_tv.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"Prima sem soltar o botão "<b>"HOME"</b>" para controlar o PIP"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Prima sem soltar o botão HOME para controlar o PIP"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"Compreendi"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Ignorar"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 10d3181..eb08d99 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Descartar <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> descartado."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Todos os apps recentes foram dispensados."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Iniciando <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notificação dispensada."</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Local definido por GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Solicitações de localização ativas"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Limpar todas as notificações."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"Mais <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Configurações de notificação"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Configurações de <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"A tela girará automaticamente."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"pesquisar"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Não foi possível iniciar <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"O app <xliff:g id="APP">%s</xliff:g> está desativado no modo de segurança."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Histórico"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Limpar"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Limpar tudo"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Este app não é compatível com o modo de várias janelas"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"O app não é compatível com o modo de várias janelas"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Divisão horizontal"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Mostrar na parte superior da lista de notificações, mostrar parcialmente na tela e permitir sons"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Mais configurações"</string>
<string name="notification_done" msgid="5279426047273930175">"Concluído"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"Controles de notificação do <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Cor e aparência"</string>
<string name="night_mode" msgid="3540405868248625488">"Modo noturno"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Calibrar tela"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"A Economia de bateria não fica disponível durante o carregamento"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Economia de bateria"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduz o desempenho e os dados em segundo plano"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Botão <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Voltar"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Para cima"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Para baixo"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Para a esquerda"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Para a direita"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Centralizar"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Space"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Barra de espaço"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Reproduzir/pausar"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Parar"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Avançar"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Anterior"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Retroceder"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Avançar rapidamente"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Numpad <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Sistema"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Início"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Recentes"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Voltar"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Notificações"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Atalhos do teclado"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Alterar o método de entrada"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Aplicativos"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Assistente"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Navegador"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Contatos"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-mail"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Mensagens instantâneas"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Música"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Agenda"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Mostrar com controles de volume"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Não perturbe"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Atalho de botões de volume"</string>
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Mover para cima"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Mover para a esquerda"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Mover para a direita"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"É possível que o app não funcione com várias janelas"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Toque duas vezes para adicionar."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Posição <xliff:g id="POSITION">%1$d</xliff:g>. Toque duas vezes para selecionar."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Move <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Remove <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> é adicionado à posição <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> é removido"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> movido para a posição <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Editor de configurações rápidas."</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt/strings_tv.xml b/packages/SystemUI/res/values-pt/strings_tv.xml
index 6061af3..0827f9c7 100644
--- a/packages/SystemUI/res/values-pt/strings_tv.xml
+++ b/packages/SystemUI/res/values-pt/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"Mantenha "<b>"INÍCIO"</b>" pressionado para controlar o PIP"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Mantenha a tecla \"HOME\" pressionada para controlar o PIP"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"Entendi"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Dispensar"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 5f71c41..81d0456 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -169,6 +169,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Închideți <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> a fost eliminată."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Toate aplicațiile recente au fost închise."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Se inițiază <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notificarea a fost închisă."</string>
@@ -237,8 +239,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Locație setată prin GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Solicitări locație active"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Ștergeți toate notificările."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Setări pentru notificări"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Setări <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Ecranul se va roti în mod automat."</string>
@@ -311,8 +312,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"căutare"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> nu a putut porni."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"Aplicația <xliff:g id="APP">%s</xliff:g> este dezactivată în modul sigur."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Istoric"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Ștergeți"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Ștergeți-le pe toate"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Această aplicație nu acceptă ferestre multiple"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Aplicația nu acceptă ferestre multiple"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Divizare pe orizontală"</string>
@@ -481,8 +481,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Apar în partea de sus a listei cu notificări, se afișează pentru scurt timp pe ecran și se permite un sunet"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Mai multe setări"</string>
<string name="notification_done" msgid="5279426047273930175">"Terminat"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"Opțiuni privind notificările pentru <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Culoare și aspect"</string>
<string name="night_mode" msgid="3540405868248625488">"Modul Noapte"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Calibrați afișarea"</string>
@@ -502,10 +501,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Economisirea bateriei nu este disponibilă pe durata încărcării"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Economisirea bateriei"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduce performanța și datele de fundal"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Butonul <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"La început"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Înapoi"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"În sus"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"În jos"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"La stânga"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"La dreapta"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"În centru"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Spațiu"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Redați/Întrerupeți"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Opriți"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Înainte"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Înapoi"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Derulați înapoi"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Derulați rapid înainte"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"O pagină mai sus"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"O pagină mai jos"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Ștergeți"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"La început"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"La final"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Inserați"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Tasta numerică <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Sistem"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Ecran de pornire"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Recente"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Înapoi"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Notificări"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Comenzi rapide de la tastatură"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Comutați metoda de introducere a textului"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Aplicații"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Asistent"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Browser"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Agendă"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-mail"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Muzică"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Calendar"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Afișează cu comenzile de volum"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Nu deranja"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Comandă rapidă din butoanele de volum"</string>
@@ -560,4 +597,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Mutați în sus"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Mutați spre stânga"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Mutați spre dreapta"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Este posibil ca aplicația să nu funcționeze cu ferestre multiple"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Atingeți de două ori pentru a adăuga."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Poziția <xliff:g id="POSITION">%1$d</xliff:g>. Atingeți de două ori pentru a selecta."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Mutați <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Eliminați <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"Caseta <xliff:g id="TILE_NAME">%1$s</xliff:g> este adăugată pe poziția <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"Caseta <xliff:g id="TILE_NAME">%1$s</xliff:g> este eliminată"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"Caseta <xliff:g id="TILE_NAME">%1$s</xliff:g> a fost mutată pe poziția <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Editorul pentru setări rapide."</string>
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings_tv.xml b/packages/SystemUI/res/values-ro/strings_tv.xml
index fcbfd07..9ef90dd 100644
--- a/packages/SystemUI/res/values-ro/strings_tv.xml
+++ b/packages/SystemUI/res/values-ro/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"Apăsați lung "<b>"ACASĂ"</b>" pentru a controla PIP"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Apăsați lung butonul ECRAN DE PORNIRE pentru a controla PIP"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"Am înțeles"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Închideți"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index ffb0a73..86f0ec4 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -170,6 +170,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Удаление приложения <xliff:g id="APP">%s</xliff:g> из списка."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Приложение \"<xliff:g id="APP">%s</xliff:g>\" удалено из списка."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Все недавние приложения закрыты."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Запуск приложения <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Уведомление закрыто"</string>
@@ -238,8 +240,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Координаты по GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Есть активные запросы на определение местоположения"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Удалить все уведомления"</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Настройки уведомлений"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Настройки приложения \"<xliff:g id="APP_NAME">%s</xliff:g>\""</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Экран будет поворачиваться автоматически."</string>
@@ -312,8 +313,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"поиск"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Не удалось запустить приложение \"<xliff:g id="APP">%s</xliff:g>\""</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"Приложение \"<xliff:g id="APP">%s</xliff:g>\" отключено в безопасном режиме."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Журнал"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Очистить"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Очистить все"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Приложение не поддерживает многооконный режим"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Приложение не поддерживает многооконный режим"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Разделить по горизонтали"</string>
@@ -482,8 +482,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Показывать со звуком в начале списка уведомлений и поверх всех окон"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Другие настройки"</string>
<string name="notification_done" msgid="5279426047273930175">"Готово"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"Управление уведомлениями (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Цвета и стиль"</string>
<string name="night_mode" msgid="3540405868248625488">"Ночной режим"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Калибровка дисплея"</string>
@@ -503,10 +502,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Режим энергосбережения нельзя включить во время зарядки"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Режим энергосбережения"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Ограничивает производительность и фоновую передачу данных"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Кнопка <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Главный экран"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Назад"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Стрелка вверх"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Стрелка вниз"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Стрелка влево"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Стрелка вправо"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Центральная стрелка"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Пробел"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Ввод"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Воспроизведение/пауза"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Стоп"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Следующий трек"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Предыдущий трек"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Перемотка назад"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Перемотка вперед"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"<xliff:g id="NAME">%1$s</xliff:g> на цифровой панели"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Система"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Главный экран"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Недавние"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Назад"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Уведомления"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Быстрые клавиши"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Сменить способ ввода"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Приложения"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Помощник"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Браузер"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Контакты"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Эл. почта"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Чат"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Музыка."</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Календарь"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Показывать при нажатии кнопок регулировки громкости"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Не беспокоить"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Кнопки регулировки громкости"</string>
@@ -561,4 +598,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Поднять"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Сдвинуть влево"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Сдвинуть вправо"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Приложение не поддерживает многооконный режим"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"Позиция <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="8133209638023882667">"Кнопка \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\". Чтобы добавить, нажмите дважды."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Позиция <xliff:g id="POSITION">%1$d</xliff:g>. Чтобы выбрать, нажмите дважды."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Переместить кнопку \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\""</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Удалить кнопку \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\""</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"Кнопка \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\" теперь занимает позицию <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"Кнопка \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\" удалена"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"Кнопка \"<xliff:g id="TILE_NAME">%1$s</xliff:g>\" теперь занимает позицию <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Редактор быстрых настроек."</string>
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings_tv.xml b/packages/SystemUI/res/values-ru/strings_tv.xml
index 309ce73..027cb1f 100644
--- a/packages/SystemUI/res/values-ru/strings_tv.xml
+++ b/packages/SystemUI/res/values-ru/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"Управляйте кадром в кадре, удерживая кнопку "<b>"ГЛАВНАЯ"</b></string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Управляйте режимом \"Кадр в кадре\", удерживая кнопку ГЛАВНАЯ"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"ОК"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Закрыть"</string>
</resources>
diff --git a/packages/SystemUI/res/values-si-rLK/strings.xml b/packages/SystemUI/res/values-si-rLK/strings.xml
index 84c2f1b..5a2f849 100644
--- a/packages/SystemUI/res/values-si-rLK/strings.xml
+++ b/packages/SystemUI/res/values-si-rLK/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> ඉවතලන්න."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> අස් කර ඇත."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"සියලුම මෑත යෙඳුම් අස් කරන ලදි."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> ආරම්භ කරමින්."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"දැනුම්දීම නිෂ්ප්රභා කරඇත."</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"GPS මඟින් ස්ථානය සකසා ඇත"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"පිහිටීම් ඉල්ලීම් සක්රියයි"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"සියලු දැනුම්දීම් හිස් කරන්න."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"දැනුම්දීම් සැකසීම්"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> සැකසීම්"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"තිරය ස්වයංක්රීයව කරකැවේ."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"සෙවීම"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> ආරම්භ කළ නොහැක."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> ආරක්ෂිත ප්රකාරය තුළ අබලයි."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"ඉතිහාසය"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"හිස් කරන්න"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"සියල්ල හිස් කරන්න"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"මෙම යෙදුම බහු-කවුළුව සඳහා සහාය නොදක්වයි"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"යෙදුම බහු-කවුළුව සඳහා සහාය නොදක්වයි"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"තිරස්ව වෙන් කරන්න"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"දැනුම්දීම් ලැයිස්තුවෙහි ඉහළින්ම පෙන්වන්න, තිරයට එබිකම් කර ශබ්දයට ඉඩ දෙන්න"</string>
<string name="notification_more_settings" msgid="816306283396553571">"තව සැකසීම්"</string>
<string name="notification_done" msgid="5279426047273930175">"නිමයි"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> දැනුම්දීම් පාලන"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"වර්ණය සහ පෙනුම"</string>
<string name="night_mode" msgid="3540405868248625488">"රාත්රී ප්රකාරය"</string>
<string name="calibrate_display" msgid="5974642573432039217">"සංදර්ශකය ක්රමාංකනය කරන්න"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"ආරෝපණය අතරතුර බැටරි සුරැකුම ලබා ගත නොහැකිය."</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"බැටරි සුරැකුම"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"ක්රියාකාරිත්වය සහ පසුබිම් දත්ත අඩු කරන්න"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"<xliff:g id="NAME">%1$s</xliff:g> බොත්තම"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home යතුර"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"ආපසු"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"උඩු"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"යටි"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"වම්"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"දකුණු"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"මැද"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab යතුර"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"ඉඩ යතුර"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter යතුර"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace යතුර"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"ධාවනය කරන්න/විරාම කරන්න"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"නතර කරන්න"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"ඊළඟ"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"පෙර"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"නැවත ඔතන්න"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"වේගයෙන් ඉදිරියට යන"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up යතුර"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down යතුර"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete යතුර"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home යතුර"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End යතුර"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert යතුර"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock යතුර"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"<xliff:g id="NAME">%1$s</xliff:g> අංක පෑඩය"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"පද්ධතිය"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"මුල් පිටුව"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"මෑත"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"ආපසු"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"දැනුම්දීම්"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"යතුරු පුවරු කෙටිමං"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"ආදාන ක්රමය මාරු කිරීම"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"යෙදුම්"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"සහාය"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"බ්රවුසරය"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"සම්බන්ධතා"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"ඊ-තැපෑල"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"සංගීතය"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"දින දර්ශනය"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"හඩ පරිමා පාලන සහිතව පෙන්වන්න"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"බාධා නොකරන්න"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"හඩ පරිමා බොත්තම් කෙටිමග"</string>
@@ -540,8 +577,7 @@
<string name="select_keycode" msgid="7413765103381924584">"යතුරු පුවරු බොත්තම තෝරන්න"</string>
<string name="preview" msgid="9077832302472282938">"පෙරදසුන"</string>
<string name="drag_to_add_tiles" msgid="7058945779098711293">"ටයිල් එක් කිරීමට අදින්න"</string>
- <!-- no translation found for drag_to_remove_tiles (3361212377437088062) -->
- <skip />
+ <string name="drag_to_remove_tiles" msgid="3361212377437088062">"ඉවත් කිරීමට මෙතැනට අදින්න"</string>
<string name="qs_edit" msgid="2232596095725105230">"සංස්කරණය කරන්න"</string>
<string name="tuner_time" msgid="6572217313285536011">"වේලාව"</string>
<string-array name="clock_options">
@@ -560,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"ඉහළට ගෙන යන්න"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"වමට ගෙන යන්න"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"දකුණට ගෙන යන්න"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"යෙදුම බහු-කවුළුව සමඟ ක්රියා නොකළ හැකිය."</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"ස්ථානය <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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. එක් කිරීමට දෙවරක් තට්ටු කරන්න."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"ස්ථානය <xliff:g id="POSITION">%1$d</xliff:g>. තෝරා ගැනීමට දෙවරක් තට්ටු කරන්න."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ගෙන යන්න"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ඉවත් කරන්න"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> <xliff:g id="POSITION">%2$d</xliff:g> වන ස්ථානයට එක් කරන ලදි"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ඉවත් කරන ලදි"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> <xliff:g id="POSITION">%2$d</xliff:g> වන ස්ථානයට ගෙන යන ලදි"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"ඉක්මන් සැකසුම් සංස්කාරකය."</string>
</resources>
diff --git a/packages/SystemUI/res/values-si-rLK/strings_tv.xml b/packages/SystemUI/res/values-si-rLK/strings_tv.xml
index f3e95a1..3380754 100644
--- a/packages/SystemUI/res/values-si-rLK/strings_tv.xml
+++ b/packages/SystemUI/res/values-si-rLK/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"PIP පාලනයට "<b>"HOME"</b>" අල්ලාගන්න"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"PIP පාලනය කිරීමට HOME බොත්තම ඔබා අල්ලාගෙන සිටින්න"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"හරි, තේරුණා"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"අස් කරන්න"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index e53b037..9468fcf 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -170,6 +170,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Zrušiť aplikáciu <xliff:g id="APP">%s</xliff:g>"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Aplikácia <xliff:g id="APP">%s</xliff:g> bola zrušená."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Všetky nedávne aplikácie boli odmietnuté."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Spúšťa sa aplikácia <xliff:g id="APP">%s</xliff:g>"</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Upozornenie bolo zrušené."</string>
@@ -238,8 +240,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Poloha nastavená pomocou GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Žiadosti o polohu sú aktívne"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Vymazať všetky upozornenia."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Nastavenia upozornení"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Nastavenia aplikácie <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Obrazovka sa automaticky otočí."</string>
@@ -312,8 +313,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"hľadať"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Aplikáciu <xliff:g id="APP">%s</xliff:g> sa nepodarilo spustiť"</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"Aplikácia <xliff:g id="APP">%s</xliff:g> je v núdzovom režime zakázaná."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"História"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Vymazať"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Vymazať všetko"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Táto aplikácia nepodporuje režim viacerých okien"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Aplikácia nepodporuje režim viacerých okien"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Rozdeliť vodorovné"</string>
@@ -482,8 +482,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Zobrazovať v hornej časti zoznamu upozornení, zobrazovať cez obrazovku a povoliť zvukový signál"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Ďalšie nastavenia"</string>
<string name="notification_done" msgid="5279426047273930175">"Hotovo"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"Ovládacie prvky pre upozornenia z aplikácie <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Farba a vzhľad"</string>
<string name="night_mode" msgid="3540405868248625488">"Nočný režim"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Kalibrovať obrazovku"</string>
@@ -503,10 +502,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Počas nabíjania nie je Šetrič batérie k dispozícii"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Šetrič batérie"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Obmedzí výkonnosť a údaje na pozadí"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Tlačidlo <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Domov"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Späť"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Nahor"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Nadol"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Doľava"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Doprava"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Do stredu"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tabulátor"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Medzerník"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Prehrať/pozastaviť"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Zastaviť"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Nasledujúce"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Predchádzajúce"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Pretočiť späť"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Pretočiť dopredu"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Posunúť o stranu vyššie"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Posunúť o stranu nižšie"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Odstrániť"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Domov"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"Ukončiť"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Vložiť"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Číselná klávesnica <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Systém"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Domovská stránka"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Nedávne"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Späť"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Upozornenia"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Klávesové skratky"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Prepnúť metódu vstupu"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Aplikácie"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Pomocná aplikácia"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Prehliadač"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontakty"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-mail"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Okamžité správy"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Hudba"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalendár"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Zobrazovať s ovládacími prvkami hlasitosti"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Nerušiť"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Skratka tlačidiel hlasitosti"</string>
@@ -561,4 +598,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Posunúť nahor"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Posunúť doľava"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Posunúť doprava"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Aplikácia nemusí fungovať v režime viacerých okien"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Pridáte ju dvojitým klepnutím."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Pozícia <xliff:g id="POSITION">%1$d</xliff:g>. Vyberiete ju dvojitým klepnutím."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Presunúť dlaždicu <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Odstrániť dlaždicu <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"Dlaždica <xliff:g id="TILE_NAME">%1$s</xliff:g> bola pridaná na pozíciu <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"Dlaždica <xliff:g id="TILE_NAME">%1$s</xliff:g> bola odstránená"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"Dlaždica <xliff:g id="TILE_NAME">%1$s</xliff:g> bola presunutá na pozíciu <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Editor rýchlych nastavení"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sk/strings_tv.xml b/packages/SystemUI/res/values-sk/strings_tv.xml
index 64fa0e8..cc48e07 100644
--- a/packages/SystemUI/res/values-sk/strings_tv.xml
+++ b/packages/SystemUI/res/values-sk/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"Režim PIP ovládajte pomocou tlačidla "<b>"PLOCHA"</b></string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Režim PIP ovládajte stlačením a podržaním tlačidla PLOCHA"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"Dobre"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Odmietnuť"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 8cff73b1..c5a43f9 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -170,6 +170,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Opusti aplikacijo <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Aplikacija <xliff:g id="APP">%s</xliff:g> je bila odstranjena."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Vse nedavne aplikacije so bile opuščene."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Zaganjanje aplikacije <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Obvestilo je bilo odstranjeno."</string>
@@ -238,8 +240,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Lokacija nastavljena z GPS-om"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Aktivne zahteve za lokacijo"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Izbriši vsa obvestila."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"in <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Nastavitve obvestil"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Nastavitve aplikacije <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Zaslon se bo samodejno zasukal."</string>
@@ -312,8 +313,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"iskanje"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Aplikacije <xliff:g id="APP">%s</xliff:g> ni bilo mogoče zagnati."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"Aplikacija <xliff:g id="APP">%s</xliff:g> je v varnem načinu onemogočena."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Zgodovina"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Izbriši"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Izbriši vse"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Ta aplikacija ne podpira načina z več okni"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Aplikacija ne podpira načina z več okni"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Razdeli vodoravno"</string>
@@ -482,8 +482,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Prikaži na vrhu seznama obvestil, za hip pokaži predogled na zaslonu in dovoli zvok"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Več nastavitev"</string>
<string name="notification_done" msgid="5279426047273930175">"Dokončano"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"Kontrolniki obvestil za aplikacijo <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Barva in videz"</string>
<string name="night_mode" msgid="3540405868248625488">"Nočni način"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Umerjanje zaslona"</string>
@@ -503,10 +502,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Varčevanje z energijo akumulatorja med polnjenjem ni na voljo"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Varčevanje z energijo akumulatorja"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Omeji zmogljivost delovanja in prenos podatkov v ozadju"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Gumb <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Začetek"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Nazaj"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Gor"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Dol"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Levo"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Desno"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Sredina"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tabulatorka"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Preslednica"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Vnesi"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Premik nazaj"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Predvajaj/zaustavi"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Ustavi"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Naslednji"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Prejšnji"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Previj nazaj"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Previj naprej"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Stran gor"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Stran dol"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Izbriši"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Začetek"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"Konec"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Vstavi"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Številska tipkovnica <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Sistem"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Začetni zaslon"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Nedavni"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Nazaj"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Obvestila"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Bližnjične tipke"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Preklop načina vnosa"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Aplikacije"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Pomoč"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Brskalnik"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Stiki"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-pošta"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Neposredno sporočanje"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Glasba"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Koledar"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Prikaži s kontrolniki glasnosti"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Ne moti"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Bližnjica z gumboma za glasnost"</string>
@@ -561,4 +598,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Premakni navzgor"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Premakni levo"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Premakni desno"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Aplikacija morda ne deluje v načinu z več okni"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Če želite dodati, se dvakrat dotaknite."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Položaj: <xliff:g id="POSITION">%1$d</xliff:g>. Če želite izbrati, se dvakrat dotaknite."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Premik tega: <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Odstranitev tega: <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> je dodano na položaj <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> je odstranjeno"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> premaknjeno na položaj <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Urejevalnik hitrih nastavitev."</string>
</resources>
diff --git a/packages/SystemUI/res/values-sl/strings_tv.xml b/packages/SystemUI/res/values-sl/strings_tv.xml
index 4d0576b..38f9e8e 100644
--- a/packages/SystemUI/res/values-sl/strings_tv.xml
+++ b/packages/SystemUI/res/values-sl/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"Pridr. "<b>"HOME"</b>" za up. n. PIP"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Pridržite gumb HOME za upravljanje načina PIP"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"Razumem"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Opusti"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sq-rAL/strings.xml b/packages/SystemUI/res/values-sq-rAL/strings.xml
index 35b4dc7..0188e5b 100644
--- a/packages/SystemUI/res/values-sq-rAL/strings.xml
+++ b/packages/SystemUI/res/values-sq-rAL/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Largo <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> është hequr."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Të gjitha aplikacionet e fundit u larguan."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Po nis <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Njoftimi është hequr."</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Vendndodhja është caktuar nga GPS-ja"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Kërkesat për vendodhje janë aktive"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Pastro të gjitha njoftimet."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Cilësimet e njoftimeve"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Cilësimet e <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Ekrani do të rrotullohet automatikisht."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"kërko"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> nuk mundi të nisej."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> është i çaktivizuar në modalitetin e sigurt."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Historiku"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Pastro"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Pastroji të gjitha"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Ky aplikacion nuk e mbështet modalitetin me shumë dritare"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Aplikacioni nuk e mbështet modalitetin me shumë dritare"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Ndaje horizontalisht"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Shfaqi në krye të listës së njoftimeve, shfaq vështrim të shpejtë në ekran dhe lësho një tingull"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Cilësime të tjera"</string>
<string name="notification_done" msgid="5279426047273930175">"U krye"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"Kontrollet e njoftimeve të <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Ngjyra dhe pamja"</string>
<string name="night_mode" msgid="3540405868248625488">"Modaliteti i natës"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Kalibro ekranin"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"\"Kursyesi i baterisë\" nuk është i disponueshëm gjatë karikimit"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Kursyesi i baterisë"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Pakëson veprimtarinë dhe të dhënat në sfond"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Butoni <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Kreu"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Prapa"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Lart"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Poshtë"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Majtas"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Djathtas"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Qendror"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Skedë"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Hapësirë"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Kthim prapa"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Luaj/pauzë"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Ndalo"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Përpara"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Prapa"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Rikthe me shpejtësi"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Përparo me shpejtësi"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Faqja lart"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Faqja poshtë"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Fshi"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Kreu"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"Fundi"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Fut"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Kyçja e numrave"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Tastiera numerike <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Sistemi"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Kreu"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Të fundit"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Prapa"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Njoftimet"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Shkurtoret e tastierës"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Ndërro metodën e hyrjes"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Aplikacionet"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Asistenti"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Shfletuesi"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontaktet"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Mail-i"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Mesazh i çastit"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Muzikë"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalendari"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Shfaq me kontrollet e volumit"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Mos shqetëso"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Shkurtorja e butonave të volumit"</string>
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Lëviz lart"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Lëviz majtas"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Lëviz djathtas"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Aplikacioni mund të mos punojë me funksionin me shumë dritare."</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Trokit dy herë për ta shtuar."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Pozicioni <xliff:g id="POSITION">%1$d</xliff:g>. Trokit dy herë për ta zgjedhur."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Zhvendose <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Hiqe <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> është shtuar te pozicioni <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> u hoq"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> u zhvendos te pozicioni <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Redaktori i cilësimeve të shpejta."</string>
</resources>
diff --git a/packages/SystemUI/res/values-sq-rAL/strings_tv.xml b/packages/SystemUI/res/values-sq-rAL/strings_tv.xml
index bcb53fc..672a119 100644
--- a/packages/SystemUI/res/values-sq-rAL/strings_tv.xml
+++ b/packages/SystemUI/res/values-sq-rAL/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"Mbaj shtypur "<b>"HOME"</b>" për të kontrolluar PIP"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Shtyp dhe mbaj shtypur butonin HOME për të kontrolluar PIP"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"E kuptova"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Hiqe"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 5bc07fa..501a3e3 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -169,6 +169,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Одбаците <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Апликација <xliff:g id="APP">%s</xliff:g> је одбачена."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Све недавно коришћене апликације су одбачене."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Покрећемо <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Обавештење је одбачено."</string>
@@ -237,8 +239,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Локацију је подесио GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Има активних захтева за локацију"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Обриши сва обавештења."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"и још <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Подешавања обавештења"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Подешавања за <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Екран ће се аутоматски ротирати."</string>
@@ -311,8 +312,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"претражи"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Покретање апликације <xliff:g id="APP">%s</xliff:g> није успело."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"Апликација <xliff:g id="APP">%s</xliff:g> је онемогућена у безбедном режиму."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Историја"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Обриши"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Обриши све"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Ова апликација не подржава режим са више прозора"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Апликација не подржава режим са више прозора"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Подели хоризонтално"</string>
@@ -481,8 +481,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Приказују се у врху листе обавештења, накратко се приказују на екрану и емитују звук"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Још подешавања"</string>
<string name="notification_done" msgid="5279426047273930175">"Готово"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"Контроле обавештења за апликацију <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Боја и изглед"</string>
<string name="night_mode" msgid="3540405868248625488">"Ноћни режим"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Калибришите екран"</string>
@@ -502,10 +501,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Уштеда батерије није доступна током пуњења"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Уштеда батерије"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Смањује перформансе и позадинске податке"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Дугме <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Тастер Почетна"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Тастер Назад"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Тастер са стрелицом нагоре"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Тастер са стрелицом надоле"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Тастер са стрелицом налево"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Тастер са стрелицом надесно"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Тастер са централном стрелицом"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Табулатор"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Тастер за размак"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Тастер за брисање уназад"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Тастер за репродукцију/паузирање"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Тастер за заустављање"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Тастер Следећа"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Тастер Претходна"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Тастер за премотавање уназад"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Тастер за премотавање унапред"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Тастер за страницу нагоре"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Тастер за страницу надоле"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Тастер за брисање"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Тастер Почетна"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"Тастер за крај"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Тастер за уметање"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Тастер <xliff:g id="NAME">%1$s</xliff:g> на нумеричкој тастатури"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Систем"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Почетни"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Недавни садржај"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Назад"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Обавештења"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Тастерске пречице"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Промени метод уноса"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Апликације"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Апликација за помоћ"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Прегледач"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Контакти"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Имејл"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Размена тренутних порука"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Музика"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Календар"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Прикажи са контролама јачине звука"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Не узнемиравај"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Пречица за дугмад за јачину звука"</string>
@@ -560,4 +597,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Помери нагоре"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Помери улево"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Помери удесно"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Апликација можда неће функционисати са више прозора"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"<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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Двапут додирните да бисте додали."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"<xliff:g id="POSITION">%1$d</xliff:g>. позиција. Двапут додирните да бисте изабрали."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Премести плочицу <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Уклони плочицу <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"Плочица <xliff:g id="TILE_NAME">%1$s</xliff:g> је додата на <xliff:g id="POSITION">%2$d</xliff:g>. позицију"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"Плочица <xliff:g id="TILE_NAME">%1$s</xliff:g> је уклоњена"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"Плочица <xliff:g id="TILE_NAME">%1$s</xliff:g> је премештена на <xliff:g id="POSITION">%2$d</xliff:g>. позицију"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Уређивач за Брза подешавања."</string>
</resources>
diff --git a/packages/SystemUI/res/values-sr/strings_tv.xml b/packages/SystemUI/res/values-sr/strings_tv.xml
index a92374f..d822e4e 100644
--- a/packages/SystemUI/res/values-sr/strings_tv.xml
+++ b/packages/SystemUI/res/values-sr/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109"><b>"ПОЧЕТНИ ЕКРАН"</b>" конт. PIP"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Притисните и задржите дугме ПОЧЕТНИ ЕКРАН да бисте контролисали PIP"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"Важи"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Одбаци"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 7e40ce0..959128e 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Ta bort <xliff:g id="APP">%s</xliff:g> från listan."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> togs bort permanent."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Alla appar har tagits bort från listan Senaste."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Startar <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Meddelandet ignorerades."</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Platsen har identifierats av GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Det finns aktiva platsbegäranden"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Ta bort alla meddelanden."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"<xliff:g id="NUMBER">%s</xliff:g> till"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Aviseringsinställningar"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Inställningar för <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Skärmen roteras automatiskt."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"sök"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Det gick inte att starta appen <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> är inaktiverad i säkert läge."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Historik"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Rensa"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Rensa alla"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Den här appen har inte stöd för flera fönster"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Appen har inte stöd för flera fönster"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Dela horisontellt"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Visa högst upp i aviseringslistan och med snabbtitt på skärmen samt tillåt ljud"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Fler inställningar"</string>
<string name="notification_done" msgid="5279426047273930175">"Klar"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"Inställningar för <xliff:g id="APP_NAME">%1$s</xliff:g>-aviseringar"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Färg och utseende"</string>
<string name="night_mode" msgid="3540405868248625488">"Nattläge"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Kalibrera skärmen"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Batterisparläget är inte tillgängligt vid laddning"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Batterisparläge"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Minskar prestanda och bakgrundsdata"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Knappen <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Start"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Tillbaka"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Upp"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Ned"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Vänster"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Höger"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Centrera"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Flik"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Blanksteg"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Retur"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backsteg"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Spela upp/Pausa"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Avsluta"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Nästa"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Föregående"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Spola tillbaka"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Snabbspola framåt"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Sida upp"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Sida ned"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Radera"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Start"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"Slut"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Infoga"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Numeriskt tangentbord <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"System"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Startsida"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Senaste"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Tillbaka"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Aviseringar"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Kortkommandon"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Byt inmatningsmetod"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Appar"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Hjälp"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Webbläsare"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontakter"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-post"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Snabbmeddelanden"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Musik"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalender"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Visa med volymkontroller"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Stör ej"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Genväg till volymknappar"</string>
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Flytta uppåt"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Flytta åt vänster"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Flytta åt höger"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Appen fungerar eventuellt inte i flerfönsterläge"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"Position <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Tryck snabbt två gånger om du vill redigera positionen."</string>
+ <string name="accessibility_qs_edit_add_tile_label" msgid="8133209638023882667">"<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_position_label" msgid="5055306305919289819">"Position <xliff:g id="POSITION">%1$d</xliff:g>. Välj den genom att trycka snabbt två gånger."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Flytta <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Ta bort <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> har lagts till på position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> har tagits bort"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> har flyttats till position <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Redigerare för snabbinställningar."</string>
</resources>
diff --git a/packages/SystemUI/res/values-sv/strings_tv.xml b/packages/SystemUI/res/values-sv/strings_tv.xml
index 790ef76..0c0afc3 100644
--- a/packages/SystemUI/res/values-sv/strings_tv.xml
+++ b/packages/SystemUI/res/values-sv/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"Styr PIP med "<b>"startknappen"</b></string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Styr bild-i-bild genom att hålla ned startsideknappen"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"OK"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Ignorera"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index aa9ed8a..4b936c9 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Ondoa <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> imeondolewa."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Programu za hivi majuzi zimeondolewa."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Inaanzisha <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Arifa imetupwa."</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Mahali pamewekwa na GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Maombi ya eneo yanatumika"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Futa arifa zote."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Mipangilio ya arifa"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Mipangilio ya <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Skrini itazunguka kiotomatiki."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"tafuta"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Haikuweza kuanzisha <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> imezimwa katika hali salama."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Historia"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Futa"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Futa zote"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Programu hii haitumiki katika hali ya madirisha mengi"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Programu haitumiki katika hali ya madirisha mengi"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Gawanya Mlalo"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Onyesha katika sehemu ya juu ya orodha ya arifa, chungulia kwenye skrini na uruhusu sauti"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Mipangilio zaidi"</string>
<string name="notification_done" msgid="5279426047273930175">"Nimemaliza"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"Vidhibiti vya arifa za <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Rangi na mwonekano"</string>
<string name="night_mode" msgid="3540405868248625488">"Hali ya usiku"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Rekebisha onyesho"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Kiokoa Betri hakipatikani unapochaji betri"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Kiokoa Betri"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Hupunguza data ya chini chini na utendaji"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Kitufe cha <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Mwanzo"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Nyuma"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Juu"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Chini"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Kushoto"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Kulia"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Katikati"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Sogeza"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Nafasi"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Nafasinyuma"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Cheza/Sitisha"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Simamisha"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Inayofuata"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Iliyotangulia"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Rudisha nyuma"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Sogeza mbele Haraka"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Futa"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Mwanzo"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"Mwisho"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Ingiza"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Numpad <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Mfumo"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Mwanzo"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Zilizotumika majuzi"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Nyuma"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Arifa"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Mikato ya Kibodi"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Badilisha mbinu ya kuingiza data"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Programu"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Programu ya maagizo ya sauti"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Kivinjari"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Anwani"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Barua pepe"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Ujumbe wa papo kwa papo"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Muziki"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalenda"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Onyesha katika vidhibiti vya sauti"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Usinisumbue"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Njia ya mkato ya vitufe vya sauti"</string>
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Sogeza juu"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Sogeza kushoto"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Sogeza kulia"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Huenda programu isifanye kazi kwenye madirisha mengi"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"Nafasi ya <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Gonga mara mbili ili ubadilishe."</string>
+ <string name="accessibility_qs_edit_add_tile_label" msgid="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Gonga mara mbili ili uongeze."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Nafasi ya <xliff:g id="POSITION">%1$d</xliff:g>. Gonga mara mbili ili uchague."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Hamisha <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Ondoa <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> imeongezwa kwenye nafasi ya <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> imeondolewa"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> imehamishiwa kwenye nafasi ya <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Kihariri cha Mipangilio ya haraka."</string>
</resources>
diff --git a/packages/SystemUI/res/values-sw/strings_tv.xml b/packages/SystemUI/res/values-sw/strings_tv.xml
index bd29705..4875f73 100644
--- a/packages/SystemUI/res/values-sw/strings_tv.xml
+++ b/packages/SystemUI/res/values-sw/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"Shikilia kitufe cha "<b>"HOME"</b>" ili udhibiti PIP"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Bonyeza na ushikilie kitufe cha HOME ili kudhibiti PIP"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"Nimeelewa"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Ondoa"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ta-rIN/strings.xml b/packages/SystemUI/res/values-ta-rIN/strings.xml
index 3eb7565..5489be9 100644
--- a/packages/SystemUI/res/values-ta-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ta-rIN/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> ஐ நிராகரி."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> விலக்கப்பட்டது."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"எல்லா சமீபத்திய பயன்பாடுகளும் விலக்கப்பட்டன."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> ஐத் தொடங்குகிறது."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"அறிவிப்பு நிராகரிக்கப்பட்டது."</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"GPS அமைத்த இருப்பிடம்"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"இருப்பிடக் கோரிக்கைகள் இயக்கப்பட்டன"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"எல்லா அறிவிப்புகளையும் அழி."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"அறிவிப்பு அமைப்புகள்"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> அமைப்புகள்"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"திரை தானாகச் சுழலும்."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"தேடு"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g>ஐத் தொடங்க முடியவில்லை."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"பாதுகாப்புப் பயன்முறையில் <xliff:g id="APP">%s</xliff:g> முடக்கப்பட்டது."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"வரலாறு"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"அழி"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"அனைத்தையும் அழி"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"இந்தப் பயன்பாடு பல சாளர அம்சத்தை ஆதரிக்கவில்லை"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"பயன்பாடு பல சாளர அம்சத்தை ஆதரிக்காது"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"கிடைமட்டமாகப் பிரி"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"அறிவிப்புகள் பட்டியலின் மேற்பகுதியில், சில வினாடிகளுக்கு ஒலியுடன் திரையில் காட்டு"</string>
<string name="notification_more_settings" msgid="816306283396553571">"மேலும் அமைப்புகள்"</string>
<string name="notification_done" msgid="5279426047273930175">"முடிந்தது"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> அறிவிப்புக் கட்டுப்பாடுகள்"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"வண்ணமும் தோற்றமும்"</string>
<string name="night_mode" msgid="3540405868248625488">"இரவுப் பயன்முறை"</string>
<string name="calibrate_display" msgid="5974642573432039217">"திரையை அளவுத்திருத்தம் செய்"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"சார்ஜ் செய்யும் போது பேட்டரி சேமிப்பானைப் பயன்படுத்த முடியாது"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"பேட்டரி சேமிப்பான்"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"செயல்திறனையும் பின்புலத்தில் தரவு செயலாக்கப்படுவதையும் குறைக்கும்"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"<xliff:g id="NAME">%1$s</xliff:g> பொத்தான்"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"ஹோம்"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"பேக்"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"மேலே"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"கீழே"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"இடது"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"வலது"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"நடு"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"டேப்"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"ஸ்பேஸ்"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"என்டர்"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"பேக்ஸ்பேஸ்"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"பிளே/பாஸ்"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"ஸ்டாப்"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"நெக்ஸ்ட்"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"ப்ரீவியஸ்"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"ரீவைன்ட்"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"ஃபாஸ்ட் பார்வேர்டு"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"பேஜ் அப்"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"பேஜ் டவுன்"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"டெலிட்"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"ஹோம்"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"என்ட்"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"இன்சர்ட்"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"நம்பர் லாக்"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"நம்பர் பேடு <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"முறைமை"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"முகப்பு"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"சமீபத்தியவை"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"முந்தையது"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"அறிவிப்புகள்"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"விசைப்பலகைக் குறுக்குவழிகள்"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"உள்ளீட்டு முறையை மாற்று"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"பயன்பாடுகள்"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"அசிஸ்ட்"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"உலாவி"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"தொடர்புகள்"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"மின்னஞ்சல்"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"மியூசிக்"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"கேலெண்டர்"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"ஒலிக் கட்டுப்பாடுகளுடன் காட்டு"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"தொந்தரவு செய்ய வேண்டாம்"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"ஒலியளவுப் பொத்தான்களுக்கான குறுக்குவழி"</string>
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"மேலே நகர்த்து"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"இடப்புறம் நகர்த்து"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"வலப்புறம் நகர்த்து"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"பல சாளர அம்சத்தில் பயன்பாடு வேலைசெய்யாமல் போகக்கூடும்"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"நிலை <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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. சேர்க்க, இருமுறை தட்டவும்."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"நிலை <xliff:g id="POSITION">%1$d</xliff:g>. தேர்ந்தெடுக்க, இருமுறை தட்டவும்."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"<xliff:g id="TILE_NAME">%1$s</xliff:g>ஐ நகர்த்தவும்"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"<xliff:g id="TILE_NAME">%1$s</xliff:g>ஐ அகற்றவும்"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> நிலை <xliff:g id="POSITION">%2$d</xliff:g> இல் சேர்க்கப்பட்டது"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> அகற்றப்பட்டது"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> நிலை <xliff:g id="POSITION">%2$d</xliff:g>க்கு நகர்த்தப்பட்டது"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"விரைவு அமைப்புகள் திருத்தி."</string>
</resources>
diff --git a/packages/SystemUI/res/values-ta-rIN/strings_tv.xml b/packages/SystemUI/res/values-ta-rIN/strings_tv.xml
index e75d86a..7412e27 100644
--- a/packages/SystemUI/res/values-ta-rIN/strings_tv.xml
+++ b/packages/SystemUI/res/values-ta-rIN/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"PIPஐக் கட்டுப்படுத்த, "<b>"முகப்பைப்"</b>" பிடித்திருக்கவும்"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"PIPஐக் கட்டுப்படுத்த, முகப்புப் பொத்தானை அழுத்திப் பிடிக்கவும்"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"சரி"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"நிராகரி"</string>
</resources>
diff --git a/packages/SystemUI/res/values-te-rIN/strings.xml b/packages/SystemUI/res/values-te-rIN/strings.xml
index 4a6e2d7..3a72809 100644
--- a/packages/SystemUI/res/values-te-rIN/strings.xml
+++ b/packages/SystemUI/res/values-te-rIN/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g>ని తీసివేయండి."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> తీసివేయబడింది."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"అన్ని ఇటీవలి అనువర్తనాలు తీసివేయబడ్డాయి."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g>ని ప్రారంభిస్తోంది."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"నోటిఫికేషన్ తీసివేయబడింది."</string>
@@ -309,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"శోధించు"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g>ని ప్రారంభించడం సాధ్యపడలేదు."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> సురక్షిత-మోడ్లో నిలిపివేయబడింది."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"చరిత్ర"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"తీసివేయి"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"అన్నీ తీసివేయి"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"ఈ అనువర్తనం బహుళ విండోలకు మద్దతు ఇవ్వదు"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"అనువర్తనం బహుళ విండోలకు మద్దతు ఇవ్వదు"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"సమతలంగా విభజించు"</string>
@@ -499,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"ఛార్జ్ అవుతున్న సమయంలో బ్యాటరీ సేవర్ అందుబాటులో ఉండదు"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"బ్యాటరీ సేవర్"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"పనితీరుని మరియు నేపథ్య డేటాను తగ్గిస్తుంది"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"బటన్ <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"వెనుకకు"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"పైకి"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"కిందికి"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"ఎడమ"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"కుడి"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"మధ్య"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"అంతరం"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"ప్లే చేయి/పాజ్ చేయి"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"ఆపివేయి"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"తదుపరి"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"మునుపటి"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"రివైండ్ చేయి"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"వేగంగా ఫార్వార్డ్ చేయి"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"నంబర్ ప్యాడ్ <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"సిస్టమ్"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"హోమ్"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"ఇటీవలివి"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"వెనుకకు"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"నోటిఫికేషన్లు"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"కీబోర్డ్ సత్వరమార్గాలు"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"ఇన్పుట్ పద్ధతిని మార్చండి"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"అనువర్తనాలు"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"సహాయకం"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"బ్రౌజర్"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"పరిచయాలు"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"ఇమెయిల్"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"సంగీతం"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"క్యాలెండర్"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"వాల్యూమ్ నియంత్రణలతో చూపు"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"అంతరాయం కలిగించవద్దు"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"వాల్యూమ్ బటన్ల సత్వరమార్గం"</string>
@@ -557,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"పైకి తరలించు"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"ఎడమవైపుకు తరలించు"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"కుడివైపుకు తరలించు"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"అనువర్తనం బహుళ విండోల రూపంలో పని చేయకపోవచ్చు"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"స్థానం <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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. జోడించడానికి రెండుసార్లు నొక్కండి."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"స్థానం <xliff:g id="POSITION">%1$d</xliff:g>. ఎంచుకోవడానికి రెండుసార్లు నొక్కండి."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"<xliff:g id="TILE_NAME">%1$s</xliff:g>ని తరలిస్తుంది"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"<xliff:g id="TILE_NAME">%1$s</xliff:g>ని తీసివేస్తుంది"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> <xliff:g id="POSITION">%2$d</xliff:g>వ స్థానానికి జోడించబడింది"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> తీసివేయబడింది"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> <xliff:g id="POSITION">%2$d</xliff:g>వ స్థానానికి తరలించబడింది"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"శీఘ్ర సెట్టింగ్ల ఎడిటర్."</string>
</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 33e4346..4844c84 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"ยกเลิก <xliff:g id="APP">%s</xliff:g>"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> ถูกลบไปแล้ว"</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"ปิดแอปพลิเคชันล่าสุดทั้งหมดแล้ว"</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"กำลังเริ่มต้น <xliff:g id="APP">%s</xliff:g>"</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"ปิดการแจ้งเตือนแล้ว"</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"ตำแหน่งที่กำหนดโดย GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"คำขอตำแหน่งที่มีการใช้งาน"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"ล้างการแจ้งเตือนทั้งหมด"</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"การตั้งค่าการแจ้งเตือน"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"การตั้งค่า <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"หน้าจอจะหมุนโดยอัตโนมัติ"</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"ค้นหา"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"ไม่สามารถเริ่มใช้ <xliff:g id="APP">%s</xliff:g>"</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> ปิดใช้ในโหมดปลอดภัย"</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"ประวัติ"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"ล้าง"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"ล้างทั้งหมด"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"แอปนี้ไม่สนับสนุนหลายหน้าต่าง"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"แอปไม่สนับสนุนหลายหน้าต่าง"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"แยกในแนวนอน"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"แสดงที่ด้านบนของรายการการแจ้งเตือน แสดงบนหน้าจอและให้ส่งเสียงได้"</string>
<string name="notification_more_settings" msgid="816306283396553571">"การตั้งค่าเพิ่มเติม"</string>
<string name="notification_done" msgid="5279426047273930175">"เสร็จสิ้น"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"ส่วนควบคุมการแจ้งเตือนของ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"สีและลักษณะที่ปรากฏ"</string>
<string name="night_mode" msgid="3540405868248625488">"โหมดกลางคืน"</string>
<string name="calibrate_display" msgid="5974642573432039217">"ปรับเทียบการแสดงผล"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"ไม่สามารถใช้โหมดประหยัดแบตเตอรี่ระหว่างการชาร์จ"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"โหมดประหยัดแบตเตอรี่"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"ลดประสิทธิภาพการทำงานและข้อมูลแบ็กกราวด์"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"ปุ่ม <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"กลับ"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"ขึ้น"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"ลง"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"ซ้าย"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"ขวา"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"กึ่งกลาง"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"แท็บ"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"วรรค"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"ลบถอยหลัง"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"เล่น/หยุดชั่วคราว"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"หยุด"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"ถัดไป"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"ก่อนหน้า"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"กรอกลับ"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"กรอไปข้างหน้า"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"เลื่อนหน้าขึ้น"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"เลื่อนหน้าลง"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"ลบ"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"แทรก"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"แผงตัวเลข <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"ระบบ"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"หน้าแรก"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"ล่าสุด"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"กลับ"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"การแจ้งเตือน"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"แป้นพิมพ์ลัด"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"สลับวิธีการป้อนข้อมูล"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"แอปพลิเคชัน"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"การสนับสนุน"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"เบราว์เซอร์"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"รายชื่อติดต่อ"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"อีเมล"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"เพลง"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"ปฏิทิน"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"แสดงพร้อมการควบคุมระดับเสียง"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"ห้ามรบกวน"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"ทางลัดปุ่มปรับระดับเสียง"</string>
@@ -540,8 +577,7 @@
<string name="select_keycode" msgid="7413765103381924584">"เลือกปุ่มแป้นพิมพ์"</string>
<string name="preview" msgid="9077832302472282938">"ดูตัวอย่าง"</string>
<string name="drag_to_add_tiles" msgid="7058945779098711293">"ลากเพื่อเพิ่มชิ้นส่วน"</string>
- <!-- no translation found for drag_to_remove_tiles (3361212377437088062) -->
- <skip />
+ <string name="drag_to_remove_tiles" msgid="3361212377437088062">"ลากมาที่นี่เพื่อนำออก"</string>
<string name="qs_edit" msgid="2232596095725105230">"แก้ไข"</string>
<string name="tuner_time" msgid="6572217313285536011">"เวลา"</string>
<string-array name="clock_options">
@@ -560,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"เลื่อนขึ้น"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"เลื่อนไปทางซ้าย"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"เลื่อนไปทางขวา"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"แอปอาจทำงานกับหลายหน้าต่างไม่ได้"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"ตำแหน่ง <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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g> แตะ 2 ครั้งเพื่อเพิ่ม"</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"ตำแหน่ง <xliff:g id="POSITION">%1$d</xliff:g> แตะ 2 ครั้งเพื่อเลือก"</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"ย้าย <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"นำ <xliff:g id="TILE_NAME">%1$s</xliff:g> ออก"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"เพิ่ม <xliff:g id="TILE_NAME">%1$s</xliff:g> ลงในตำแหน่ง <xliff:g id="POSITION">%2$d</xliff:g> แล้ว"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"นำ <xliff:g id="TILE_NAME">%1$s</xliff:g> ออกแล้ว"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"ย้าย <xliff:g id="TILE_NAME">%1$s</xliff:g> ไปยังตำแหน่ง <xliff:g id="POSITION">%2$d</xliff:g> แล้ว"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"ตัวแก้ไขการตั้งค่าด่วน"</string>
</resources>
diff --git a/packages/SystemUI/res/values-th/strings_tv.xml b/packages/SystemUI/res/values-th/strings_tv.xml
index b43078d..d7b26687 100644
--- a/packages/SystemUI/res/values-th/strings_tv.xml
+++ b/packages/SystemUI/res/values-th/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"กด "<b>"HOME"</b>" ค้างไว้เพื่อควบคุม PIP"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"กดปุ่ม HOME ค้างไว้เพื่อควบคุม PIP"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"รับทราบ"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"ปิด"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 61eaa9d..0672959 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"I-dismiss ang <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Hindi pinansin ang <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Na-dismiss ang lahat ng kamakailang application."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Sinisimulan ang <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Na-dismiss ang notification."</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Lokasyong itinatakda ng GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Aktibo ang mga kahilingan ng lokasyon"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"I-clear ang lahat ng notification."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Mga setting ng notification"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Mg setting ng <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Awtomatikong iikot ang screen."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"maghanap"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Hindi masimulan <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"Naka-disable ang <xliff:g id="APP">%s</xliff:g> sa safe-mode."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"History"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"I-clear"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"I-clear lahat"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Hindi sinusuportahan ng app na ito ang multi-window"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Hindi sinusuportahan ng app na ito ang multi-window"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Split Horizontal"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Ipakita sa itaas ng listahan ng mga notification, palitawin sa screen at payagang tumunog"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Higit pang mga setting"</string>
<string name="notification_done" msgid="5279426047273930175">"Tapos Na"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"Mga kontrol sa notification ng <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Kulay at hitsura"</string>
<string name="night_mode" msgid="3540405868248625488">"Night mode"</string>
<string name="calibrate_display" msgid="5974642573432039217">"I-calibrate ang display"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Hindi available ang Pangtipid sa Baterya kapag nagcha-charge"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Pangtipid sa Baterya"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Binabawasan ang pagganap at data sa background"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Button na <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Back"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Up"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Down"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Left"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Right"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Center"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Space"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Play/Pause"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Stop"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Next"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Previous"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Rewind"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Fast Forward"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Numpad <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"System"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Home"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Mga Kamakailang Ginamit"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Bumalik"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Mga Notification"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Mga Keyboard Shortcut"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Magpalit ng pamamaraan ng pag-input"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Mga Application"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Tulong"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Browser"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Mga Contact"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Email"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Music"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalendaryo"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Ipakita nang may mga kontrol ng volume"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Huwag istorbohin"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Shortcut ng mga button ng volume"</string>
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Ilipat pataas"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Ilipat pakaliwa"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Ilipat pakanan"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Maaaring hindi gumana ang app sa maraming window"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. I-double tap upang idagdag."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Posisyon <xliff:g id="POSITION">%1$d</xliff:g>. I-double tap upang piliin."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Ilipat ang <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Alisin ang <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"Idinagdag ang <xliff:g id="TILE_NAME">%1$s</xliff:g> sa posisyon <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"Inalis ang <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"Inilipat ang <xliff:g id="TILE_NAME">%1$s</xliff:g> sa posisyon <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Editor ng Mga mabilisang setting."</string>
</resources>
diff --git a/packages/SystemUI/res/values-tl/strings_tv.xml b/packages/SystemUI/res/values-tl/strings_tv.xml
index 8d4b1b0..74fe314 100644
--- a/packages/SystemUI/res/values-tl/strings_tv.xml
+++ b/packages/SystemUI/res/values-tl/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"I-hold ang "<b>"HOME"</b>" para makontrol ang PIP"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Pindutin nang matagal ang button ng HOME upang makontrol ang PIP"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"OK"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"I-dismiss"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index d5a8b9b..58d488c 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> uygulamasını kapat."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> kaldırıldı."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Tüm son uygulamalar kapatıldı."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> başlatılıyor."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Bildirim kapatıldı."</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Konum GPS ile belirlendi"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Konum bilgisi istekleri etkin"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Tüm bildirimleri temizle"</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Bildirim ayarları"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> ayarları"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Ekran otomatik olarak dönecektir."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"ara"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> başlatılamadı."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g>, güvenli modda devre dışıdır."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Geçmiş"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Sil"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Tümünü temizle"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Bu uygulama, çoklu pencere kullanımını desteklemiyor"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Uygulama, çoklu pencere kullanımını desteklemiyor"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Yatay Ayırma"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Bildirim listesinin üstünde göster, ekrana getir ve sesli bildirime izin ver"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Diğer ayarlar"</string>
<string name="notification_done" msgid="5279426047273930175">"Bitti"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> bildirim denetimleri"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Renk ve görünüm"</string>
<string name="night_mode" msgid="3540405868248625488">"Gece modu"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Ekranı kalibre et"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Şarj sırasında Pil Tasarrufu özelliği kullanılamaz"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Pil Tasarrufu"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Performansı ve arka plan verilerini azaltır"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"<xliff:g id="NAME">%1$s</xliff:g> düğmesi"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Geri"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Yukarı"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Aşağı"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Sol"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Sağ"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Orta"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Sekme"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Boşluk"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Geri tuşu"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Oynat/Duraklat"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Durdur"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Sonraki"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Önceki"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Geri Sar"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"İleri Sar"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Sayfa Yukarı"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Sayfa Aşağı"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"<xliff:g id="NAME">%1$s</xliff:g> (Sayısal Tuş Takımında)"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Sistem"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Ana ekran"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Son çağrılar"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Geri"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Bildirimler"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Klavye Kısayolları"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Giriş yöntemini değiştir"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Uygulamalar"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Asist"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Tarayıcı"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kişiler"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-posta"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Müzik"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Takvim"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Ses seviyesi kontrolleriyle göster"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Rahatsız etmeyin"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Ses düğmeleri kısayolu"</string>
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Yukarı taşı"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Sola taşı"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Sağa taşı"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Uygulama birden fazla pencerede çalışmayabilir"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"<xliff:g id="POSITION">%1$d</xliff:g>. konum, <xliff:g id="TILE_NAME">%2$s</xliff:g>. Düzenlemek için iki kez hafifçe dokunun."</string>
+ <string name="accessibility_qs_edit_add_tile_label" msgid="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Eklemek için iki kez hafifçe dokunun."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"<xliff:g id="POSITION">%1$d</xliff:g>. konum. Seçmek için iki kez hafifçe dokunun."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"<xliff:g id="TILE_NAME">%1$s</xliff:g> kutusunu taşı"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"<xliff:g id="TILE_NAME">%1$s</xliff:g> kutusunu kaldır"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> kutusu <xliff:g id="POSITION">%2$d</xliff:g>. konuma eklendi"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> kaldırıldı"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> <xliff:g id="POSITION">%2$d</xliff:g>. konumuna taşındı"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Hızlı ayar düzenleyicisi."</string>
</resources>
diff --git a/packages/SystemUI/res/values-tr/strings_tv.xml b/packages/SystemUI/res/values-tr/strings_tv.xml
index a5be6a2..57da7fb 100644
--- a/packages/SystemUI/res/values-tr/strings_tv.xml
+++ b/packages/SystemUI/res/values-tr/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"PIP\'yi kontrol etmek için "<b>"ANA EKRAN"</b>"\'ı basılı tutun"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"PIP\'yi kontrol etmek için ANA EKRAN düğmesini basılı tutun"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"Anladım"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Kapat"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 398dea1..c75891c 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -170,6 +170,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Видалити додаток <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Програму <xliff:g id="APP">%s</xliff:g> закрито."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Усі останні додатки закрито."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Запуск додатка <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Сповіщення відхилено."</string>
@@ -311,8 +313,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"пошук"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Не вдалося запустити <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"Додаток <xliff:g id="APP">%s</xliff:g> вимкнено в безпечному режимі."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Історія"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Очистити"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Очистити все"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Цей додаток не підтримує багатоекранний режим"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Додаток не підтримує багатоекранний режим"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Розділити горизонтально"</string>
@@ -501,10 +502,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Режим економії заряду акумулятора недоступний під час заряджання"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Режим економії заряду акумулятора"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Знижується продуктивність і обмежується обмін даними у фоновому режимі"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Кнопка <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Назад"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Стрілка вгору"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Стрілка вниз"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Стрілка вліво"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Стрілка вправо"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Центр"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Пробіл"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Відтворити/призупинити"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Зупинити"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Далі"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Назад"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Перемотати назад"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Перемотати вперед"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Сторінка вгору"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Сторінка вниз"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Numpad <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Система"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Головний екран"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Останні"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Назад"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Сповіщення"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Комбінації клавіш"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Змінити метод введення"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Додатки"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Помічник"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Веб-переглядач"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Контакти"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Електронна пошта"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Чат"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Музика"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Календар"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Показувати регулятори гучності"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Не турбувати"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Кнопки гучності на корпусі"</string>
@@ -559,4 +598,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Перемістити вгору"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Перемістити ліворуч"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Перемістити праворуч"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Додаток може не працювати в багатовіконному режимі"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"Позиція <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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Двічі торкніться, щоб додати."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Позиція <xliff:g id="POSITION">%1$d</xliff:g>. Двічі торкніться, щоб вибрати."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Перемістити <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Видалити <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> додано на позицію <xliff:g id="POSITION">%2$d</xliff:g>."</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> видалено"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> переміщено на позицію <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Редактор швидких налаштувань."</string>
</resources>
diff --git a/packages/SystemUI/res/values-ur-rPK/strings.xml b/packages/SystemUI/res/values-ur-rPK/strings.xml
index 7b5013fe..807a923 100644
--- a/packages/SystemUI/res/values-ur-rPK/strings.xml
+++ b/packages/SystemUI/res/values-ur-rPK/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> کو مسترد کریں۔"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> کو ہٹا دیا گیا۔"</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"سبھی حالیہ ایپلیکیشنز کو برخاست کر دیا گیا۔"</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> شروع ہو رہی ہے۔"</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"اطلاع مسترد ہوگئی۔"</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"مقام متعین کیا گیا بذریعہ GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"مقام کی درخواستیں فعال ہیں"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"سبھی اطلاعات صاف کریں۔"</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"<xliff:g id="NUMBER">%s</xliff:g> +"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"اطلاع کی ترتیبات"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> ترتیبات"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"اسکرین خود بخود گردش کرے گی۔"</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"تلاش کریں"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> کو شروع نہیں کیا جا سکا۔"</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"محفوظ موڈ میں <xliff:g id="APP">%s</xliff:g> غیر فعال ہوتی ہے۔"</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"سرگزشت"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"صاف کریں"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"سبھی کو صاف کریں"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"یہ ایپ ملٹی ونڈو کی معاونت نہیں کرتی"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"ایپ ملٹی ونڈز کی معاونت نہیں کرتی"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"بلحاظ افقی الگ کریں"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"اطلاعات کی فہرست پر سب سے اوپر دکھائیں، اسکرین پر دکھائیں اور آواز کی اجازت دیں"</string>
<string name="notification_more_settings" msgid="816306283396553571">"مزید ترتیبات"</string>
<string name="notification_done" msgid="5279426047273930175">"ہوگیا"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> کے نوٹیفکیشن کنٹرولز"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"رنگ اور ظہور"</string>
<string name="night_mode" msgid="3540405868248625488">"رات موڈ"</string>
<string name="calibrate_display" msgid="5974642573432039217">"نشان زد ڈسپلے"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"چارجنگ کے دوران بیٹری سیور دستیاب نہیں ہے"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"بیٹری سیور"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"کارکردگی اور پس منظر کا ڈیٹا کم کر دیتا ہے"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"بٹن <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"پیچھے"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"اوپر"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"نیچے"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"بائیں"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"دائیں"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"سینٹر"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Space"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"چلائیں/موقوف کریں"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"روکیں"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"اگلا"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"گزشتہ"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"ریوائینڈ کریں"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"تیزی سے فارورڈ کریں"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"نمبر پیڈ <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"سسٹم"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"ہوم"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"حالیہ"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"پیچھے"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"اطلاعات"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"کی بورڈ شارٹ کٹس"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"اندراج کا طریقہ سوئچ کریں"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"ایپلیکیشنز"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"اسسٹ"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"براؤزر"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"رابطے"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"ای میل"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"موسیقی"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"کیلنڈر"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"والیوم کنٹرولز کے ساتھ دکھائیں"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"ڈسٹرب نہ کریں"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"والیوم بٹنز کے شارٹ کٹ"</string>
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"اوپر منتقل کریں"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"بائیں منتقل کریں"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"دائیں منتقل کریں"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"ایپ شاید ملٹی ونڈو کے ساتھ کام نہ کرے"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"پوزیشن <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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>۔ شامل کرنے کیلئے دو بار تھپتھپائیں۔"</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"پوزیشن <xliff:g id="POSITION">%1$d</xliff:g>۔ منتخب کرنے کیلئے دو بار تھپتھپائیں۔"</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"<xliff:g id="TILE_NAME">%1$s</xliff:g> کو منتقل کریں"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ہٹائیں"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="POSITION">%2$d</xliff:g> پوزیشن پر <xliff:g id="TILE_NAME">%1$s</xliff:g> شامل ہو گیا ہے"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ہٹا دیا گیا"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="POSITION">%2$d</xliff:g> پوزیشن پر <xliff:g id="TILE_NAME">%1$s</xliff:g> منتقل ہو گیا ہے"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"فوری ترتیبات کا ایڈیٹر۔"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ur-rPK/strings_tv.xml b/packages/SystemUI/res/values-ur-rPK/strings_tv.xml
index aff14b2..78de898 100644
--- a/packages/SystemUI/res/values-ur-rPK/strings_tv.xml
+++ b/packages/SystemUI/res/values-ur-rPK/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"PIP کنٹرول کرنے کیلئے "<b>"ہوم"</b>" پکڑے رکھیں"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"PIP کنٹرول کرنے کیلئے ہوم بٹن دبائیں اور پکڑے رکھیں"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"سمجھ آ گئی"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"برخاست کریں"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uz-rUZ/strings.xml b/packages/SystemUI/res/values-uz-rUZ/strings.xml
index bd606c2..fe67b52 100644
--- a/packages/SystemUI/res/values-uz-rUZ/strings.xml
+++ b/packages/SystemUI/res/values-uz-rUZ/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Olib tashlash: <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> olib tashlangan."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Yaqinda ishlatilgan barcha ilovalar olib tashlandi."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> ishga tushirilmoqda."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Xabarnoma e‘tiborsiz qoldirildi."</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"GPS yordamida manzilni o‘rnatish"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Joylashuv so‘rovlari yoniq"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Barcha eslatmalarni tozalash."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Bildirishnoma sozlamalari"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> sozlamalari"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Ekran avtomatik buriladi."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"qidirish"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"“<xliff:g id="APP">%s</xliff:g>” ilovasini ishga tushirib bo‘lmadi."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"Xavfsiz rejimda <xliff:g id="APP">%s</xliff:g> ilovasi o‘chirib qo‘yildi."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Jurnal"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Tozalash"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Hammasini tozalash"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Bu ilova ko‘p oynali rejimni qo‘llab-quvvatlamaydi"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Bu ilova ko‘p oynali rejimni qo‘llab-quvvatlamaydi"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Gorizontal yo‘nalishda bo‘lish"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Bildirishnomalar ro‘yxatining boshida va barcha oynalar ustida ovoz bilan ko‘rsatilsin"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Boshqa sozlamalar"</string>
<string name="notification_done" msgid="5279426047273930175">"Tayyor"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> bildirishnomalarini boshqarish"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Rang va ko‘rinishi"</string>
<string name="night_mode" msgid="3540405868248625488">"Tungi rejim"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Ekranni kalibrlash"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Quvvat tejash rejimidan quvvatlash vaqtida foydalanib bo‘lmaydi"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Quvvat tejash rejimi"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Unumdorlik pasayadi va fonda internetdan foydalanish cheklanadi"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"<xliff:g id="NAME">%1$s</xliff:g> tugmasi"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Bosh ekran"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Orqaga"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Tepaga qaragan ko‘rsatkichli chiziq"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Pastga qaragan ko‘rsatkichli chiziq"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Chapga qaragan ko‘rsatkichli chiziq"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"O‘ngga qaragan ko‘rsatkichli chiziq"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Markaziy ko‘rsatkichli chiziq"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Bo‘sh joy"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Ijro/Pauza"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"To‘xtatish"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Keyingi"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Avvalgi"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Orqaga qaytarish"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Oldinga o‘tkazish"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Raqamli klaviatura (<xliff:g id="NAME">%1$s</xliff:g>)"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Tizim"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Bosh ekran"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"So‘nggi ishlatilganlar"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Orqaga"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Bildirishnomalar"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Tezkor tugmalar"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Matn kiritish usulini o‘zgartirish"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Ilovalar"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Yordamchi"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Brauzer"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontaktlar"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-pochta"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Musiqa"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Taqvim"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Ovoz balandligini boshqarish tugmalari bilan ko‘rsatish"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Bezovta qilinmasin"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Ovoz balandligini boshqarish tugmalari"</string>
@@ -540,8 +577,7 @@
<string name="select_keycode" msgid="7413765103381924584">"Klaviatura tugmasini tanlang"</string>
<string name="preview" msgid="9077832302472282938">"Oldindan ko‘rish"</string>
<string name="drag_to_add_tiles" msgid="7058945779098711293">"Fragmentlar qo‘shish uchun torting"</string>
- <!-- no translation found for drag_to_remove_tiles (3361212377437088062) -->
- <skip />
+ <string name="drag_to_remove_tiles" msgid="3361212377437088062">"O‘chirish uchun bu yerga torting"</string>
<string name="qs_edit" msgid="2232596095725105230">"Tahrirlash"</string>
<string name="tuner_time" msgid="6572217313285536011">"Vaqt"</string>
<string-array name="clock_options">
@@ -560,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Tepaga siljitish"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Chapga siljitish"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"O‘ngga siljitish"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Ilova ko‘p oynali rejimni qo‘llab-quvvatlamaydi"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"<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="8133209638023882667">"“<xliff:g id="TILE_NAME">%1$s</xliff:g>” tugmasi. Qo‘shish uchun ustiga ikki marta bosing."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Joylashuv: <xliff:g id="POSITION">%1$d</xliff:g>. Belgilash uchun ustiga ikki marta bosing."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"“<xliff:g id="TILE_NAME">%1$s</xliff:g>” tugmasini ko‘chirish"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"“<xliff:g id="TILE_NAME">%1$s</xliff:g>” tugmasini o‘chirish"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"“<xliff:g id="TILE_NAME">%1$s</xliff:g>” tugmasi endi <xliff:g id="POSITION">%2$d</xliff:g>-joyni egallamoqda"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"“<xliff:g id="TILE_NAME">%1$s</xliff:g>” tugmasi o‘chirildi"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"“<xliff:g id="TILE_NAME">%1$s</xliff:g>” tugmasi endi <xliff:g id="POSITION">%2$d</xliff:g>-joyni egallanmoqda"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Tezkor sozlamalar muharriri"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uz-rUZ/strings_tv.xml b/packages/SystemUI/res/values-uz-rUZ/strings_tv.xml
index 3a4d176..9300aaa 100644
--- a/packages/SystemUI/res/values-uz-rUZ/strings_tv.xml
+++ b/packages/SystemUI/res/values-uz-rUZ/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"“Kadr ichida kadr” rejimini boshqarish uchun "<b>"BOSHI"</b>" tugmasini bosib turing"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"“Kadr ichida kadr” rejimini boshqarish uchun BOSHIGA tugmasini bosib turing"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"OK"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Yopish"</string>
</resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index edd0a8b..ca7c59b 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Xóa bỏ <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> đã bị loại bỏ."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Đã bỏ qua tất cả các ứng dụng gần đây."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Bắt đầu <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Đã loại bỏ thông báo."</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Vị trí đặt bởi GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Yêu cầu về thông tin vị trí đang hoạt động"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Xóa tất cả thông báo."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Cài đặt thông báo"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Cài đặt <xliff:g id="APP_NAME">%s</xliff:g>"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Màn hình sẽ xoay tự động."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"tìm kiếm"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Không thể khởi động <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> bị tắt ở chế độ an toàn."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Lịch sử"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Xóa"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Xóa tất cả"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Ứng dụng này không hỗ trợ chế độ nhiều cửa sổ"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Ứng dụng không hỗ trợ chế độ nhiều cửa sổ"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Phân tách ngang"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Hiển thị ở đầu danh sách thông báo, hiển thị trên màn hình và phát ra âm thanh"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Cài đặt khác"</string>
<string name="notification_done" msgid="5279426047273930175">"Xong"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"Điều khiển thông báo <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Màu sắc và giao diện"</string>
<string name="night_mode" msgid="3540405868248625488">"Chế độ ban đêm"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Hiệu chỉnh hiển thị"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Trình tiết kiệm pin không khả dụng trong khi sạc"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Trình tiết kiệm pin"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Giảm hiệu suất và dữ liệu nền"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Nút <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Quay lại"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Lên"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Xuống"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Trái"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Phải"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Giữa"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Dấu cách"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Phát/Tạm dừng"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Dừng"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Tiếp theo"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Trước"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Tua lại"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Tua nhanh"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"Cuối"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Bàn phím số <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Hệ thống"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Màn hình chính"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Gần đây"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Quay lại"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Thông báo"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Phím tắt"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Chuyển phương thức nhập"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Ứng dụng"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Trợ lý"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Trình duyệt"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Danh bạ"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Email"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Nhắn tin nhanh"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Âm nhạc"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Lịch"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Hiển thị với các điều khiển âm lượng"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Không làm phiền"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Phím tắt các nút âm lượng"</string>
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Chuyển lên"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Di chuyển sang trái"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Di chuyển sang phải"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Ứng dụng có thể không hoạt động với nhiều cửa sổ"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Nhấn đúp để thêm."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Vị trí <xliff:g id="POSITION">%1$d</xliff:g>. Nhấn đúp để chọn."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Di chuyển <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Xóa <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> được thêm vào vị trí <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> được di chuyển"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> được di chuyển sang vị trí <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Trình chỉnh sửa cài đặt nhanh."</string>
</resources>
diff --git a/packages/SystemUI/res/values-vi/strings_tv.xml b/packages/SystemUI/res/values-vi/strings_tv.xml
index 7a5e34a..b781503 100644
--- a/packages/SystemUI/res/values-vi/strings_tv.xml
+++ b/packages/SystemUI/res/values-vi/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"Giữ "<b>"HOME"</b>" để đ.khiển PIP"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Bấm và giữ nút HOME để điều khiển PIP"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"OK"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Loại bỏ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 23fb020..a25b9f1 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -34,14 +34,14 @@
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"通知"</string>
<string name="battery_low_title" msgid="6456385927409742437">"电池电量偏低"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"剩余<xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
- <string name="battery_low_percent_format_saver_started" msgid="6859235584035338833">"剩余<xliff:g id="PERCENTAGE">%s</xliff:g>。节电助手已开启。"</string>
+ <string name="battery_low_percent_format_saver_started" msgid="6859235584035338833">"剩余<xliff:g id="PERCENTAGE">%s</xliff:g>。省电模式已开启。"</string>
<string name="invalid_charger" msgid="4549105996740522523">"不支持USB充电功能。\n只能使用随附的充电器充电。"</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"不支持USB充电。"</string>
<string name="invalid_charger_text" msgid="5474997287953892710">"仅限使用设备随附的充电器。"</string>
<string name="battery_low_why" msgid="4553600287639198111">"设置"</string>
- <string name="battery_saver_confirmation_title" msgid="5299585433050361634">"要开启节电助手吗?"</string>
+ <string name="battery_saver_confirmation_title" msgid="5299585433050361634">"要开启省电模式吗?"</string>
<string name="battery_saver_confirmation_ok" msgid="7507968430447930257">"开启"</string>
- <string name="battery_saver_start_action" msgid="5576697451677486320">"开启节电助手"</string>
+ <string name="battery_saver_start_action" msgid="5576697451677486320">"开启省电模式"</string>
<string name="status_bar_settings_settings_button" msgid="3023889916699270224">"设置"</string>
<string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"WLAN"</string>
<string name="status_bar_settings_auto_rotation" msgid="3790482541357798421">"自动旋转屏幕"</string>
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"移除<xliff:g id="APP">%s</xliff:g>。"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"已删除<xliff:g id="APP">%s</xliff:g>"</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"已关闭所有最近用过的应用。"</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"正在启动<xliff:g id="APP">%s</xliff:g>。"</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"已关闭通知。"</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"已通过GPS确定位置"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"应用发出了有效位置信息请求"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"清除所有通知。"</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"通知设置"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g>设置"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"屏幕会自动旋转。"</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"搜索"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"无法启动<xliff:g id="APP">%s</xliff:g>。"</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g>已在安全模式下停用。"</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"历史记录"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"清除"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"全部清除"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"此应用不支持多窗口模式"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"应用不支持多窗口模式"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"水平分割"</string>
@@ -373,9 +373,9 @@
<string name="user_remove_user_title" msgid="4681256956076895559">"是否移除用户?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"此用户的所有应用和数据均将被删除。"</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"移除"</string>
- <string name="battery_saver_notification_title" msgid="237918726750955859">"节电助手已开启"</string>
+ <string name="battery_saver_notification_title" msgid="237918726750955859">"省电模式已开启"</string>
<string name="battery_saver_notification_text" msgid="820318788126672692">"降低性能并限制后台流量"</string>
- <string name="battery_saver_notification_action_text" msgid="109158658238110382">"关闭节电助手"</string>
+ <string name="battery_saver_notification_action_text" msgid="109158658238110382">"关闭省电模式"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>将开始截取您的屏幕上显示的所有内容。"</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"不再显示"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"全部清除"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"在通知列表顶部显示,同时在屏幕上短暂显示,并允许发出提示音"</string>
<string name="notification_more_settings" msgid="816306283396553571">"更多设置"</string>
<string name="notification_done" msgid="5279426047273930175">"完成"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g>通知设置"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"颜色和外观"</string>
<string name="night_mode" msgid="3540405868248625488">"夜间模式"</string>
<string name="calibrate_display" msgid="5974642573432039217">"校准显示画面"</string>
@@ -498,13 +497,51 @@
<string name="color_revert_title" msgid="4746666545480534663">"确认设置"</string>
<string name="color_revert_message" msgid="9116001069397996691">"部分颜色设置可能会导致此设备无法使用。请点击“确定”确认这些颜色设置,否则,系统将在 10 秒后重置这些设置。"</string>
<string name="battery_panel_title" msgid="7944156115535366613">"电池使用情况"</string>
- <string name="battery_detail_charging_summary" msgid="1279095653533044008">"充电过程中无法使用节电助手"</string>
- <string name="battery_detail_switch_title" msgid="6285872470260795421">"节电助手"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"充电过程中无法使用省电模式"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"省电模式"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"降低性能并限制后台流量"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"<xliff:g id="NAME">%1$s</xliff:g>按钮"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"返回"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"向上"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"向下"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"向左"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"向右"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"中心"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"空格"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"退格"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"播放/暂停"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"停止"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"下一曲"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"上一曲"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"快退"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"快进"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"向上翻页"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"向下翻页"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"删除"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"数字键盘 <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"系统"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"主屏幕"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"最近"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"返回"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"通知"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"键盘快捷键"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"切换输入法"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"应用"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"辅助应用"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"浏览器"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"通讯录"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"电子邮件"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"即时通讯"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"音乐"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"日历"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"与音量控件一起显示"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"请勿打扰"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"音量按钮快捷键"</string>
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"上移"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"左移"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"右移"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"应用可能无法在多窗口模式下正常运行"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"位置 <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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>。点按两次即可添加。"</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"位置 <xliff:g id="POSITION">%1$d</xliff:g>。点按两次即可选择。"</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"移动<xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"移除<xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"已将<xliff:g id="TILE_NAME">%1$s</xliff:g>添加到位置 <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"已移除<xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"已将<xliff:g id="TILE_NAME">%1$s</xliff:g>移至位置 <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"快捷设置编辑器。"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings_tv.xml b/packages/SystemUI/res/values-zh-rCN/strings_tv.xml
index 2bfe478..77d3bff 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings_tv.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"按住"<b>"主屏幕"</b>"按钮即可控制画中画功能"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"按住主屏幕按钮即可控制画中画功能"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"知道了"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"关闭"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 902cf71..083cd5e 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"關閉「<xliff:g id="APP">%s</xliff:g>」。"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"「<xliff:g id="APP">%s</xliff:g>」已關閉。"</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"所有最近使用的應用程式均已關閉。"</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"正在啟動「<xliff:g id="APP">%s</xliff:g>」。"</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g><xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"通知已關閉。"</string>
@@ -309,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"搜尋"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"無法啟動「<xliff:g id="APP">%s</xliff:g>」。"</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"「<xliff:g id="APP">%s</xliff:g>」已在安全模式中停用。"</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"記錄"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"清除"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"全部清除"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"此應用程式不支援多視窗模式"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"應用程式不支援多視窗模式"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"水平分割"</string>
@@ -499,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"充電時無法使用「省電模式」"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"省電模式"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"降低效能並限制背景數據傳輸"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"<xliff:g id="NAME">%1$s</xliff:g> 鍵"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"返回"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"向上"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"向下"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"向左"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"向右"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"箭咀中央"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"空格"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"輸入 (Enter)"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"播放/暫停"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"停止"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"下一首"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"上一首"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"倒帶"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"向前快轉"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"上一頁"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"下一頁"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"刪除"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"插入"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"數字鎖定"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"數字鍵盤 <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"系統"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"主畫面"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"最近的活動"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"返回"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"通知"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"鍵盤快速鍵"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"切換輸入法"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"應用程式"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"輔助"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"瀏覽器"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"通訊錄"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"電郵"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"即時通訊"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"音樂"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"日曆"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"與音量控制一起顯示"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"請勿騷擾"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"音量按鈕快速鍵"</string>
@@ -557,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"向上移"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"向左移"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"向右移"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"應用程式可能無法在多重視窗下運作"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"位置 <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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>。輕按兩下即可新增。"</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"位置 <xliff:g id="POSITION">%1$d</xliff:g>。輕按兩下即可選取。"</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"移動 <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"移除 <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> 已新增至位置 <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> 已移除"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> 已移至位置 <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"快速設定編輯工具。"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index ea32930..3c95a93 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"關閉「<xliff:g id="APP">%s</xliff:g>」。"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"「<xliff:g id="APP">%s</xliff:g>」已關閉。"</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"最近使用的應用程式已全部關閉。"</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"正在啟動「<xliff:g id="APP">%s</xliff:g>」。"</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"已關閉通知。"</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"GPS 已定位"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"有位置資訊要求"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"清除所有通知。"</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"通知設定"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g>設定"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"螢幕會自動旋轉。"</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"搜尋"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"無法啟動「<xliff:g id="APP">%s</xliff:g>」。"</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"「<xliff:g id="APP">%s</xliff:g>」在安全模式中為停用狀態。"</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"紀錄"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"清除"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"全部清除"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"這個應用程式不支援多視窗模式"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"應用程式不支援多視窗模式"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"水平分割"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"顯示在通知清單頂端,同時短暫顯示在畫面上並發出音效"</string>
<string name="notification_more_settings" msgid="816306283396553571">"更多設定"</string>
<string name="notification_done" msgid="5279426047273930175">"完成"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」通知控制項"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"顏色和外觀"</string>
<string name="night_mode" msgid="3540405868248625488">"夜間模式"</string>
<string name="calibrate_display" msgid="5974642573432039217">"校正顯示畫面"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"充電時無法使用節約耗電量模式"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"節約耗電量"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"降低效能並限制背景資料傳輸"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"<xliff:g id="NAME">%1$s</xliff:g> 按鈕"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Home 鍵"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"返回"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"向上鍵"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"向下鍵"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"向左鍵"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"向右鍵"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"中央鍵"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Tab 鍵"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"空格鍵"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Enter 鍵"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Backspace 鍵"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"播放/暫停"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"停止"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"下一個"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"上一個"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"倒轉"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"快轉"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Page Up 鍵"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Page Down 鍵"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Delete 鍵"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Home 鍵"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"End 鍵"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Insert 鍵"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock 鍵"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"數字鍵 <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"系統"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"主畫面"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"近期活動"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"返回"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"通知"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"鍵盤快速鍵"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"切換輸入法"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"應用程式"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"小幫手"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"瀏覽器"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"聯絡人"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"電子郵件"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"即時訊息"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"音樂"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"日曆"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"與音量控制項一起顯示"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"零打擾"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"音量按鈕快速鍵"</string>
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"向上移"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"向左移"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"向右移"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"應用程式可能無法在多視窗模式下運作"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"位置 <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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>。輕按兩下即可新增。"</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"位置 <xliff:g id="POSITION">%1$d</xliff:g>。輕按兩下即可選取。"</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"移動 <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"移除 <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"已將 <xliff:g id="TILE_NAME">%1$s</xliff:g> 新增到位置 <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"已移除 <xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"已將 <xliff:g id="TILE_NAME">%1$s</xliff:g> 移到位置 <xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"快速設定編輯器。"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings_tv.xml b/packages/SystemUI/res/values-zh-rTW/strings_tv.xml
index a6744e7..4420d87 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings_tv.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"按住「主畫面」"<b></b>"按鈕即可控制子母畫面"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"按住「主畫面」按鈕即可控制子母畫面"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"我知道了"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"關閉"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 9f8f298..e03e642 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -168,6 +168,8 @@
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Cashisa i-<xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> ivaliwe."</string>
<string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Zonke izinhlelo zokusebenza zakamuva zicashisiwe."</string>
+ <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
+ <skip />
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Iqala i-<xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Isaziso sichithiwe."</string>
@@ -236,8 +238,7 @@
<string name="gps_notification_found_text" msgid="4619274244146446464">"Indawo ihlelwe i-GPS"</string>
<string name="accessibility_location_active" msgid="2427290146138169014">"Izicelo zendawo ziyasebenza"</string>
<string name="accessibility_clear_all" msgid="5235938559247164925">"Susa zonke izaziso."</string>
- <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
- <skip />
+ <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Izilungiselelo zesaziso"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> izilungiselelo"</string>
<string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Isikrini sizophenduka ngokuzenzakalela."</string>
@@ -310,8 +311,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"sesha"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Ayikwazanga ukuqala i-<xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"I-<xliff:g id="APP">%s</xliff:g> ikhutshaziwe kumodi yokuphepha."</string>
- <string name="recents_history_button_label" msgid="5153358867807604821">"Umlando"</string>
- <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Sula"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Sula konke"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Lolu hlelo lokusebenza alusekeli amawindi amaningi"</string>
<string name="recents_launch_non_dockable_task_label" msgid="7862379814938391888">"Uhlelo lokusebenza alusekeli amawindi amaningi"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Hlukanisa okuvundlile"</string>
@@ -480,8 +480,7 @@
<string name="notification_importance_max" msgid="5806278962376556491">"Bonisa phezulu kohlu lwezaziso, beka phezu kwesikrini futhi uvumele umsindo"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Izilungiselelo eziningi"</string>
<string name="notification_done" msgid="5279426047273930175">"Kwenziwe"</string>
- <!-- no translation found for notification_gear_accessibility (94429150213089611) -->
- <skip />
+ <string name="notification_gear_accessibility" msgid="94429150213089611">"<xliff:g id="APP_NAME">%1$s</xliff:g> izilawuli zasaziso"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Umbala nokubonakala"</string>
<string name="night_mode" msgid="3540405868248625488">"Imodi yasebusuku"</string>
<string name="calibrate_display" msgid="5974642573432039217">"Sika isibonisi"</string>
@@ -501,10 +500,48 @@
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"Isilondolozi sebhethri asitholakali ngesikhathi sokushaja"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"Isilondolozi sebhethri"</string>
<string name="battery_detail_switch_summary" msgid="9049111149407626804">"Sehlisa ukusebenza nedatha yasemuva"</string>
+ <string name="keyboard_key_button_template" msgid="6230056639734377300">"Inkinobho <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="keyboard_key_home" msgid="2243500072071305073">"Ekhaya"</string>
+ <string name="keyboard_key_back" msgid="2337450286042721351">"Emuva"</string>
+ <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"Phezulu"</string>
+ <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"Phansi"</string>
+ <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"Kwesobunxele"</string>
+ <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"Kwesokudla"</string>
+ <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"Maphakathi"</string>
+ <string name="keyboard_key_tab" msgid="3871485650463164476">"Ithebhu"</string>
+ <string name="keyboard_key_space" msgid="2499861316311153293">"Isikhala"</string>
+ <string name="keyboard_key_enter" msgid="5739632123216118137">"Faka"</string>
+ <string name="keyboard_key_backspace" msgid="1559580097512385854">"Isikhala"</string>
+ <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"Dlala/Misa okwesikhashana"</string>
+ <string name="keyboard_key_media_stop" msgid="2859963958595908962">"Misa"</string>
+ <string name="keyboard_key_media_next" msgid="1894394911630345607">"Okulandelayo"</string>
+ <string name="keyboard_key_media_previous" msgid="4256072387192967261">"Okwangaphambilini"</string>
+ <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"Buyisela emuva"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"Iya phambili ngokushesha"</string>
+ <string name="keyboard_key_page_up" msgid="5654098530106845603">"Ikhasi phezulu"</string>
+ <string name="keyboard_key_page_down" msgid="8720502083731906136">"Ikhasi phansi"</string>
+ <string name="keyboard_key_forward_del" msgid="1391451334716490176">"Susa"</string>
+ <string name="keyboard_key_move_home" msgid="2765693292069487486">"Ekhaya"</string>
+ <string name="keyboard_key_move_end" msgid="5901174332047975247">"Phelisa"</string>
+ <string name="keyboard_key_insert" msgid="8530501581636082614">"Faka"</string>
+ <string name="keyboard_key_num_lock" msgid="5052537581246772117">"Izinombolo"</string>
+ <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"Phedi yezinombolo <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"Isistimu"</string>
<string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Ekhaya"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Okwakamuva"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Emuva"</string>
+ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Izaziso"</string>
+ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Izinqamulelo Zekhibhodi"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Shintsha indlela yokufaka"</string>
+ <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Izinhlelo zokusebenza"</string>
+ <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Siza"</string>
+ <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Isiphequluli"</string>
+ <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Oxhumana nabo"</string>
+ <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"I-imeyili"</string>
+ <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"I-IM"</string>
+ <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Umculo"</string>
+ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"I-YouTube"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Ikhalenda"</string>
<string name="tuner_full_zen_title" msgid="4540823317772234308">"Bonisa ngezilawuli zevolomu"</string>
<string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Ungaphazamisi"</string>
<string name="volume_dnd_silent" msgid="4363882330723050727">"Izinqamuleli zezinkinobho zevolomu"</string>
@@ -559,4 +596,14 @@
<string name="accessibility_action_divider_move_up" msgid="4580103171609248006">"Iya phezulu"</string>
<string name="accessibility_action_divider_move_left" msgid="9218189832115847253">"Iya kwesokunxele"</string>
<string name="accessibility_action_divider_move_right" msgid="4671522715182567972">"Iya kwesokudla"</string>
+ <string name="forced_resizable_info_text" msgid="7591061837558867999">"Uhlelo lokusebenza kungenzeka lungasebenzi namawindi amaningi"</string>
+ <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"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="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>. Thepha kabili ukuze ungeze."</string>
+ <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"Isimo esingu-<xliff:g id="POSITION">%1$d</xliff:g>. Thepha kabili ukuze ukhethe."</string>
+ <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"Hambisa i-<xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"Susa i-<xliff:g id="TILE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"I-<xliff:g id="TILE_NAME">%1$s</xliff:g> ingezwe kusimo esingu-<xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"I-<xliff:g id="TILE_NAME">%1$s</xliff:g> isusiwe"</string>
+ <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"I-<xliff:g id="TILE_NAME">%1$s</xliff:g> ihanjiswe kusimo esingu-<xliff:g id="POSITION">%2$d</xliff:g>"</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Isihleli sezilungiselelo ezisheshayo."</string>
</resources>
diff --git a/packages/SystemUI/res/values-zu/strings_tv.xml b/packages/SystemUI/res/values-zu/strings_tv.xml
index 5fb063d..1904237 100644
--- a/packages/SystemUI/res/values-zu/strings_tv.xml
+++ b/packages/SystemUI/res/values-zu/strings_tv.xml
@@ -26,6 +26,5 @@
<string name="pip_hold_home" msgid="340086535668778109">"Bamba "<b>"IKHAYA"</b>" ukuze ulawule i-PIP"</string>
<string name="pip_onboarding_description" msgid="2882896641362814195">"Cindezela futhi ubambe inkinobho EKHAYA ukuze ulawule i-PIP"</string>
<string name="pip_onboarding_button" msgid="3957426748484904611">"Ngiyezwa"</string>
- <!-- no translation found for recents_tv_dismiss (3555093879593377731) -->
- <skip />
+ <string name="recents_tv_dismiss" msgid="3555093879593377731">"Cashisa"</string>
</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 4f3db05..b874e7c 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -151,6 +151,7 @@
<color name="docked_divider_background">#ff000000</color>
<color name="docked_divider_handle">#ffffff</color>
+ <drawable name="forced_resizable_background">#80000000</drawable>
<color name="default_remote_input_background">@*android:color/notification_default_color</color>
<color name="remote_input_hint">#99ffffff</color>
@@ -168,6 +169,8 @@
<color name="ksh_system_group_color">#ff00bcd4</color>
<color name="ksh_application_group_color">#fff44336</color>
<color name="ksh_keyword_color">#d9000000</color>
+ <color name="ksh_key_item_color">@color/material_grey_600</color>
+ <color name="ksh_key_item_background">#eeeeee</color>
<!-- Background color of edit overflow -->
<color name="qs_edit_overflow_bg">#455A64</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 622ae71..6ce2a5d 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -153,6 +153,9 @@
<!-- The animation duration for animating the removal of a task view. -->
<integer name="recents_animate_task_view_remove_duration">175</integer>
+ <!-- The base animation duration for animating the removal of all task views. -->
+ <integer name="recents_animate_task_views_remove_all_duration">300</integer>
+
<!-- The animation duration for scrolling the stack to a particular item. -->
<integer name="recents_animate_task_stack_scroll_duration">200</integer>
@@ -175,7 +178,7 @@
<!-- Recents: The relative range of visible tasks from the current scroll position
while the stack is focused. -->
<item name="recents_layout_focused_range_min" format="float" type="integer">-3</item>
- <item name="recents_layout_focused_range_max" format="float" type="integer">3</item>
+ <item name="recents_layout_focused_range_max" format="float" type="integer">2</item>
<!-- Recents: The relative range of visible tasks from the current scroll position
while the stack is not focused. -->
diff --git a/packages/SystemUI/res/values/config_tv.xml b/packages/SystemUI/res/values/config_tv.xml
new file mode 100644
index 0000000..22b7578
--- /dev/null
+++ b/packages/SystemUI/res/values/config_tv.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows,
+ when the PIP menu is shown in center. -->
+ <string translatable="false" name="pip_menu_bounds">"596 280 1324 690"</string>
+
+ <!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows,
+ when the PIP is shown in Recents without focus. -->
+ <string translatable="false" name="pip_recents_bounds">"800 54 1120 234"</string>
+
+ <!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows,
+ when the PIP is shown in Recents with focus. -->
+ <string translatable="false" name="pip_recents_focused_bounds">"775 54 1145 262"</string>
+</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 5417480..75e57b0 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -179,7 +179,8 @@
<dimen name="qs_date_collapsed_text_size">14sp</dimen>
<dimen name="qs_date_text_size">16sp</dimen>
<dimen name="qs_header_gear_translation">16dp</dimen>
- <dimen name="qs_page_indicator_size">12dp</dimen>
+ <dimen name="qs_page_indicator_width">16dp</dimen>
+ <dimen name="qs_page_indicator_height">8dp</dimen>
<dimen name="qs_tile_icon_size">24dp</dimen>
<dimen name="qs_tile_text_size">12sp</dimen>
<dimen name="qs_tile_divider_height">1dp</dimen>
@@ -560,6 +561,9 @@
<!-- Keyboard shortcuts helper -->
<dimen name="ksh_layout_width">@dimen/match_parent</dimen>
+ <dimen name="ksh_item_text_size">14sp</dimen>
+ <dimen name="ksh_item_padding">4dp</dimen>
+ <dimen name="ksh_item_margin_start">4dp</dimen>
<!-- Recents Layout -->
@@ -573,10 +577,12 @@
<dimen name="recents_layout_bottom_margin">16dp</dimen>
<dimen name="recents_layout_side_margin_phone">16dp</dimen>
<dimen name="recents_layout_side_margin_tablet">48dp</dimen>
+ <dimen name="recents_layout_side_margin_tablet_docked">16dp</dimen>
<dimen name="recents_layout_side_margin_tablet_xlarge">64dp</dimen>
+ <dimen name="recents_layout_side_margin_tablet_xlarge_docked">16dp</dimen>
<!-- The height between the top margin and the top of the focused task. -->
- <dimen name="recents_layout_top_peek_size">56dp</dimen>
+ <dimen name="recents_layout_top_peek_size">48dp</dimen>
<!-- The height between the bottom margin and the top of task in front of the focused task. -->
<dimen name="recents_layout_bottom_peek_size">56dp</dimen>
@@ -604,16 +610,14 @@
<!-- Recents Views -->
- <!-- The height of a task view bar. -->
+ <!-- The height of a task view bar. This has to be large enough to cover the action bar
+ height in either orientation at this smallest width. -->
<dimen name="recents_task_view_header_height">56dp</dimen>
+ <dimen name="recents_task_view_header_height_tablet_land">64dp</dimen>
- <!-- The size of the icon in the recents task view header. -->
- <dimen name="recents_task_view_header_icon_width">@dimen/recents_task_view_header_height</dimen>
- <dimen name="recents_task_view_header_icon_height">@dimen/recents_task_view_header_height</dimen>
-
- <!-- The size of a button in the recents task view header. -->
- <dimen name="recents_task_view_header_button_width">@dimen/recents_task_view_header_height</dimen>
- <dimen name="recents_task_view_header_button_height">@dimen/recents_task_view_header_height</dimen>
+ <!-- The padding of a button in the recents task view header. -->
+ <dimen name="recents_task_view_header_button_padding">16dp</dimen>
+ <dimen name="recents_task_view_header_button_padding_tablet_land">20dp</dimen>
<!-- The radius of the rounded corners on a task view and its shadow (which can be larger
to create a softer corner effect. -->
diff --git a/packages/SystemUI/res/values/dimens_tv.xml b/packages/SystemUI/res/values/dimens_tv.xml
index 953dd65..4e6a4b1 100644
--- a/packages/SystemUI/res/values/dimens_tv.xml
+++ b/packages/SystemUI/res/values/dimens_tv.xml
@@ -38,9 +38,6 @@
<dimen name="recents_tv_unselected_item_z">6dp</dimen>
<dimen name="recents_tv_selected_item_z_delta">10dp</dimen>
- <!-- Extra space around the PIP and its outline in PIP onboarding activity -->
- <dimen name="tv_pip_bounds_space">3dp</dimen>
-
<!-- Values for text on recents cards on tv -->
<dimen name="recents_tv_title_text_size">12sp</dimen>
@@ -52,4 +49,12 @@
<dimen name="recents_tv_dismiss_icon_bottom_margin">1dip</dimen>
<dimen name="recents_tv_dismiss_text_size">12sp</dimen>
+ <!-- Values for PIP in recents -->
+ <dimen name="recents_tv_pip_controls_margin_top">10dp</dimen>
+
+ <!-- Extra space around the PIP and its outline in PIP onboarding activity -->
+ <dimen name="tv_pip_bounds_space">3dp</dimen>
+ <!-- Extra space around the PIP control button icon to match with the focused circle -->
+ <dimen name="tv_pip_button_icon_padding">5dp</dimen>
+
</resources>
diff --git a/packages/SystemUI/res/values/integers_tv.xml b/packages/SystemUI/res/values/integers_tv.xml
index c60c245..20cd330 100644
--- a/packages/SystemUI/res/values/integers_tv.xml
+++ b/packages/SystemUI/res/values/integers_tv.xml
@@ -17,4 +17,5 @@
<integer name="item_scale_anim_duration">150</integer>
<integer name="dismiss_short_duration">200</integer>
<integer name="dismiss_long_duration">400</integer>
-</resources>
\ No newline at end of file
+ <integer name="recents_tv_pip_focus_anim_duration">200</integer>
+</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 6f0f30d..060e050 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -417,6 +417,8 @@
<string name="accessibility_recents_item_dismissed"><xliff:g id="app" example="Calendar">%s</xliff:g> dismissed.</string>
<!-- Content description to tell the user all applications has been removed from recents -->
<string name="accessibility_recents_all_items_dismissed">All recent applications dismissed.</string>
+ <!-- Content description to tell the user that this button will open application info for an application in recents -->
+ <string name="accessibility_recents_item_open_app_info">Open <xliff:g id="app" example="Calendar">%s</xliff:g> application info.</string>
<!-- Content description to tell the user an application has been launched from recents -->
<string name="accessibility_recents_item_launched">Starting <xliff:g id="app" example="Calendar">%s</xliff:g>.</string>
<!-- Content description of individual recents task. -->
@@ -1388,6 +1390,31 @@
<string name="keyboard_shortcut_group_system_recents">Recents</string>
<!-- User visible title for the the keyboard shortcut that triggers the back action. -->
<string name="keyboard_shortcut_group_system_back">Back</string>
+ <!-- User visible title for the the keyboard shortcut that triggers the notification shade. -->
+ <string name="keyboard_shortcut_group_system_notifications">Notifications</string>
+ <!-- User visible title for the the keyboard shortcut that triggers the keyboard shortcuts helper. -->
+ <string name="keyboard_shortcut_group_system_shortcuts_helper">Keyboard Shortcuts</string>
+ <!-- User visible title for the the keyboard shortcut that switches input methods. -->
+ <string name="keyboard_shortcut_group_system_switch_input">Switch input method</string>
+
+ <!-- User visible title for the system-wide applications keyboard shortcuts list. -->
+ <string name="keyboard_shortcut_group_applications">Applications</string>
+ <!-- User visible title for the keyboard shortcut that takes the user to the assist app. -->
+ <string name="keyboard_shortcut_group_applications_assist">Assist</string>
+ <!-- User visible title for the keyboard shortcut that takes the user to the browser app. -->
+ <string name="keyboard_shortcut_group_applications_browser">Browser</string>
+ <!-- User visible title for the keyboard shortcut that takes the user to the contacts app. -->
+ <string name="keyboard_shortcut_group_applications_contacts">Contacts</string>
+ <!-- User visible title for the keyboard shortcut that takes the user to the email app. -->
+ <string name="keyboard_shortcut_group_applications_email">Email</string>
+ <!-- User visible title for the keyboard shortcut that takes the user to the instant messaging app. -->
+ <string name="keyboard_shortcut_group_applications_im">IM</string>
+ <!-- User visible title for the keyboard shortcut that takes the user to the music app. -->
+ <string name="keyboard_shortcut_group_applications_music">Music</string>
+ <!-- User visible title for the keyboard shortcut that takes the user to the YouTube app. -->
+ <string name="keyboard_shortcut_group_applications_youtube">YouTube</string>
+ <!-- User visible title for the keyboard shortcut that takes the user to the calendar app. -->
+ <string name="keyboard_shortcut_group_applications_calendar">Calendar</string>
<!-- SysUI Tuner: Option to show full do not disturb panel in volume [CHAR LIMIT=60] -->
<string name="tuner_full_zen_title">Show with volume controls</string>
@@ -1530,4 +1557,34 @@
<!-- Accessibility action for moving down the docked stack divider [CHAR LIMIT=NONE] -->
<string name="accessibility_action_divider_move_right">Move right</string>
+ <!-- Text that gets shown on top of current activity to inform the user that the system force-resized the current activity and that things might crash/not work properly [CHAR LIMIT=NONE] -->
+ <string name="forced_resizable_info_text">App may not work with multi-window</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 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 description of a place to drop a tile while editing positions [CHAR LIMIT=NONE] -->
+ <string name="accessibility_qs_edit_position_label">Position <xliff:g id="position" example="2">%1$d</xliff:g>. Double tap to select.</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 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 when QS tile is added [CHAR LIMIT=NONE] -->
+ <string name="accessibility_qs_edit_tile_added"><xliff:g id="tile_name" example="Wi-Fi">%1$s</xliff:g> is added to position <xliff:g id="position" example="5">%2$d</xliff:g></string>
+
+ <!-- Accessibility action when QS tile is removed [CHAR LIMIT=NONE] -->
+ <string name="accessibility_qs_edit_tile_removed"><xliff:g id="tile_name" example="Wi-Fi">%1$s</xliff:g> is removed</string>
+
+ <!-- Accessibility action when QS tile is moved [CHAR LIMIT=NONE] -->
+ <string name="accessibility_qs_edit_tile_moved"><xliff:g id="tile_name" example="Wi-Fi">%1$s</xliff:g> moved to position <xliff:g id="position" example="5">%2$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>
+
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 21ad216..2b134af 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -40,6 +40,18 @@
<item name="android:windowBackground">@android:color/black</item>
</style>
+ <!-- Theme used for the activity that shows when the system forced an app to be resizable -->
+ <style name="ForcedResizableTheme" parent="@android:style/Theme.Translucent.NoTitleBar.Fullscreen">
+ <item name="android:windowBackground">@drawable/forced_resizable_background</item>
+ <item name="android:statusBarColor">@color/transparent</item>
+ <item name="android:windowAnimationStyle">@style/Animation.ForcedResizable</item>
+ </style>
+
+ <style name="Animation.ForcedResizable" parent="@android:style/Animation">
+ <item name="android:activityOpenEnterAnimation">@anim/forced_resizable_enter</item>
+ <item name="android:activityCloseExitAnimation">@anim/forced_resizable_exit</item>
+ </style>
+
<style name="TextAppearance.StatusBar.HeadsUp"
parent="@*android:style/TextAppearance.StatusBar">
</style>
diff --git a/packages/SystemUI/res/xml/night_mode.xml b/packages/SystemUI/res/xml/night_mode.xml
index d5f5333..34af820 100644
--- a/packages/SystemUI/res/xml/night_mode.xml
+++ b/packages/SystemUI/res/xml/night_mode.xml
@@ -27,10 +27,6 @@
android:title="@string/when_night_mode_on">
<SwitchPreference
- android:key="dark_theme"
- android:title="@string/use_dark_theme" />
-
- <SwitchPreference
android:key="adjust_tint"
android:title="@string/adjust_tint" />
@@ -40,8 +36,4 @@
</PreferenceCategory>
- <Preference
- android:selectable="false"
- android:summary="@string/night_mode_disclaimer" />
-
</PreferenceScreen>
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
index 4845425..087f61e 100755
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
@@ -452,7 +452,7 @@
boolean pctOpaque = false;
float pctX = 0, pctY = 0;
String pctText = null;
- if (!mPluggedIn && level > mCriticalLevel && mShowPercent) {
+ if (!mPluggedIn && !mPowerSaveEnabled && level > mCriticalLevel && mShowPercent) {
mTextPaint.setColor(getColorForLevel(level));
mTextPaint.setTextSize(height *
(SINGLE_DIGIT_PERCENT ? 0.75f
@@ -480,7 +480,7 @@
mShapePath.op(mClipPath, Path.Op.INTERSECT);
c.drawPath(mShapePath, mBatteryPaint);
- if (!mPluggedIn) {
+ if (!mPluggedIn && !mPowerSaveEnabled) {
if (level <= mCriticalLevel) {
// draw the warning text
final float x = mWidth * 0.5f;
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index aa3f6e5..d12ab29 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -514,7 +514,7 @@
if (canBeExpanded) {
if (DEBUG) Log.d(TAG, "working on an expandable child");
mNaturalHeight = mScaler.getNaturalHeight();
- mSmallSize = v.getMinExpandHeight();
+ mSmallSize = v.getCollapsedHeight();
} else {
if (DEBUG) Log.d(TAG, "working on a non-expandable child");
mNaturalHeight = mOldHeight;
diff --git a/packages/SystemUI/src/com/android/systemui/Interpolators.java b/packages/SystemUI/src/com/android/systemui/Interpolators.java
index 5e33a9f..17f4dab 100644
--- a/packages/SystemUI/src/com/android/systemui/Interpolators.java
+++ b/packages/SystemUI/src/com/android/systemui/Interpolators.java
@@ -17,6 +17,7 @@
package com.android.systemui;
import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
@@ -32,6 +33,7 @@
public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f);
public static final Interpolator LINEAR = new LinearInterpolator();
+ public static final Interpolator ACCELERATE = new AccelerateInterpolator();
public static final Interpolator ACCELERATE_DECELERATE = new AccelerateDecelerateInterpolator();
public static final Interpolator DECELERATE_QUINT = new DecelerateInterpolator(2.5f);
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index a08b2c1..e838191 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -274,10 +274,7 @@
mWatchLongPress = new Runnable() {
@Override
public void run() {
- float pos = getPos(ev);
- float delta = pos - mInitialTouchPos;
- if (mCurrView != null && !mLongPressSent
- && Math.abs(delta) < mPagingTouchSlop) {
+ if (mCurrView != null && !mLongPressSent) {
mLongPressSent = true;
mCurrView.sendAccessibilityEvent(
AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
@@ -328,10 +325,11 @@
/**
* @param view The view to be dismissed
* @param velocity The desired pixels/second speed at which the view should move
+ * @param useAccelerateInterpolator Should an accelerating Interpolator be used
*/
- public void dismissChild(final View view, float velocity) {
+ public void dismissChild(final View view, float velocity, boolean useAccelerateInterpolator) {
dismissChild(view, velocity, null /* endAction */, 0 /* delay */,
- velocity == 0 /* useAccelerateInterpolator */, 0 /* fixedDuration */);
+ useAccelerateInterpolator, 0 /* fixedDuration */, false /* isDismissAll */);
}
/**
@@ -343,17 +341,22 @@
* @param fixedDuration If not 0, this exact duration will be taken
*/
public void dismissChild(final View animView, float velocity, final Runnable endAction,
- long delay, boolean useAccelerateInterpolator, long fixedDuration) {
+ long delay, boolean useAccelerateInterpolator, long fixedDuration,
+ boolean isDismissAll) {
final boolean canBeDismissed = mCallback.canChildBeDismissed(animView);
float newPos;
boolean isLayoutRtl = animView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
- if (velocity < 0
- || (velocity == 0 && getTranslation(animView) < 0)
- // if we use the Menu to dismiss an item in landscape, animate up
- || (velocity == 0 && getTranslation(animView) == 0 && mSwipeDirection == Y)
- // if the language is rtl we prefer swiping to the left
- || (velocity == 0 && getTranslation(animView) == 0 && isLayoutRtl)) {
+ // if we use the Menu to dismiss an item in landscape, animate up
+ boolean animateUpForMenu = velocity == 0 && (getTranslation(animView) == 0 || isDismissAll)
+ && mSwipeDirection == Y;
+ // if the language is rtl we prefer swiping to the left
+ boolean animateLeftForRtl = velocity == 0 && (getTranslation(animView) == 0 || isDismissAll)
+ && isLayoutRtl;
+ boolean animateLeft = velocity < 0
+ || (velocity == 0 && getTranslation(animView) < 0 && !isDismissAll);
+
+ if (animateLeft || animateLeftForRtl || animateUpForMenu) {
newPos = -getSize(animView);
} else {
newPos = getSize(animView);
@@ -572,7 +575,8 @@
if (!handleUpEvent(ev, mCurrView, velocity, getTranslation(mCurrView))) {
if (isDismissGesture(ev)) {
// flingadingy
- dismissChild(mCurrView, swipedFastEnough() ? velocity : 0f);
+ dismissChild(mCurrView, velocity,
+ !swipedFastEnough() /* useAccelerateInterpolator */);
} else {
// snappity
mCallback.onDragCancelled(mCurrView);
@@ -618,11 +622,9 @@
protected boolean swipedFastEnough() {
float velocity = getVelocity(mVelocityTracker);
- float perpendicularVelocity = getPerpendicularVelocity(mVelocityTracker);
float translation = getTranslation(mCurrView);
- boolean ret = (Math.abs(velocity) > getEscapeVelocity()) &&
- (Math.abs(velocity) > Math.abs(perpendicularVelocity)) &&
- (velocity > 0) == (translation > 0);
+ boolean ret = (Math.abs(velocity) > getEscapeVelocity())
+ && (velocity > 0) == (translation > 0);
return ret;
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index b2b6127..455a69f 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -123,6 +123,17 @@
startServicesIfNeeded(SERVICES);
}
+ /**
+ * Ensures that all the Secondary user SystemUI services are running. If they are already
+ * running, this is a no-op. This is needed to conditinally start all the services, as we only
+ * need to have it in the main process.
+ *
+ * <p>This method must only be called from the main thread.</p>
+ */
+ void startSecondaryUserServicesIfNeeded() {
+ startServicesIfNeeded(SERVICES_PER_USER);
+ }
+
private void startServicesIfNeeded(Class<?>[] services) {
if (mServicesStarted) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 0d75fdd..53c2233 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -27,9 +27,25 @@
import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
+import com.android.systemui.statusbar.phone.QSTileHost;
import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarWindowManager;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.BluetoothController;
+import com.android.systemui.statusbar.policy.CastController;
+import com.android.systemui.statusbar.policy.FlashlightController;
+import com.android.systemui.statusbar.policy.HotspotController;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.LocationController;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NextAlarmController;
+import com.android.systemui.statusbar.policy.RotationLockController;
+import com.android.systemui.statusbar.policy.SecurityController;
+import com.android.systemui.statusbar.policy.UserInfoController;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.statusbar.policy.ZenModeController;
/**
* Class factory to provide customizable SystemUI components.
@@ -82,6 +98,20 @@
return new NotificationIconAreaController(context, phoneStatusBar);
}
+ public QSTileHost createQSTileHost(Context context, PhoneStatusBar statusBar,
+ BluetoothController bluetooth, LocationController location,
+ RotationLockController rotation, NetworkController network,
+ ZenModeController zen, HotspotController hotspot,
+ CastController cast, FlashlightController flashlight,
+ UserSwitcherController userSwitcher, UserInfoController userInfo,
+ KeyguardMonitor keyguard, SecurityController security,
+ BatteryController battery, StatusBarIconController iconController,
+ NextAlarmController nextAlarmController) {
+ return new QSTileHost(context, statusBar, bluetooth, location, rotation, network, zen,
+ hotspot, cast, flashlight, userSwitcher, userInfo, keyguard, security, battery,
+ iconController, nextAlarmController);
+ }
+
public <T> T createInstance(Class<T> classType) {
return null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUISecondaryUserService.java b/packages/SystemUI/src/com/android/systemui/SystemUISecondaryUserService.java
new file mode 100644
index 0000000..f619bfb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/SystemUISecondaryUserService.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui;
+
+import android.app.ActivityManager;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.Process;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+public class SystemUISecondaryUserService extends Service {
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ ((SystemUIApplication) getApplication()).startSecondaryUserServicesIfNeeded();
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ SystemUI[] services = ((SystemUIApplication) getApplication()).getServices();
+ if (args == null || args.length == 0) {
+ for (SystemUI ui: services) {
+ pw.println("dumping service: " + ui.getClass().getName());
+ ui.dump(fd, pw, args);
+ }
+ } else {
+ String svc = args[0];
+ for (SystemUI ui: services) {
+ String name = ui.getClass().getName();
+ if (name.endsWith(svc)) {
+ ui.dump(fd, pw, args);
+ }
+ }
+ }
+ }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index d2c60ef..84d3599 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -115,9 +115,9 @@
}
@Override // Binder interface
- public void onFinishedGoingToSleep(int reason) {
+ public void onFinishedGoingToSleep(int reason, boolean cameraGestureTriggered) {
checkPermission();
- mKeyguardViewMediator.onFinishedGoingToSleep(reason);
+ mKeyguardViewMediator.onFinishedGoingToSleep(reason, cameraGestureTriggered);
}
@Override // Binder interface
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index a5dfc4b..e00bf6c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -747,7 +747,7 @@
notifyStartedGoingToSleep();
}
- public void onFinishedGoingToSleep(int why) {
+ public void onFinishedGoingToSleep(int why, boolean cameraGestureTriggered) {
if (DEBUG) Log.d(TAG, "onFinishedGoingToSleep(" + why + ")");
synchronized (this) {
mDeviceInteractive = false;
@@ -758,6 +758,16 @@
notifyFinishedGoingToSleep();
+ if (cameraGestureTriggered) {
+ Log.i(TAG, "Camera gesture was triggered, preventing Keyguard locking.");
+
+ // Just to make sure, make sure the device is awake.
+ mContext.getSystemService(PowerManager.class).wakeUp(SystemClock.uptimeMillis(),
+ "com.android.systemui:CAMERA_GESTURE_PREVENT_LOCK");
+ mPendingLock = false;
+ mPendingReset = false;
+ }
+
if (mPendingReset) {
resetStateLocked();
mPendingReset = false;
@@ -771,7 +781,7 @@
// We do not have timeout and power button instant lock setting for profile lock.
// So we use the personal setting if there is any. But if there is no device
// we need to make sure we lock it immediately when the screen is off.
- if (!mLockLater) {
+ if (!mLockLater && !cameraGestureTriggered) {
doKeyguardForChildProfilesLocked();
}
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 522d533..109a456 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -183,14 +183,16 @@
return;
}
+ boolean isPowerSaver = mPowerManager.isPowerSaveMode();
if (!plugged
+ && !isPowerSaver
&& (bucket < oldBucket || oldPlugged)
&& mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
&& bucket < 0) {
// only play SFX when the dialog comes up or the bucket changes
final boolean playSound = bucket != oldBucket || oldPlugged;
mWarnings.showLowBatteryWarning(playSound);
- } else if (plugged || (bucket > oldBucket && bucket > 0)) {
+ } else if (isPowerSaver || plugged || (bucket > oldBucket && bucket > 0)) {
mWarnings.dismissLowBatteryWarning();
} else {
mWarnings.updateLowBatteryWarning();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java b/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
index 1200266..ba07bf4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
@@ -1,86 +1,219 @@
package com.android.systemui.qs;
import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Paint;
+import android.graphics.drawable.AnimatedVectorDrawable;
import android.util.AttributeSet;
-import android.view.Gravity;
+import android.util.Log;
import android.view.View;
-import android.widget.LinearLayout;
-
+import android.view.ViewGroup;
+import android.widget.ImageView;
import com.android.systemui.R;
-public class PageIndicator extends LinearLayout {
+import java.util.ArrayList;
- private final int mPageIndicatorSize;
+public class PageIndicator extends ViewGroup {
+
+ private static final String TAG = "PageIndicator";
+ private static final boolean DEBUG = false;
+
+ private static final long ANIMATION_DURATION = 250;
+
+ // The size of a single dot in relation to the whole animation.
+ private static final float SINGLE_SCALE = .4f;
+
+ private static final float MINOR_ALPHA = .3f;
+
+ private final ArrayList<Integer> mQueuedPositions = new ArrayList<>();
+
+ private final int mPageIndicatorWidth;
+ private final int mPageIndicatorHeight;
+ private final int mPageDotWidth;
+
+ private int mPosition = -1;
+ private boolean mAnimating;
public PageIndicator(Context context, AttributeSet attrs) {
super(context, attrs);
- setGravity(Gravity.CENTER);
- mPageIndicatorSize =
- (int) mContext.getResources().getDimension(R.dimen.qs_page_indicator_size);
+ mPageIndicatorWidth =
+ (int) mContext.getResources().getDimension(R.dimen.qs_page_indicator_width);
+ mPageIndicatorHeight =
+ (int) mContext.getResources().getDimension(R.dimen.qs_page_indicator_height);
+ mPageDotWidth = (int) (mPageIndicatorWidth * SINGLE_SCALE);
}
public void setNumPages(int numPages) {
+ setVisibility(numPages > 1 ? View.VISIBLE : View.INVISIBLE);
+ if (mAnimating) {
+ Log.w(TAG, "setNumPages during animation");
+ }
while (numPages < getChildCount()) {
removeViewAt(getChildCount() - 1);
}
while (numPages > getChildCount()) {
- SinglePageIndicator v = new SinglePageIndicator(mContext);
- v.setAmount(0);
- addView(v, new LayoutParams(mPageIndicatorSize, mPageIndicatorSize));
+ ImageView v = new ImageView(mContext);
+ v.setImageResource(R.drawable.minor_a_b);
+ addView(v, new LayoutParams(mPageIndicatorWidth, mPageIndicatorHeight));
}
+ // Refresh state.
+ setIndex(mPosition >> 1);
}
public void setLocation(float location) {
int index = (int) location;
- location -= index;
+ int position = index << 1 | ((location != index) ? 1 : 0);
+ if (DEBUG) Log.d(TAG, "setLocation " + location + " " + index + " " + position);
+ int lastPosition = mPosition;
+ if (mQueuedPositions.size() != 0) {
+ lastPosition = mQueuedPositions.get(mQueuedPositions.size() - 1);
+ }
+ if (position == lastPosition) return;
+ if (mAnimating) {
+ if (DEBUG) Log.d(TAG, "Queueing transition to " + Integer.toHexString(position));
+ mQueuedPositions.add(position);
+ return;
+ }
+
+ setPosition(position);
+ }
+
+ private void setPosition(int position) {
+ if (isVisibleToUser() && Math.abs(mPosition - position) == 1) {
+ animate(mPosition, position);
+ } else {
+ if (DEBUG) Log.d(TAG, "Skipping animation " + isVisibleToUser() + " " + mPosition
+ + " " + position);
+ setIndex(position >> 1);
+ }
+ mPosition = position;
+ }
+
+ private void setIndex(int index) {
final int N = getChildCount();
for (int i = 0; i < N; i++) {
- float amount = 0;
- if (i == index) {
- amount = 1 - location;
- } else if (i == index + 1) {
- amount = location;
+ ImageView v = (ImageView) getChildAt(i);
+ // Clear out any animation positioning.
+ v.setTranslationX(0);
+ v.setImageResource(R.drawable.major_a_b);
+ v.setAlpha(getAlpha(i == index));
+ }
+ }
+
+ private void animate(int from, int to) {
+ if (DEBUG) Log.d(TAG, "Animating from " + Integer.toHexString(from) + " to "
+ + Integer.toHexString(to));
+ int fromIndex = from >> 1;
+ int toIndex = to >> 1;
+
+ // Set the position of everything, then we will manually control the two views involved
+ // in the animation.
+ setIndex(fromIndex);
+
+ boolean fromTransition = (from & 1) != 0;
+ boolean isAState = fromTransition ? from > to : from < to;
+ int firstIndex = Math.min(fromIndex, toIndex);
+ int secondIndex = Math.max(fromIndex, toIndex);
+ if (secondIndex == firstIndex) {
+ secondIndex++;
+ }
+ ImageView first = (ImageView) getChildAt(firstIndex);
+ ImageView second = (ImageView) getChildAt(secondIndex);
+ // Lay the two views on top of each other.
+ second.setTranslationX(first.getX() - second.getX());
+
+ playAnimation(first, getTransition(fromTransition, isAState, false));
+ first.setAlpha(getAlpha(false));
+
+ playAnimation(second, getTransition(fromTransition, isAState, true));
+ second.setAlpha(getAlpha(true));
+
+ mAnimating = true;
+ }
+
+ private float getAlpha(boolean isMajor) {
+ return isMajor ? 1 : MINOR_ALPHA;
+ }
+
+ private void playAnimation(ImageView imageView, int res) {
+ final AnimatedVectorDrawable avd = (AnimatedVectorDrawable) getContext().getDrawable(res);
+ imageView.setImageDrawable(avd);
+ avd.forceAnimationOnUI();
+ avd.start();
+ // TODO: Figure out how to user an AVD animation callback instead, which doesn't
+ // seem to be working right now...
+ postDelayed(mAnimationDone, ANIMATION_DURATION);
+ }
+
+ private int getTransition(boolean fromB, boolean isMajorAState, boolean isMajor) {
+ if (isMajor) {
+ if (fromB) {
+ if (isMajorAState) {
+ return R.drawable.major_b_a_animation;
+ } else {
+ return R.drawable.major_b_c_animation;
+ }
+ } else {
+ if (isMajorAState) {
+ return R.drawable.major_a_b_animation;
+ } else {
+ return R.drawable.major_c_b_animation;
+ }
}
- ((SinglePageIndicator) getChildAt(i)).setAmount(amount);
+ } else {
+ if (fromB) {
+ if (isMajorAState) {
+ return R.drawable.minor_b_c_animation;
+ } else {
+ return R.drawable.minor_b_a_animation;
+ }
+ } else {
+ if (isMajorAState) {
+ return R.drawable.minor_c_b_animation;
+ } else {
+ return R.drawable.minor_a_b_animation;
+ }
+ }
}
}
- // This could be done with a circle drawable and an ImageView, but this seems
- // easier for now.
- public static class SinglePageIndicator extends View {
- private static final int MIN_ALPHA = 0x4d;
- private static final int MAX_ALPHA = 0xff;
-
- private static final float MIN_SIZE = .55f;
- private static final float MAX_SIZE = .7f;
-
- private final Paint mPaint;
- private float mSize;
-
- public SinglePageIndicator(Context context) {
- super(context);
- mPaint = new Paint();
- mPaint.setColor(0xffffffff);
- mPaint.setAlpha(MAX_ALPHA);
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final int N = getChildCount();
+ if (N == 0) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ return;
}
-
- public void setAmount(float amount) {
- mSize = amount * (MAX_SIZE - MIN_SIZE) + MIN_SIZE;
- int alpha = (int) (amount * (MAX_ALPHA - MIN_ALPHA)) + MIN_ALPHA;
- mPaint.setAlpha(alpha);
- postInvalidate();
+ final int widthChildSpec = MeasureSpec.makeMeasureSpec(mPageIndicatorWidth,
+ MeasureSpec.EXACTLY);
+ final int heightChildSpec = MeasureSpec.makeMeasureSpec(mPageIndicatorHeight,
+ MeasureSpec.EXACTLY);
+ for (int i = 0; i < N; i++) {
+ getChildAt(i).measure(widthChildSpec, heightChildSpec);
}
+ int width = (mPageIndicatorWidth - mPageDotWidth) * N + mPageDotWidth;
+ setMeasuredDimension(width, mPageIndicatorHeight);
+ }
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ final int N = getChildCount();
+ if (N == 0) {
+ return;
+ }
+ for (int i = 0; i < N; i++) {
+ int left = (mPageIndicatorWidth - mPageDotWidth) * i;
+ getChildAt(i).layout(left, 0, mPageIndicatorWidth + left, mPageIndicatorHeight);
+ }
+ }
+
+ private final Runnable mAnimationDone = new Runnable() {
@Override
- public void draw(Canvas canvas) {
- int minDimen = Math.min(getWidth(), getHeight()) / 2;
- float radius = mSize * minDimen;
- float x = getWidth() / 2f;
- float y = getHeight() / 2f;
- canvas.drawCircle(x, y, radius, mPaint);
+ public void run() {
+ if (DEBUG) Log.d(TAG, "onAnimationEnd - queued: " + mQueuedPositions.size());
+ mAnimating = false;
+ if (mQueuedPositions.size() != 0) {
+ setPosition(mQueuedPositions.remove(0));
+ }
}
- }
+ };
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 24b45cc..115c9d0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -1,13 +1,13 @@
package com.android.systemui.qs;
import android.content.Context;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import com.android.internal.widget.PagerAdapter;
-import com.android.internal.widget.ViewPager;
import com.android.systemui.R;
import com.android.systemui.qs.QSPanel.QSTileLayout;
import com.android.systemui.qs.QSPanel.TileRecord;
@@ -36,9 +36,9 @@
@Override
public void onPageSelected(int position) {
if (mPageIndicator == null) return;
- mPageIndicator.setLocation(position);
if (mPageListener != null) {
- mPageListener.onPageChanged(position == 0);
+ mPageListener.onPageChanged(isLayoutRtl() ? position == mPages.size() - 1
+ : position == 0);
}
}
@@ -48,7 +48,8 @@
if (mPageIndicator == null) return;
mPageIndicator.setLocation(position + positionOffset);
if (mPageListener != null) {
- mPageListener.onPageChanged(position == 0 && positionOffsetPixels == 0);
+ mPageListener.onPageChanged(positionOffsetPixels == 0 &&
+ (isLayoutRtl() ? position == mPages.size() - 1 : position == 0));
}
}
@@ -60,6 +61,21 @@
}
@Override
+ public void onRtlPropertiesChanged(int layoutDirection) {
+ super.onRtlPropertiesChanged(layoutDirection);
+ setAdapter(mAdapter);
+ setCurrentItem(0, false);
+ }
+
+ @Override
+ public void setCurrentItem(int item, boolean smoothScroll) {
+ if (isLayoutRtl()) {
+ item = mPages.size() - 1 - item;
+ }
+ super.setCurrentItem(item, smoothScroll);
+ }
+
+ @Override
public boolean hasOverlappingRendering() {
return false;
}
@@ -129,6 +145,7 @@
mNumPages = index + 1;
mPageIndicator.setNumPages(mNumPages);
mAdapter.notifyDataSetChanged();
+ setCurrentItem(0, false);
}
}
@@ -207,6 +224,9 @@
public Object instantiateItem(ViewGroup container, int position) {
if (DEBUG) Log.d(TAG, "Instantiating " + position);
+ if (isLayoutRtl()) {
+ position = mPages.size() - 1 - position;
+ }
ViewGroup view = mPages.get(position);
container.addView(view);
return view;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 815c679..de8eec4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -41,7 +41,7 @@
private static final String MOVE_FULL_ROWS = "sysui_qs_move_whole_rows";
public static final float EXPANDED_TILE_DELAY = .7f;
- private static final float LAST_ROW_EXPANDED_DELAY = .84f;
+ private static final float LAST_ROW_EXPANDED_DELAY = .86f;
private final ArrayList<View> mAllViews = new ArrayList<>();
private final ArrayList<View> mTopFiveQs = new ArrayList<>();
@@ -64,6 +64,7 @@
private boolean mAllowFancy;
private boolean mFullRows;
private int mNumQuickTiles;
+ private float mLastPosition;
public QSAnimator(QSContainer container, QuickQSPanel quickPanel, QSPanel panel) {
mQsContainer = container;
@@ -80,6 +81,10 @@
}
}
+ public void onRtlChanged() {
+ updateAnimators();
+ }
+
public void setOnKeyguard(boolean onKeyguard) {
mOnKeyguard = onKeyguard;
if (mOnKeyguard) {
@@ -113,7 +118,7 @@
} else if (MOVE_FULL_ROWS.equals(key)) {
mFullRows = newValue == null || Integer.parseInt(newValue) != 0;
} else if (QuickQSPanel.NUM_QUICK_TILES.equals(key)) {
- mNumQuickTiles = QuickQSPanel.getNumQuickTiles(mQsContainer.getContext());
+ mNumQuickTiles = mQuickQsPanel.getNumQuickTiles(mQsContainer.getContext());
clearAnimationState();
}
updateAnimators();
@@ -139,7 +144,9 @@
int count = 0;
int[] loc1 = new int[2];
int[] loc2 = new int[2];
+ int lastXDiff = 0;
int lastYDiff = 0;
+ int lastX = 0;
clearAnimationState();
mAllViews.clear();
@@ -155,10 +162,12 @@
// Quick tiles.
QSTileBaseView quickTileView = mQuickQsPanel.getTileView(tile);
+ lastX = loc1[0];
getRelativePosition(loc1, quickTileView.getIcon(), mQsContainer);
getRelativePosition(loc2, tileIcon, mQsContainer);
final int xDiff = loc2[0] - loc1[0];
final int yDiff = loc2[1] - loc1[1];
+ lastXDiff = loc1[0] - lastX;
lastYDiff = yDiff;
// Move the quick tile right from its location to the new one.
translationXBuilder.addFloat(quickTileView, "translationX", 0, xDiff);
@@ -177,9 +186,20 @@
mAllViews.add(tileIcon);
mAllViews.add(quickTileView);
} else if (mFullRows && isIconInAnimatedRow(count)) {
+ // TODO: Refactor some of this, it shares a lot with the above block.
+ // Move the last tile position over by the last difference between quick tiles.
+ // This makes the extra icons seems as if they are coming from positions in the
+ // quick panel.
+ loc1[0] += lastXDiff;
+ getRelativePosition(loc2, tileIcon, mQsContainer);
+ final int xDiff = loc2[0] - loc1[0];
+ final int yDiff = loc2[1] - loc1[1];
+
firstPageBuilder.addFloat(tileView, "translationY", mQsPanel.getHeight(), 0);
- translationYBuilder.addFloat(label, "translationY", -lastYDiff, 0);
- translationYBuilder.addFloat(tileIcon, "translationY", -lastYDiff, 0);
+ translationXBuilder.addFloat(tileView, "translationX", -xDiff, 0);
+ translationYBuilder.addFloat(label, "translationY", -yDiff, 0);
+ translationYBuilder.addFloat(tileIcon, "translationY", -yDiff, 0);
+
mAllViews.add(tileIcon);
} else {
lastRowBuilder.addFloat(tileView, "alpha", 0, 1);
@@ -231,7 +251,7 @@
private void getRelativePositionInt(int[] loc1, View view, View parent) {
if(view == parent || view == null) return;
- loc1[0] += view.getLeft();
+ loc1[0] += view.getX();
loc1[1] += view.getTop();
getRelativePositionInt(loc1, (View) view.getParent(), parent);
}
@@ -241,6 +261,7 @@
if (mOnKeyguard) {
return;
}
+ mLastPosition = position;
if (mOnFirstPage && mAllowFancy) {
mQuickQsPanel.setAlpha(1);
mFirstPageAnimator.setPosition(position);
@@ -281,7 +302,7 @@
private void clearAnimationState() {
final int N = mAllViews.size();
mQuickQsPanel.setAlpha(0);
- mQuickQsPanel.setVisibility(View.INVISIBLE);
+ mQuickQsPanel.setVisibility(View.VISIBLE);
for (int i = 0; i < N; i++) {
View v = mAllViews.get(i);
v.setAlpha(1);
@@ -297,7 +318,7 @@
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
int oldTop, int oldRight, int oldBottom) {
- updateAnimators();
+ mQsPanel.post(mUpdateAnimators);
}
@Override
@@ -319,6 +340,7 @@
@Override
public void run() {
updateAnimators();
+ setPosition(mLastPosition);
}
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
index 5b05e84..5d06aeb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
@@ -20,6 +20,7 @@
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.graphics.Point;
+import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
@@ -43,6 +44,7 @@
private static final boolean DEBUG = false;
private final Point mSizePoint = new Point();
+ private final Rect mQsBounds = new Rect();
private int mHeightOverride = -1;
private QSPanel mQSPanel;
@@ -76,6 +78,12 @@
mQSCustomizer.setQsContainer(this);
}
+ @Override
+ public void onRtlPropertiesChanged(int layoutDirection) {
+ super.onRtlPropertiesChanged(layoutDirection);
+ mQSAnimator.onRtlChanged();
+ }
+
public void setHost(QSTileHost qsh) {
mQSPanel.setHost(qsh, mQSCustomizer);
mHeader.setQSPanel(mQSPanel);
@@ -140,6 +148,8 @@
public void notifyCustomizeChanged() {
// The customize state changed, so our height changed.
updateBottom();
+ mQSPanel.setVisibility(!mQSCustomizer.isCustomizing() ? View.VISIBLE : View.INVISIBLE);
+ mHeader.setVisibility(!mQSCustomizer.isCustomizing() ? View.VISIBLE : View.INVISIBLE);
// Let the panel know the position changed and it needs to update where notifications
// and whatnot are.
mPanelView.onQsHeightChanged();
@@ -224,6 +234,12 @@
mQSDetail.setFullyExpanded(expansion == 1);
mQSAnimator.setPosition(expansion);
updateBottom();
+
+ // Set bounds on the QS panel so it doesn't run over the header.
+ mQsBounds.top = (int) (mQSPanel.getHeight() * (1 - expansion));
+ mQsBounds.right = mQSPanel.getWidth();
+ mQsBounds.bottom = mQSPanel.getHeight();
+ mQSPanel.setClipBounds(mQsBounds);
}
public void animateHeaderSlidingIn(long delay) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
index 6d8b476..51efbf0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
@@ -36,12 +36,12 @@
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.SecurityController;
+import static android.provider.Settings.ACTION_VPN_SETTINGS;
+
public class QSFooter implements OnClickListener, DialogInterface.OnClickListener {
protected static final String TAG = "QSFooter";
protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final String ACTION_VPN_SETTINGS = "android.net.vpn.SETTINGS";
-
private final View mRootView;
private final TextView mFooterText;
private final ImageView mFooterIcon;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 899b0ef..a05818a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -119,12 +119,18 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
TunerService.get(mContext).addTunable(this, QS_SHOW_BRIGHTNESS);
+ if (mHost != null) {
+ setTiles(mHost.getTiles());
+ }
}
@Override
protected void onDetachedFromWindow() {
TunerService.get(mContext).removeTunable(this);
mHost.removeCallback(this);
+ for (TileRecord record : mRecords) {
+ record.tile.removeCallbacks();
+ }
super.onDetachedFromWindow();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index 42e98aa..3e32905 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -29,6 +29,8 @@
import android.view.View;
import android.view.ViewGroup;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.MetricsProto.MetricsEvent;
import com.android.settingslib.RestrictedLockUtils;
import com.android.systemui.qs.QSTile.State;
import com.android.systemui.qs.external.TileServices;
@@ -212,6 +214,7 @@
}
protected void handleLongClick() {
+ MetricsLogger.action(mContext, MetricsEvent.ACTION_QS_LONG_PRESS, getTileSpec());
mHost.startActivityDismissingKeyguard(getLongClickIntent());
}
@@ -294,6 +297,8 @@
}
}
+ public abstract CharSequence getTileLabel();
+
protected final class H extends Handler {
private static final int ADD_CALLBACK = 1;
private static final int CLICK = 2;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
index 98a1c23..57a1a4a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
@@ -37,7 +37,7 @@
private final int mTileSpacingPx;
private int mTilePaddingTopPx;
- private TextView mLabel;
+ protected TextView mLabel;
private ImageView mPadLock;
public QSTileView(Context context, QSIconView icon) {
@@ -81,7 +81,7 @@
FontSizeUtils.updateFontSize(mLabel, R.dimen.qs_tile_text_size);
}
- private void createLabel() {
+ protected void createLabel() {
final Resources res = mContext.getResources();
View view = LayoutInflater.from(mContext).inflate(R.layout.qs_tile_label, null);
mLabel = (TextView) view.findViewById(R.id.tile_label);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index ab90179..2ef3672 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -143,7 +143,7 @@
}
};
- public static int getNumQuickTiles(Context context) {
+ public int getNumQuickTiles(Context context) {
return TunerService.get(context).getValue(NUM_QUICK_TILES, 5);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java
new file mode 100644
index 0000000..e512f93
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.qs.customize;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.TextView;
+import com.android.systemui.R;
+import com.android.systemui.qs.QSIconView;
+import com.android.systemui.qs.QSTileView;
+import libcore.util.Objects;
+
+public class CustomizeTileView extends QSTileView {
+
+ private TextView mAppLabel;
+
+ public CustomizeTileView(Context context, QSIconView icon) {
+ super(context, icon);
+ }
+
+ @Override
+ protected void createLabel() {
+ super.createLabel();
+ View view = LayoutInflater.from(mContext).inflate(R.layout.qs_tile_label, null);
+ mAppLabel = (TextView) view.findViewById(R.id.tile_label);
+ mAppLabel.setAlpha(.6f);
+ mAppLabel.setSingleLine(true);
+ addView(view);
+ }
+
+ public void setShowAppLabel(boolean showAppLabel) {
+ mAppLabel.setVisibility(showAppLabel ? View.VISIBLE : View.GONE);
+ mLabel.setSingleLine(showAppLabel);
+ }
+
+ public void setAppLabel(CharSequence label) {
+ if (!Objects.equal(label, mAppLabel.getText())) {
+ mAppLabel.setText(label);
+ }
+ }
+
+ public TextView getAppLabel() {
+ return mAppLabel;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index aeca840..716185f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -32,6 +32,8 @@
import android.widget.LinearLayout;
import android.widget.Toolbar;
import android.widget.Toolbar.OnMenuItemClickListener;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.MetricsProto;
import com.android.systemui.R;
import com.android.systemui.qs.QSContainer;
import com.android.systemui.qs.QSDetailClipper;
@@ -116,23 +118,27 @@
public void show(int x, int y) {
if (!isShown) {
+ MetricsLogger.visible(getContext(), MetricsProto.MetricsEvent.QS_EDIT);
isShown = true;
setTileSpecs();
setVisibility(View.VISIBLE);
mClipper.animateCircularClip(x, y, true, mExpandAnimationListener);
new TileQueryHelper(mContext, mHost).setListener(mTileAdapter);
mNotifQsContainer.setCustomizerAnimating(true);
+ mNotifQsContainer.setCustomizerShowing(true);
}
}
public void hide(int x, int y) {
if (isShown) {
+ MetricsLogger.hidden(getContext(), MetricsProto.MetricsEvent.QS_EDIT);
isShown = false;
mToolbar.dismissPopupMenus();
setCustomizing(false);
save();
mClipper.animateCircularClip(x, y, false, mCollapseAnimationListener);
mNotifQsContainer.setCustomizerAnimating(true);
+ mNotifQsContainer.setCustomizerShowing(false);
}
}
@@ -149,6 +155,7 @@
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case MENU_RESET:
+ MetricsLogger.action(getContext(), MetricsProto.MetricsEvent.ACTION_QS_EDIT_RESET);
reset();
break;
}
@@ -170,6 +177,7 @@
specs.add(tile.getTileSpec());
}
mTileAdapter.setTileSpecs(specs);
+ mRecyclerView.setAdapter(mTileAdapter);
}
private void save() {
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 03c2a0b..e8e17b1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -14,7 +14,11 @@
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.graphics.Canvas;
import android.graphics.drawable.ColorDrawable;
import android.os.Handler;
@@ -27,16 +31,22 @@
import android.support.v7.widget.helper.ItemTouchHelper;
import android.view.LayoutInflater;
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 com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.MetricsProto;
import com.android.systemui.R;
import com.android.systemui.qs.QSIconView;
-import com.android.systemui.qs.QSTileView;
import com.android.systemui.qs.customize.TileAdapter.Holder;
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.statusbar.phone.QSTileHost;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
import java.util.ArrayList;
import java.util.List;
@@ -49,28 +59,40 @@
private static final int TYPE_TILE = 0;
private static final int TYPE_EDIT = 1;
+ private static final int TYPE_ACCESSIBLE_DROP = 2;
+ private static final int TYPE_DIVIDER = 4;
+
+ private static final long EDIT_ID = 10000;
+ private static final long DIVIDER_ID = 20000;
private final Context mContext;
private final Handler mHandler = new Handler();
private final List<TileInfo> mTiles = new ArrayList<>();
private final ItemTouchHelper mItemTouchHelper;
- private int mDividerIndex;
+ private final AccessibilityManager mAccessibilityManager;
+ private int mEditIndex;
+ private int mTileDividerIndex;
+ private boolean mNeedsFocus;
private List<String> mCurrentSpecs;
private List<TileInfo> mOtherTiles;
private List<TileInfo> mAllTiles;
private Holder mCurrentDrag;
+ private boolean mAccessibilityMoving;
+ private int mAccessibilityFromIndex;
public TileAdapter(Context context) {
mContext = context;
+ mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
mItemTouchHelper = new ItemTouchHelper(mCallbacks);
setHasStableIds(true);
}
@Override
public long getItemId(int position) {
- return mTiles.get(position) != null ? mAllTiles.indexOf(mTiles.get(position)) : -1;
+ return mTiles.get(position) != null ? mAllTiles.indexOf(mTiles.get(position))
+ : position == mEditIndex ? EDIT_ID : DIVIDER_ID;
}
public ItemTouchHelper getItemTouchHelper() {
@@ -83,7 +105,7 @@
public void saveSpecs(QSTileHost host) {
List<String> newSpecs = new ArrayList<>();
- for (int i = 0; mTiles.get(i) != null; i++) {
+ for (int i = 0; i < mTiles.size() && mTiles.get(i) != null; i++) {
newSpecs.add(mTiles.get(i).spec);
}
host.changeTiles(mCurrentSpecs, newSpecs);
@@ -114,8 +136,19 @@
}
}
mTiles.add(null);
+ for (int i = 0; i < mOtherTiles.size(); i++) {
+ final TileInfo tile = mOtherTiles.get(i);
+ if (tile.isSystem) {
+ mOtherTiles.remove(i--);
+ mTiles.add(tile);
+ }
+ }
+ if (mOtherTiles.size() != 0) {
+ mTileDividerIndex = mTiles.size();
+ mTiles.add(null);
+ }
mTiles.addAll(mOtherTiles);
- mDividerIndex = mTiles.indexOf(null);
+ mEditIndex = mTiles.indexOf(null);
notifyDataSetChanged();
}
@@ -130,6 +163,12 @@
@Override
public int getItemViewType(int position) {
+ if (mAccessibilityMoving && position == mEditIndex - 1) {
+ return TYPE_ACCESSIBLE_DROP;
+ }
+ if (position == mTileDividerIndex) {
+ return TYPE_DIVIDER;
+ }
if (mTiles.get(position) == null) {
return TYPE_EDIT;
}
@@ -140,12 +179,15 @@
public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
final Context context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
- if (viewType == 1) {
+ if (viewType == TYPE_DIVIDER) {
+ return new Holder(inflater.inflate(R.layout.qs_customize_tile_divider, parent, false));
+ }
+ if (viewType == TYPE_EDIT) {
return new Holder(inflater.inflate(R.layout.qs_customize_divider, parent, false));
}
FrameLayout frame = (FrameLayout) inflater.inflate(R.layout.qs_customize_tile_frame, parent,
false);
- frame.addView(new QSTileView(context, new QSIconView(context)));
+ frame.addView(new CustomizeTileView(context, new QSIconView(context)));
return new Holder(frame);
}
@@ -155,29 +197,222 @@
}
@Override
- public void onBindViewHolder(final Holder holder, int position) {
+ public void onBindViewHolder(final Holder holder, final int position) {
+ if (holder.getItemViewType() == TYPE_DIVIDER) {
+ return;
+ }
if (holder.getItemViewType() == TYPE_EDIT) {
((TextView) holder.itemView.findViewById(android.R.id.title)).setText(
mCurrentDrag != null ? R.string.drag_to_remove_tiles
: R.string.drag_to_add_tiles);
return;
}
+ if (holder.getItemViewType() == TYPE_ACCESSIBLE_DROP) {
+ holder.mTileView.setClickable(true);
+ holder.mTileView.setFocusable(true);
+ holder.mTileView.setFocusableInTouchMode(true);
+ holder.mTileView.setVisibility(View.VISIBLE);
+ holder.mTileView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+ holder.mTileView.setContentDescription(mContext.getString(
+ R.string.accessibility_qs_edit_position_label, position + 1));
+ holder.mTileView.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ selectPosition(position, v);
+ }
+ });
+ if (mNeedsFocus) {
+ // Wait for this to get laid out then set its focus.
+ holder.mTileView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ holder.mTileView.removeOnLayoutChangeListener(this);
+ holder.mTileView.requestFocus();
+ }
+ });
+ mNeedsFocus = false;
+ }
+ return;
+ }
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 (mAccessibilityMoving) {
+ info.state.contentDescription = mContext.getString(
+ R.string.accessibility_qs_edit_position_label, position + 1);
+ } else {
+ info.state.contentDescription = mContext.getString(
+ R.string.accessibility_qs_edit_tile_label, position + 1, info.state.label);
+ }
holder.mTileView.onStateChanged(info.state);
+ holder.mTileView.setAppLabel(info.appLabel);
+ holder.mTileView.setShowAppLabel(position > mTileDividerIndex);
+
+ if (mAccessibilityManager.isTouchExplorationEnabled()) {
+ final boolean selectable = !mAccessibilityMoving || position < mEditIndex;
+ holder.mTileView.setClickable(selectable);
+ holder.mTileView.setFocusable(selectable);
+ holder.mTileView.setImportantForAccessibility(selectable
+ ? View.IMPORTANT_FOR_ACCESSIBILITY_YES
+ : View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+ if (selectable) {
+ holder.mTileView.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mAccessibilityMoving) {
+ selectPosition(position, v);
+ } else {
+ if (position < mEditIndex) {
+ showAccessibilityDialog(position, v);
+ } else {
+ startAccessibleDrag(position);
+ }
+ }
+ }
+ });
+ }
+ }
+ }
+
+ private void selectPosition(int position, View v) {
+ // Remove the placeholder.
+ mTiles.remove(mEditIndex--);
+ mAccessibilityMoving = false;
+ move(mAccessibilityFromIndex, position, v);
+ notifyDataSetChanged();
+ }
+
+ private void showAccessibilityDialog(final int position, final View v) {
+ 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) {
+ startAccessibleDrag(position);
+ } else {
+ move(position, mEditIndex, v);
+ }
+ }
+ }).setNegativeButton(android.R.string.cancel, null)
+ .create();
+ SystemUIDialog.setShowForAllUsers(dialog, true);
+ SystemUIDialog.applyFlags(dialog);
+ dialog.show();
+ }
+
+ private void startAccessibleDrag(int position) {
+ mAccessibilityMoving = true;
+ mNeedsFocus = true;
+ mAccessibilityFromIndex = position;
+ // Add placeholder for last slot.
+ mTiles.add(mEditIndex++, null);
+ notifyDataSetChanged();
}
public SpanSizeLookup getSizeLookup() {
return mSizeLookup;
}
+ private boolean move(int from, int to, View v) {
+ if (to >= mEditIndex) {
+ if (from >= mEditIndex) {
+ return false;
+ }
+ // Sort tiles into system/non-system groups.
+ TileInfo tile = mTiles.get(from);
+ if (tile.isSystem) {
+ if (to > mTileDividerIndex) {
+ to = mTileDividerIndex;
+ }
+ } else {
+ if (mTileDividerIndex == mTiles.size()) {
+ mTiles.add(null);
+ }
+ if (to <= mTileDividerIndex) {
+ to = mTileDividerIndex;
+ }
+ }
+ }
+ CharSequence fromLabel = mTiles.get(from).state.label;
+ move(from, to, mTiles);
+ notifyDataSetChanged();
+ updateDividerLocations();
+ CharSequence announcement;
+ if (to >= mEditIndex) {
+ MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_REMOVE_SPEC,
+ strip(mTiles.get(to)));
+ MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_REMOVE,
+ from);
+ announcement = mContext.getString(R.string.accessibility_qs_edit_tile_removed,
+ fromLabel);
+ } else if (from >= mEditIndex) {
+ MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_ADD_SPEC,
+ strip(mTiles.get(to)));
+ MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_ADD,
+ to);
+ announcement = mContext.getString(R.string.accessibility_qs_edit_tile_added,
+ fromLabel, (to + 1));
+ } else {
+ MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_MOVE_SPEC,
+ strip(mTiles.get(to)));
+ MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_MOVE,
+ to);
+ announcement = mContext.getString(R.string.accessibility_qs_edit_tile_moved,
+ fromLabel, (to + 1));
+ }
+ v.announceForAccessibility(announcement);
+ return true;
+ }
+
+ private void updateDividerLocations() {
+ // The first null is the edit tiles label, the second null is the tile divider.
+ // If there is no second null, then there are no non-system tiles.
+ mEditIndex = -1;
+ mTileDividerIndex = mTiles.size();
+ for (int i = 0; i < mTiles.size(); i++) {
+ if (mTiles.get(i) == null) {
+ if (mEditIndex == -1) {
+ mEditIndex = i;
+ } else {
+ mTileDividerIndex = i;
+ }
+ }
+ }
+ if (mTiles.get(mTiles.size() - 1) == null) {
+ mTiles.remove(mTiles.size() - 1);
+ }
+ }
+
+ private String strip(TileInfo tileInfo) {
+ String spec = tileInfo.spec;
+ if (spec.startsWith(CustomTile.PREFIX)) {
+ ComponentName component = CustomTile.getComponentFromSpec(spec);
+ return component.getPackageName();
+ }
+ return spec;
+ }
+
+ private <T> void move(int from, int to, List<T> list) {
+ list.add(from > to ? to : to + 1, list.get(from));
+ list.remove(from > to ? from + 1 : from);
+ }
+
public class Holder extends ViewHolder {
- private QSTileView mTileView;
+ private CustomizeTileView mTileView;
public Holder(View itemView) {
super(itemView);
if (itemView instanceof FrameLayout) {
- mTileView = (QSTileView) ((FrameLayout) itemView).getChildAt(0);
+ mTileView = (CustomizeTileView) ((FrameLayout) itemView).getChildAt(0);
mTileView.setBackground(null);
mTileView.getIcon().disableAnimation();
}
@@ -191,6 +426,9 @@
mTileView.findViewById(R.id.tile_label).animate()
.setDuration(DRAG_LENGTH)
.alpha(0);
+ mTileView.getAppLabel().animate()
+ .setDuration(DRAG_LENGTH)
+ .alpha(0);
}
public void stopDrag() {
@@ -201,13 +439,17 @@
mTileView.findViewById(R.id.tile_label).animate()
.setDuration(DRAG_LENGTH)
.alpha(1);
+ mTileView.getAppLabel().animate()
+ .setDuration(DRAG_LENGTH)
+ .alpha(.6f);
}
}
private final SpanSizeLookup mSizeLookup = new SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
- return getItemViewType(position) == TYPE_EDIT ? 3 : 1;
+ final int type = getItemViewType(position);
+ return type == TYPE_EDIT || type == TYPE_DIVIDER ? 3 : 1;
}
};
@@ -225,7 +467,7 @@
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final ViewHolder holder = parent.getChildViewHolder(child);
- if (holder.getAdapterPosition() < mDividerIndex) {
+ if (holder.getAdapterPosition() < mEditIndex) {
continue;
}
@@ -267,7 +509,7 @@
mHandler.post(new Runnable() {
@Override
public void run() {
- notifyItemChanged(mDividerIndex);
+ notifyItemChanged(mEditIndex);
}
});
}
@@ -286,20 +528,7 @@
public boolean onMove(RecyclerView recyclerView, ViewHolder viewHolder, ViewHolder target) {
int from = viewHolder.getAdapterPosition();
int to = target.getAdapterPosition();
- if (to > mDividerIndex) {
- if (from >= mDividerIndex) {
- return false;
- }
- }
- move(from, to, mTiles);
- mDividerIndex = mTiles.indexOf(null);
- notifyItemMoved(from, to);
- return true;
- }
-
- private <T> void move(int from, int to, List<T> list) {
- list.add(from > to ? to : to + 1, list.get(from));
- list.remove(from > to ? from + 1 : from);
+ return move(from, to, target.itemView);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index d95d3ef..d04a2fc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -31,9 +31,9 @@
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
import com.android.systemui.qs.QSTile.DrawableIcon;
+import com.android.systemui.qs.QSTile.State;
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.statusbar.phone.QSTileHost;
-import com.android.systemui.tuner.TunerService;
import java.util.ArrayList;
import java.util.Collection;
@@ -75,10 +75,12 @@
public void run() {
final QSTile.State state = tile.newTileState();
tile.getState().copyTo(state);
+ // Ignore the current state and get the generic label instead.
+ state.label = tile.getTileLabel();
mainHandler.post(new Runnable() {
@Override
public void run() {
- addTile(spec, state);
+ addTile(spec, null, state, true);
mListener.onTilesChanged(mTiles);
}
});
@@ -102,28 +104,33 @@
mListener = listener;
}
- private void addTile(String spec, QSTile.State state) {
+ private void addTile(String spec, CharSequence appLabel, State state, boolean isSystem) {
if (mSpecs.contains(spec)) {
return;
}
TileInfo info = new TileInfo();
info.state = state;
info.spec = spec;
+ info.appLabel = appLabel;
+ info.isSystem = isSystem;
mTiles.add(info);
mSpecs.add(spec);
}
- private void addTile(String spec, Drawable drawable, CharSequence label, Context context) {
+ private void addTile(String spec, Drawable drawable, CharSequence label, CharSequence appLabel,
+ Context context) {
QSTile.State state = new QSTile.State();
state.label = label;
state.contentDescription = label;
state.icon = new DrawableIcon(drawable);
- addTile(spec, state);
+ addTile(spec, appLabel, state, false);
}
public static class TileInfo {
public String spec;
+ public CharSequence appLabel;
public QSTile.State state;
+ public boolean isSystem;
}
private class QueryTilesTask extends AsyncTask<Void, Void, Collection<TileInfo>> {
@@ -146,7 +153,8 @@
icon.setTint(mContext.getColor(android.R.color.white));
}
CharSequence label = info.serviceInfo.loadLabel(pm);
- addTile(spec, icon, label != null ? label.toString() : "null", mContext);
+ final CharSequence appLabel = info.serviceInfo.applicationInfo.loadLabel(pm);
+ addTile(spec, icon, label != null ? label.toString() : "null", appLabel, mContext);
}
return tiles;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 9156f3a..6114573 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -221,6 +221,11 @@
}
@Override
+ public CharSequence getTileLabel() {
+ return getState().label;
+ }
+
+ @Override
protected void handleUpdateState(State state, Object arg) {
Drawable drawable = mTile.getIcon().loadDrawable(mContext);
int tileState = mTile.getState();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index 89f1985b..5f5a87e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -76,6 +76,11 @@
}
@Override
+ public CharSequence getTileLabel() {
+ return mContext.getString(R.string.airplane_mode);
+ }
+
+ @Override
protected void handleUpdateState(BooleanState state, Object arg) {
final int value = arg instanceof Integer ? (Integer)arg : mSetting.getValue();
final boolean airplaneMode = value != 0;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
index 2e87525..2032783 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
@@ -21,6 +21,7 @@
import android.content.IntentFilter;
import android.graphics.drawable.Drawable;
import android.os.Handler;
+import android.os.Looper;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.RelativeSizeSpan;
@@ -44,7 +45,6 @@
public class BatteryTile extends QSTile<QSTile.State> implements BatteryController.BatteryStateChangeCallback {
- private final BatteryMeterDrawable mDrawable;
private final BatteryController mBatteryController;
private final BatteryDetail mBatteryDetail = new BatteryDetail();
@@ -52,13 +52,11 @@
private boolean mPowerSave;
private boolean mCharging;
private boolean mDetailShown;
+ private boolean mPluggedIn;
public BatteryTile(Host host) {
super(host);
mBatteryController = host.getBatteryController();
- mDrawable = new BatteryMeterDrawable(host.getContext(), new Handler(),
- host.getContext().getColor(R.color.batterymeter_frame_color));
- mDrawable.setBatteryController(mBatteryController);
}
@Override
@@ -79,10 +77,8 @@
@Override
public void setListening(boolean listening) {
if (listening) {
- mDrawable.startListening();
mBatteryController.addStateChangedCallback(this);
} else {
- mDrawable.stopListening();
mBatteryController.removeStateChangedCallback(this);
}
}
@@ -106,6 +102,11 @@
}
@Override
+ public CharSequence getTileLabel() {
+ return mContext.getString(R.string.battery);
+ }
+
+ @Override
protected void handleUpdateState(State state, Object arg) {
int level = (arg != null) ? (Integer) arg : mLevel;
String percentage = NumberFormat.getPercentInstance().format((double) level / 100.0);
@@ -113,7 +114,12 @@
state.icon = new Icon() {
@Override
public Drawable getDrawable(Context context) {
- return mDrawable;
+ BatteryMeterDrawable drawable =
+ new BatteryMeterDrawable(context, new Handler(Looper.getMainLooper()),
+ context.getColor(R.color.batterymeter_frame_color));
+ drawable.onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
+ drawable.onPowerSaveChanged(mPowerSave);
+ return drawable;
}
@Override
@@ -128,6 +134,7 @@
@Override
public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
mLevel = level;
+ mPluggedIn = pluggedIn;
mCharging = charging;
refreshState((Integer) level);
if (mDetailShown) {
@@ -138,6 +145,7 @@
@Override
public void onPowerSaveChanged(boolean isPowerSave) {
mPowerSave = isPowerSave;
+ refreshState(null);
if (mDetailShown) {
mBatteryDetail.postBindView();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 80f667c..1fef8f1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -96,6 +96,11 @@
}
@Override
+ public CharSequence getTileLabel() {
+ return mContext.getString(R.string.quick_settings_bluetooth_label);
+ }
+
+ @Override
protected void handleUpdateState(BooleanState state, Object arg) {
final boolean enabled = mController.isBluetoothEnabled();
final boolean connected = mController.isBluetoothConnected();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index e0ad002..bea1e15 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -108,6 +108,11 @@
}
@Override
+ public CharSequence getTileLabel() {
+ return mContext.getString(R.string.quick_settings_cast_title);
+ }
+
+ @Override
protected void handleUpdateState(BooleanState state, Object arg) {
state.label = mContext.getString(R.string.quick_settings_cast_title);
state.value = false;
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 5f87741..55b00b5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -101,6 +101,11 @@
}
@Override
+ public CharSequence getTileLabel() {
+ return mContext.getString(R.string.quick_settings_cellular_detail_title);
+ }
+
+ @Override
protected void handleUpdateState(SignalState state, Object arg) {
CallbackInfo cb = (CallbackInfo) arg;
if (cb == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index a608316..416132e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -85,6 +85,11 @@
}
@Override
+ public CharSequence getTileLabel() {
+ return mContext.getString(R.string.quick_settings_inversion_label);
+ }
+
+ @Override
protected void handleUpdateState(BooleanState state, Object arg) {
final int value = arg instanceof Integer ? (Integer) arg : mSetting.getValue();
final boolean enabled = value != 0;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index 74b3fdc..35aff21 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -57,6 +57,11 @@
}
@Override
+ public CharSequence getTileLabel() {
+ return mContext.getString(R.string.data_saver);
+ }
+
+ @Override
protected void handleUpdateState(BooleanState state, Object arg) {
state.value = arg instanceof Boolean ? (Boolean) arg
: mDataSaverController.isDataSaverEnabled();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 8b22868..11efd56 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -128,6 +128,11 @@
}
@Override
+ public CharSequence getTileLabel() {
+ return mContext.getString(R.string.quick_settings_dnd_label);
+ }
+
+ @Override
protected void handleUpdateState(BooleanState state, Object arg) {
final int zen = arg instanceof Integer ? (Integer) arg : mController.getZen();
final boolean newValue = zen != Global.ZEN_MODE_OFF;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index a01a9a5..69e71bc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -87,6 +87,16 @@
}
@Override
+ public CharSequence getTileLabel() {
+ return mContext.getString(R.string.quick_settings_flashlight_label);
+ }
+
+ @Override
+ protected void handleLongClick() {
+ handleClick();
+ }
+
+ @Override
protected void handleUpdateState(BooleanState state, Object arg) {
state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label);
if (!mFlashlightController.isAvailable()) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index da93120..bf5b22c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -74,6 +74,11 @@
}
@Override
+ public CharSequence getTileLabel() {
+ return mContext.getString(R.string.quick_settings_hotspot_label);
+ }
+
+ @Override
protected void handleUpdateState(BooleanState state, Object arg) {
state.label = mContext.getString(R.string.quick_settings_hotspot_label);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
index bb5ff8e..2a2cc46 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
@@ -119,6 +119,11 @@
}
@Override
+ public CharSequence getTileLabel() {
+ return getState().label;
+ }
+
+ @Override
protected void handleUpdateState(State state, Object arg) {
Intent intent = (Intent) arg;
if (intent == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index b1d1c77..6286f5e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -89,6 +89,11 @@
}
@Override
+ public CharSequence getTileLabel() {
+ return mContext.getString(R.string.quick_settings_location_label);
+ }
+
+ @Override
protected void handleUpdateState(BooleanState state, Object arg) {
final boolean locationEnabled = mController.isLocationEnabled();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index d80ca10..38b3706 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -80,6 +80,11 @@
}
@Override
+ public CharSequence getTileLabel() {
+ return getState().label;
+ }
+
+ @Override
protected void handleUpdateState(BooleanState state, Object arg) {
if (mController == null) return;
final boolean rotationLocked = arg != null ? (Boolean) arg
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
index f1066c1..5b4279c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
@@ -73,6 +73,11 @@
}
@Override
+ public CharSequence getTileLabel() {
+ return getState().label;
+ }
+
+ @Override
protected void handleUpdateState(State state, Object arg) {
final Pair<String, Drawable> p = arg != null ? (Pair<String, Drawable>) arg : mLastUpdate;
if (p != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 7ee795f..c72bbf3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -119,6 +119,11 @@
}
@Override
+ public CharSequence getTileLabel() {
+ return mContext.getString(R.string.quick_settings_wifi_label);
+ }
+
+ @Override
protected void handleUpdateState(SignalState state, Object arg) {
if (DEBUG) Log.d(TAG, "handleUpdateState arg=" + arg);
CallbackInfo cb = (CallbackInfo) arg;
@@ -149,7 +154,7 @@
state.label = removeDoubleQuotes(cb.enabledDesc);
signalContentDescription = cb.wifiSignalContentDescription;
} else if (wifiNotConnected) {
- state.icon = ResourceIcon.get(R.drawable.ic_qs_wifi_full_0);
+ state.icon = ResourceIcon.get(R.drawable.ic_qs_wifi_disconnected);
state.label = r.getString(R.string.quick_settings_wifi_label);
signalContentDescription = r.getString(R.string.accessibility_no_wifi);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index 421a2cf..2c5f7d5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -82,6 +82,11 @@
}
@Override
+ public CharSequence getTileLabel() {
+ return mContext.getString(R.string.quick_settings_work_mode_label);
+ }
+
+ @Override
protected void handleUpdateState(BooleanState state, Object arg) {
if (arg instanceof Boolean) {
state.value = (Boolean) arg;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 3fb3106..b1d9555 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -34,6 +34,7 @@
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.MetricsProto.MetricsEvent;
@@ -43,6 +44,7 @@
import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
import com.android.systemui.recents.events.activity.DebugFlagsChangedEvent;
import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
+import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent;
import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
import com.android.systemui.recents.events.activity.ExitRecentsWindowFirstAnimationFrameEvent;
@@ -71,10 +73,14 @@
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.views.AnimationProps;
import com.android.systemui.recents.views.RecentsView;
import com.android.systemui.recents.views.SystemBarScrimViews;
import com.android.systemui.statusbar.BaseStatusBar;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
/**
* The main Recents activity that is started from AlternateRecentsComponent.
*/
@@ -87,6 +93,7 @@
private RecentsPackageMonitor mPackageMonitor;
private long mLastTabKeyEventTime;
+ private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED;
private boolean mFinishedOnStartup;
private boolean mIgnoreAltTabRelease;
private boolean mIsVisible;
@@ -263,6 +270,7 @@
getWindow().getAttributes().privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
+ mLastOrientation = getResources().getConfiguration().orientation;
mFocusTimerDuration = getResources().getInteger(R.integer.recents_auto_advance_duration);
mIterateTrigger = new DozeTrigger(mFocusTimerDuration, new Runnable() {
@Override
@@ -271,6 +279,9 @@
}
});
+ // Set the window background
+ getWindow().setBackgroundDrawable(mRecentsView.getBackgroundScrim());
+
// Create the home intent runnable
mHomeIntent = new Intent(Intent.ACTION_MAIN, null);
mHomeIntent.addCategory(Intent.CATEGORY_HOME);
@@ -281,6 +292,11 @@
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);
registerReceiver(mSystemBroadcastReceiver, filter);
+
+ getWindow().addPrivateFlags(LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION);
+
+ // Reload the stack view
+ reloadStackView();
}
@Override
@@ -293,15 +309,17 @@
}
@Override
- public void onEnterAnimationComplete() {
- super.onEnterAnimationComplete();
- EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+
+ // Reload the stack view
+ reloadStackView();
}
- @Override
- protected void onResume() {
- super.onResume();
-
+ /**
+ * Reloads the stack views upon launching Recents.
+ */
+ private void reloadStackView() {
// If the Recents component has preloaded a load plan, then use that to prevent
// reconstructing the task stack
RecentsTaskLoader loader = Recents.getTaskLoader();
@@ -324,38 +342,21 @@
loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
loader.loadTasks(this, loadPlan, loadOpts);
TaskStack stack = loadPlan.getTaskStack();
- mRecentsView.onResume(mIsVisible, false /* multiWindowChange */, stack);
+ mRecentsView.onReload(mIsVisible, stack.getTaskCount() == 0);
+ mRecentsView.updateStack(stack);
- // Animate the SystemUI scrims into view
- Task launchTarget = stack.getLaunchTarget();
- int taskCount = stack.getTaskCount();
- int launchTaskIndexInStack = launchTarget != null
- ? stack.indexOfStackTask(launchTarget)
- : 0;
- boolean hasNavBarScrim = (taskCount > 0) && !config.hasTransposedNavBar;
- boolean animateNavBarScrim = !launchState.launchedWhileDocking;
- mScrimViews.prepareEnterRecentsAnimation(hasNavBarScrim, animateNavBarScrim);
+ // Update the nav bar scrim, but defer the animation until the enter-window event
+ boolean animateNavBarScrim = !launchState.launchedViaDockGesture;
+ mScrimViews.updateNavBarScrim(animateNavBarScrim, stack.getTaskCount() > 0, null);
- // If this is a new instance from a configuration change, then we have to manually trigger
- // the enter animation state, or if recents was relaunched by AM, without going through
- // the normal mechanisms
+ // If this is a new instance relaunched by AM, without going through the normal mechanisms,
+ // then we have to manually trigger the enter animation state
boolean wasLaunchedByAm = !launchState.launchedFromHome &&
!launchState.launchedFromApp;
- if (launchState.launchedHasConfigurationChanged || wasLaunchedByAm) {
+ if (wasLaunchedByAm) {
EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
}
- mRecentsView.getViewTreeObserver().addOnPreDrawListener(
- new ViewTreeObserver.OnPreDrawListener() {
-
- @Override
- public boolean onPreDraw() {
- mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this);
- EventBus.getDefault().post(new RecentsDrawnEvent());
- return true;
- }
- });
-
// Keep track of whether we launched from the nav bar button or via alt-tab
if (launchState.launchedWithAltTab) {
MetricsLogger.count(this, "overview_trigger_alttab", 1);
@@ -365,6 +366,10 @@
// Keep track of whether we launched from an app or from home
if (launchState.launchedFromApp) {
+ Task launchTarget = stack.getLaunchTarget();
+ int launchTaskIndexInStack = launchTarget != null
+ ? stack.indexOfStackTask(launchTarget)
+ : 0;
MetricsLogger.count(this, "overview_source_app", 1);
// If from an app, track the stack index of the app in the stack (for affiliated tasks)
MetricsLogger.histogram(this, "overview_source_app_index", launchTaskIndexInStack);
@@ -373,6 +378,7 @@
}
// Keep track of the total stack task count
+ int taskCount = mRecentsView.getStack().getTaskCount();
MetricsLogger.histogram(this, "overview_task_count", taskCount);
// After we have resumed, set the visible state until the next onStop() call
@@ -380,6 +386,29 @@
}
@Override
+ public void onEnterAnimationComplete() {
+ super.onEnterAnimationComplete();
+ EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ // Notify of the next draw
+ mRecentsView.getViewTreeObserver().addOnPreDrawListener(
+ new ViewTreeObserver.OnPreDrawListener() {
+
+ @Override
+ public boolean onPreDraw() {
+ mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this);
+ EventBus.getDefault().post(new RecentsDrawnEvent());
+ return true;
+ }
+ });
+ }
+
+ @Override
protected void onPause() {
super.onPause();
@@ -391,7 +420,43 @@
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- EventBus.getDefault().post(new ConfigurationChangedEvent());
+
+ // Notify of the config change
+ int newOrientation = getResources().getConfiguration().orientation;
+ int numStackTasks = mRecentsView.getStack().getStackTaskCount();
+ EventBus.getDefault().send(new ConfigurationChangedEvent(false /* fromMultiWindow */,
+ (mLastOrientation != newOrientation), numStackTasks > 0));
+ mLastOrientation = newOrientation;
+ }
+
+ @Override
+ public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {
+ super.onMultiWindowModeChanged(isInMultiWindowMode);
+
+ // Reload the task stack completely
+ RecentsConfiguration config = Recents.getConfiguration();
+ RecentsActivityLaunchState launchState = config.getLaunchState();
+ RecentsTaskLoader loader = Recents.getTaskLoader();
+ RecentsTaskLoadPlan loadPlan = loader.createLoadPlan(this);
+ loader.preloadTasks(loadPlan, -1 /* topTaskId */, false /* isTopTaskHome */);
+
+ RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
+ loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
+ loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
+ loader.loadTasks(this, loadPlan, loadOpts);
+
+ TaskStack stack = loadPlan.getTaskStack();
+ int numStackTasks = stack.getStackTaskCount();
+
+ EventBus.getDefault().send(new ConfigurationChangedEvent(true /* fromMultiWindow */,
+ false /* fromOrientationChange */, numStackTasks > 0));
+
+ if (mRecentsView != null) {
+ mRecentsView.updateStack(stack);
+ }
+
+ EventBus.getDefault().send(new MultiWindowStateChangedEvent(isInMultiWindowMode,
+ numStackTasks > 0));
}
@Override
@@ -450,28 +515,6 @@
}
@Override
- public void onMultiWindowChanged(boolean inMultiWindow) {
- super.onMultiWindowChanged(inMultiWindow);
- EventBus.getDefault().send(new ConfigurationChangedEvent());
-
- // Reload the task stack completely
- RecentsConfiguration config = Recents.getConfiguration();
- RecentsActivityLaunchState launchState = config.getLaunchState();
- RecentsTaskLoader loader = Recents.getTaskLoader();
- RecentsTaskLoadPlan loadPlan = loader.createLoadPlan(this);
- loader.preloadTasks(loadPlan, -1 /* topTaskId */, false /* isTopTaskHome */);
-
- RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
- loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
- loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
- loader.loadTasks(this, loadPlan, loadOpts);
-
- mRecentsView.onResume(mIsVisible, true /* multiWindowChange */, loadPlan.getTaskStack());
-
- EventBus.getDefault().send(new MultiWindowStateChangedEvent(inMultiWindow));
- }
-
- @Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_TAB: {
@@ -602,6 +645,11 @@
mRecentsView.invalidate();
}
+ public final void onBusEvent(DockedFirstAnimationFrameEvent event) {
+ mRecentsView.getViewTreeObserver().addOnPreDrawListener(this);
+ mRecentsView.invalidate();
+ }
+
public final void onBusEvent(CancelEnterRecentsWindowAnimationEvent event) {
RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
int launchToTaskId = launchState.launchedToTaskId;
@@ -688,4 +736,20 @@
});
return true;
}
+
+ @Override
+ public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ super.dump(prefix, fd, writer, args);
+ String id = Integer.toHexString(System.identityHashCode(this));
+
+ writer.print(prefix); writer.print(TAG);
+ writer.print(" visible="); writer.print(mIsVisible ? "Y" : "N");
+ writer.print(" [0x"); writer.print(id); writer.print("]");
+ writer.println();
+
+ if (mRecentsView != null) {
+ mRecentsView.dump(prefix, writer);
+ }
+ EventBus.getDefault().dump(prefix, writer);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
index ab3b79e..7161053 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
@@ -29,12 +29,9 @@
public boolean launchedWithAltTab;
public boolean launchedFromApp;
- public boolean launchedFromAppDocked;
public boolean launchedFromHome;
- public boolean launchedReuseTaskStackViews;
- public boolean launchedHasConfigurationChanged;
public boolean launchedViaDragGesture;
- public boolean launchedWhileDocking;
+ public boolean launchedViaDockGesture;
public int launchedToTaskId;
public int launchedNumVisibleTasks;
public int launchedNumVisibleThumbnails;
@@ -42,23 +39,10 @@
public void reset() {
launchedFromHome = false;
launchedFromApp = false;
- launchedFromAppDocked = false;
launchedToTaskId = -1;
launchedWithAltTab = false;
- launchedHasConfigurationChanged = false;
launchedViaDragGesture = false;
- launchedWhileDocking = false;
- }
-
- /** Called when the configuration has changed, and we want to reset any configuration specific
- * members. */
- public void updateOnConfigurationChange() {
- // Reset this flag on configuration change to ensure that we recreate new task views
- launchedReuseTaskStackViews = false;
- // Set this flag to indicate that the configuration has changed since Recents last launched
- launchedHasConfigurationChanged = true;
- launchedViaDragGesture = false;
- launchedWhileDocking = false;
+ launchedViaDockGesture = false;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 2afb09a..73c6e6e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -47,11 +47,6 @@
// Launch states
public RecentsActivityLaunchState mLaunchState = new RecentsActivityLaunchState();
- // TODO: Values determined by the current context, needs to be refactored into something that is
- // agnostic of the activity context, but still calculable from the Recents component for
- // the transition into recents
- public boolean hasTransposedNavBar;
-
// Since the positions in Recents has to be calculated globally (before the RecentsActivity
// starts), we need to calculate some resource values ourselves, instead of relying on framework
// resources.
@@ -79,25 +74,10 @@
}
/**
- * Updates the configuration based on the current state of the system
- */
- void update(Rect systemInsets) {
- hasTransposedNavBar = systemInsets.right > 0;
- }
-
- /**
* Returns the activity launch state.
* TODO: This will be refactored out of RecentsConfiguration.
*/
public RecentsActivityLaunchState getLaunchState() {
return mLaunchState;
}
-
- /**
- * Called when the configuration has changed, and we want to reset any configuration specific
- * members.
- */
- public void updateOnConfigurationChange() {
- mLaunchState.updateOnConfigurationChange();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
index f67f099..1715356 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
@@ -34,9 +34,9 @@
// This disables the bitmap and icon caches
public static final boolean DisableBackgroundCache = false;
// Enables the task affiliations
- public static final boolean EnableAffiliatedTaskGroups = true;
+ public static final boolean EnableAffiliatedTaskGroups = false;
// TODO: To be repurposed
- public static final boolean EnableStackActionButton = false;
+ public static final boolean EnableStackActionButton = true;
// Overrides the Tuner flags and enables the timeout
private static final boolean EnableFastToggleTimeout = false;
// Overrides the Tuner flags and enables the paging via the Recents button
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 58216b0..aba05aa 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -45,6 +45,7 @@
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
+import com.android.systemui.recents.events.activity.ForcedResizableEvent;
import com.android.systemui.recents.events.activity.HideRecentsEvent;
import com.android.systemui.recents.events.activity.IterateRecentsEvent;
import com.android.systemui.recents.events.activity.LaunchNextTaskRequestEvent;
@@ -124,6 +125,13 @@
loader.loadTasks(mContext, plan, launchOpts);
}
}
+
+ @Override
+ public void onActivityForcedResizable(String packageName, int taskId) {
+ EventBus.getDefault().sendOntoMainThread(
+ new ForcedResizableEvent(packageName, taskId));
+
+ }
}
protected static RecentsTaskLoadPlan sInstanceLoadPlan;
@@ -131,13 +139,11 @@
protected Context mContext;
protected Handler mHandler;
TaskStackListenerImpl mTaskStackListener;
- protected boolean mCanReuseTaskStackViews = true;
boolean mDraggingInRecents;
boolean mLaunchedWhileDocking;
// Task launching
Rect mTaskStackBounds = new Rect();
- Rect mLastTaskViewBounds = new Rect();
TaskViewTransform mTmpTransform = new TaskViewTransform();
int mStatusBarHeight;
int mNavBarHeight;
@@ -162,8 +168,7 @@
}
});
- protected Bitmap mThumbnailTransitionBitmapCache;
- Task mThumbnailTransitionBitmapCacheKey;
+ protected Bitmap mThumbTransitionBitmapCache;
public RecentsImpl(Context context) {
mContext = context;
@@ -179,7 +184,6 @@
// Initialize the static configuration resources
reloadHeaderBarLayout();
- updateHeaderBarLayout(null /* stack */);
// When we start, preload the data associated with the previous recent tasks.
// We can use a new plan since the caches will be the same.
@@ -194,15 +198,11 @@
}
public void onBootCompleted() {
- updateHeaderBarLayout(null /* stack */);
+ // Do nothing
}
public void onConfigurationChanged() {
reloadHeaderBarLayout();
- updateHeaderBarLayout(null /* stack */);
- // Don't reuse task stack views if the configuration changes
- mCanReuseTaskStackViews = false;
- Recents.getConfiguration().updateOnConfigurationChange();
}
/**
@@ -364,9 +364,14 @@
loader.preloadTasks(sInstanceLoadPlan, topTask.id, topTaskHome.value);
TaskStack stack = sInstanceLoadPlan.getTaskStack();
if (stack.getTaskCount() > 0) {
- // We try and draw the thumbnail transition bitmap in parallel before
- // toggle/show recents is called
- preCacheThumbnailTransitionBitmapAsync(topTask, stack, mDummyStackView);
+ // Only preload the icon (but not the thumbnail since it may not have been taken for
+ // the pausing activity)
+ preloadIcon(topTask);
+
+ // At this point, we don't know anything about the stack state. So only calculate
+ // the dimensions of the thumbnail that we need for the transition into Recents, but
+ // do not draw it until we construct the activity options when we start Recents
+ updateHeaderBarLayout(stack);
}
}
}
@@ -432,7 +437,7 @@
}
// Launch the task
- ssp.startActivityFromRecents(mContext, toTask.key.id, toTask.title, launchOpts);
+ ssp.startActivityFromRecents(mContext, toTask.key, toTask.title, launchOpts);
}
/**
@@ -504,7 +509,7 @@
MetricsLogger.count(mContext, "overview_affiliated_task_launch", 1);
// Launch the task
- ssp.startActivityFromRecents(mContext, toTask.key.id, toTask.title, launchOpts);
+ ssp.startActivityFromRecents(mContext, toTask.key, toTask.title, launchOpts);
}
public void showNextAffiliatedTask() {
@@ -557,8 +562,13 @@
com.android.internal.R.dimen.navigation_bar_height);
mNavBarWidth = res.getDimensionPixelSize(
com.android.internal.R.dimen.navigation_bar_width);
- mTaskBarHeight = res.getDimensionPixelSize(
- R.dimen.recents_task_view_header_height);
+ mTaskBarHeight = TaskStackLayoutAlgorithm.getDimensionForDevice(res,
+ R.dimen.recents_task_view_header_height,
+ R.dimen.recents_task_view_header_height,
+ R.dimen.recents_task_view_header_height,
+ R.dimen.recents_task_view_header_height_tablet_land,
+ R.dimen.recents_task_view_header_height,
+ R.dimen.recents_task_view_header_height_tablet_land);
mDummyStackView = new TaskStackView(mContext);
mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header,
null, false);
@@ -575,36 +585,47 @@
Rect systemInsets = new Rect();
ssp.getStableInsets(systemInsets);
Rect windowRect = ssp.getWindowRect();
+ // When docked, the nav bar insets are consumed and the activity is measured without insets.
+ // However, the window bounds include the insets, so we need to subtract them here to make
+ // them identical.
+ if (ssp.hasDockedTask()) {
+ windowRect.bottom -= systemInsets.bottom;
+ systemInsets.bottom = 0;
+ }
calculateWindowStableInsets(systemInsets, windowRect);
windowRect.offsetTo(0, 0);
- // Update the configuration for the current state
- Recents.getConfiguration().update(systemInsets);
-
TaskStackLayoutAlgorithm stackLayout = mDummyStackView.getStackAlgorithm();
- stackLayout.getTaskStackBounds(windowRect, systemInsets.top, systemInsets.right,
- mTaskStackBounds);
// Rebind the header bar and draw it for the transition
- Rect taskStackBounds = new Rect(mTaskStackBounds);
stackLayout.setSystemInsets(systemInsets);
if (stack != null) {
- stackLayout.initialize(windowRect, taskStackBounds,
+ stackLayout.getTaskStackBounds(windowRect, systemInsets.top, systemInsets.right,
+ mTaskStackBounds);
+ stackLayout.reset();
+ stackLayout.initialize(windowRect, mTaskStackBounds,
TaskStackLayoutAlgorithm.StackState.getStackStateForStack(stack));
- mDummyStackView.setTasks(stack, false /* notifyStackChanges */,
- false /* relayoutTaskStack */, false /* multiWindowChange */);
- }
- Rect taskViewBounds = stackLayout.getUntransformedTaskViewBounds();
- if (!taskViewBounds.equals(mLastTaskViewBounds)) {
- mLastTaskViewBounds.set(taskViewBounds);
+ mDummyStackView.setTasks(stack, false /* allowNotifyStackChanges */);
+ Rect taskViewBounds = stackLayout.getUntransformedTaskViewBounds();
int taskViewWidth = taskViewBounds.width();
synchronized (mHeaderBarLock) {
- mHeaderBar.measure(
- View.MeasureSpec.makeMeasureSpec(taskViewWidth, View.MeasureSpec.EXACTLY),
- View.MeasureSpec.makeMeasureSpec(mTaskBarHeight, View.MeasureSpec.EXACTLY));
+ if (mHeaderBar.getMeasuredWidth() != taskViewWidth ||
+ mHeaderBar.getMeasuredHeight() != mTaskBarHeight) {
+ mHeaderBar.measure(
+ View.MeasureSpec.makeMeasureSpec(taskViewWidth, View.MeasureSpec.EXACTLY),
+ View.MeasureSpec.makeMeasureSpec(mTaskBarHeight, View.MeasureSpec.EXACTLY));
+ }
mHeaderBar.layout(0, 0, taskViewWidth, mTaskBarHeight);
}
+
+ // Update the transition bitmap to match the new header bar height
+ if (mThumbTransitionBitmapCache == null ||
+ (mThumbTransitionBitmapCache.getWidth() != taskViewWidth) ||
+ (mThumbTransitionBitmapCache.getHeight() != mTaskBarHeight)) {
+ mThumbTransitionBitmapCache = Bitmap.createBitmap(taskViewWidth,
+ mTaskBarHeight, Bitmap.Config.ARGB_8888);
+ }
}
}
@@ -641,35 +662,6 @@
}
/**
- * Caches the header thumbnail used for a window animation asynchronously into
- * {@link #mThumbnailTransitionBitmapCache}.
- */
- private void preCacheThumbnailTransitionBitmapAsync(ActivityManager.RunningTaskInfo topTask,
- TaskStack stack, TaskStackView stackView) {
- preloadIcon(topTask);
-
- // Update the header bar if necessary
- updateHeaderBarLayout(stack);
-
- // Update the destination rect
- final Task toTask = new Task();
- final TaskViewTransform toTransform = getThumbnailTransitionTransform(stackView, toTask);
- ForegroundThread.getHandler().postAtFrontOfQueue(new Runnable() {
- @Override
- public void run() {
- final Bitmap transitionBitmap = drawThumbnailTransitionBitmap(toTask, toTransform);
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mThumbnailTransitionBitmapCache = transitionBitmap;
- mThumbnailTransitionBitmapCacheKey = toTask;
- }
- });
- }
- });
- }
-
- /**
* Creates the activity options for a unknown state->recents transition.
*/
protected ActivityOptions getUnknownTransitionActivityOptions() {
@@ -701,16 +693,17 @@
TaskStackViewScroller stackScroller = stackView.getScroller();
stackView.updateLayoutAlgorithm(true /* boundScroll */);
- stackView.updateToInitialState();
+ stackView.updateToInitialState(true /* scrollToInitialState */);
for (int i = tasks.size() - 1; i >= 0; i--) {
Task task = tasks.get(i);
if (task.isFreeformTask()) {
mTmpTransform = stackLayout.getStackTransformScreenCoordinates(task,
stackScroller.getStackScroll(), mTmpTransform, null);
+ Bitmap thumbnail = drawThumbnailTransitionBitmap(task, mTmpTransform,
+ mThumbTransitionBitmapCache);
Rect toTaskRect = new Rect();
mTmpTransform.rect.round(toTaskRect);
- Bitmap thumbnail = getThumbnailBitmap(topTask, task, mTmpTransform);
specs.add(new AppTransitionAnimationSpec(task.key.id, thumbnail, toTaskRect));
}
}
@@ -722,9 +715,10 @@
// Update the destination rect
Task toTask = new Task();
TaskViewTransform toTransform = getThumbnailTransitionTransform(stackView, toTask);
- RectF toTaskRect = toTransform.rect;
- Bitmap thumbnail = getThumbnailBitmap(topTask, toTask, toTransform);
+ Bitmap thumbnail = drawThumbnailTransitionBitmap(toTask, toTransform,
+ mThumbTransitionBitmapCache);
if (thumbnail != null) {
+ RectF toTaskRect = toTransform.rect;
return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
thumbnail, (int) toTaskRect.left, (int) toTaskRect.top,
(int) toTaskRect.width(), (int) toTaskRect.height(), mHandler, null);
@@ -734,22 +728,6 @@
}
}
- private Bitmap getThumbnailBitmap(ActivityManager.RunningTaskInfo topTask, Task toTask,
- TaskViewTransform toTransform) {
- Bitmap thumbnail;
- if (mThumbnailTransitionBitmapCacheKey != null
- && mThumbnailTransitionBitmapCacheKey.key != null
- && mThumbnailTransitionBitmapCacheKey.key.equals(toTask.key)) {
- thumbnail = mThumbnailTransitionBitmapCache;
- mThumbnailTransitionBitmapCacheKey = null;
- mThumbnailTransitionBitmapCache = null;
- } else {
- preloadIcon(topTask);
- thumbnail = drawThumbnailTransitionBitmap(toTask, toTransform);
- }
- return thumbnail;
- }
-
/**
* Returns the transition rect for the given task id.
*/
@@ -768,8 +746,8 @@
// Get the transform for the running task
stackView.updateLayoutAlgorithm(true /* boundScroll */);
- stackView.updateToInitialState();
- mTmpTransform = stackView.getStackAlgorithm().getStackTransformScreenCoordinates(launchTask,
+ stackView.updateToInitialState(true /* scrollToInitialState */);
+ stackView.getStackAlgorithm().getStackTransformScreenCoordinates(launchTask,
stackView.getScroller().getStackScroll(), mTmpTransform, null);
return mTmpTransform;
}
@@ -777,23 +755,19 @@
/**
* Draws the header of a task used for the window animation into a bitmap.
*/
- private Bitmap drawThumbnailTransitionBitmap(Task toTask, TaskViewTransform toTransform) {
+ private Bitmap drawThumbnailTransitionBitmap(Task toTask, TaskViewTransform toTransform,
+ Bitmap thumbnail) {
SystemServicesProxy ssp = Recents.getSystemServices();
if (toTransform != null && toTask.key != null) {
- Bitmap thumbnail;
synchronized (mHeaderBarLock) {
- int toHeaderWidth = (int) toTransform.rect.width();
- int toHeaderHeight = (int) (mHeaderBar.getMeasuredHeight() * toTransform.scale);
boolean disabledInSafeMode = !toTask.isSystemApp && ssp.isInSafeMode();
mHeaderBar.onTaskViewSizeChanged((int) toTransform.rect.width(),
(int) toTransform.rect.height());
- thumbnail = Bitmap.createBitmap(toHeaderWidth, toHeaderHeight,
- Bitmap.Config.ARGB_8888);
if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) {
thumbnail.eraseColor(0xFFff0000);
} else {
+ thumbnail.eraseColor(0);
Canvas c = new Canvas(thumbnail);
- c.scale(toTransform.scale, toTransform.scale);
// Workaround for b/27815919, reset the callback so that we do not trigger an
// invalidate on the header bar as a result of updating the icon
Drawable icon = mHeaderBar.getIconView().getDrawable();
@@ -818,6 +792,7 @@
protected void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
boolean isTopTaskHome, boolean animate) {
RecentsTaskLoader loader = Recents.getTaskLoader();
+ RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
// In the case where alt-tab is triggered, we never get a preloadRecents() call, so we
// should always preload the tasks now. If we are dragging in recents, reload them as
@@ -829,7 +804,22 @@
if (mLaunchedWhileDocking || mTriggeredFromAltTab || !sInstanceLoadPlan.hasTasks()) {
loader.preloadTasks(sInstanceLoadPlan, topTask.id, isTopTaskHome);
}
+
TaskStack stack = sInstanceLoadPlan.getTaskStack();
+ boolean hasRecentTasks = stack.getTaskCount() > 0;
+ boolean useThumbnailTransition = (topTask != null) && !isTopTaskHome && hasRecentTasks;
+
+ // Update the launch state that we need in updateHeaderBarLayout()
+ launchState.launchedFromHome = !useThumbnailTransition;
+ launchState.launchedFromApp = useThumbnailTransition || mLaunchedWhileDocking;
+ launchState.launchedViaDockGesture = mLaunchedWhileDocking;
+ launchState.launchedViaDragGesture = mDraggingInRecents;
+ launchState.launchedToTaskId = (topTask != null) ? topTask.id : -1;
+ launchState.launchedWithAltTab = mTriggeredFromAltTab;
+
+ // Preload the icon (this will be a null-op if we have preloaded the icon already in
+ // preloadRecents())
+ preloadIcon(topTask);
// Update the header bar if necessary
updateHeaderBarLayout(stack);
@@ -838,61 +828,34 @@
TaskStackLayoutAlgorithm.VisibilityReport stackVr =
mDummyStackView.computeStackVisibilityReport();
+ // Update the remaining launch state
+ launchState.launchedNumVisibleTasks = stackVr.numVisibleTasks;
+ launchState.launchedNumVisibleThumbnails = stackVr.numVisibleThumbnails;
+
if (!animate) {
- ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext, -1, -1);
- startRecentsActivity(topTask, opts, false /* fromHome */, false /* fromThumbnail*/,
- stackVr);
+ startRecentsActivity(ActivityOptions.makeCustomAnimation(mContext, -1, -1));
return;
}
- boolean hasRecentTasks = stack.getTaskCount() > 0;
- boolean useThumbnailTransition = (topTask != null) && !isTopTaskHome && hasRecentTasks;
-
+ ActivityOptions opts;
if (useThumbnailTransition) {
// Try starting with a thumbnail transition
- ActivityOptions opts = getThumbnailTransitionActivityOptions(topTask, mDummyStackView);
- if (opts != null) {
- startRecentsActivity(topTask, opts, false /* fromHome */, true /* fromThumbnail */,
- stackVr);
- } else {
- // Fall through below to the non-thumbnail transition
- useThumbnailTransition = false;
- }
- }
-
- if (!useThumbnailTransition) {
+ opts = getThumbnailTransitionActivityOptions(topTask, mDummyStackView);
+ } else {
// If there is no thumbnail transition, but is launching from home into recents, then
// use a quick home transition
- ActivityOptions opts = hasRecentTasks
- ? getHomeTransitionActivityOptions()
- : getUnknownTransitionActivityOptions();
- startRecentsActivity(topTask, opts, true /* fromHome */, false /* fromThumbnail */,
- stackVr);
+ opts = hasRecentTasks
+ ? getHomeTransitionActivityOptions()
+ : getUnknownTransitionActivityOptions();
}
+ startRecentsActivity(opts);
mLastToggleTime = SystemClock.elapsedRealtime();
}
/**
* Starts the recents activity.
*/
- private void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
- ActivityOptions opts, boolean fromHome, boolean fromThumbnail,
- TaskStackLayoutAlgorithm.VisibilityReport vr) {
- // Update the configuration based on the launch options
- RecentsConfiguration config = Recents.getConfiguration();
- RecentsActivityLaunchState launchState = config.getLaunchState();
- launchState.launchedFromHome = fromHome;
- launchState.launchedFromApp = fromThumbnail || mLaunchedWhileDocking;
- launchState.launchedFromAppDocked = mLaunchedWhileDocking;
- launchState.launchedToTaskId = (topTask != null) ? topTask.id : -1;
- launchState.launchedWithAltTab = mTriggeredFromAltTab;
- launchState.launchedReuseTaskStackViews = mCanReuseTaskStackViews;
- launchState.launchedNumVisibleTasks = vr.numVisibleTasks;
- launchState.launchedNumVisibleThumbnails = vr.numVisibleThumbnails;
- launchState.launchedHasConfigurationChanged = false;
- launchState.launchedViaDragGesture = mDraggingInRecents;
- launchState.launchedWhileDocking = mLaunchedWhileDocking;
-
+ private void startRecentsActivity(ActivityOptions opts) {
Intent intent = new Intent();
intent.setClassName(RECENTS_PACKAGE, RECENTS_ACTIVITY);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
@@ -904,7 +867,6 @@
} else {
mContext.startActivityAsUser(intent, UserHandle.CURRENT);
}
- mCanReuseTaskStackViews = true;
EventBus.getDefault().send(new RecentsActivityStartingEvent());
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
index 0d56ae9..38ad1c7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
@@ -30,6 +30,7 @@
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
+import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
@@ -652,19 +653,43 @@
/**
* @return a dump of the current state of the EventBus
*/
- public String dump() {
+ public void dump(String prefix, PrintWriter writer) {
+ writer.println(dumpInternal(prefix));
+ }
+
+ public String dumpInternal(String prefix) {
+ String innerPrefix = prefix + " ";
+ String innerInnerPrefix = innerPrefix + " ";
StringBuilder output = new StringBuilder();
+ output.append(prefix);
output.append("Registered class types:");
output.append("\n");
- for (Class<?> clz : mSubscriberTypeMap.keySet()) {
- output.append("\t");
+ ArrayList<Class<?>> subsciberTypes = new ArrayList<>(mSubscriberTypeMap.keySet());
+ Collections.sort(subsciberTypes, new Comparator<Class<?>>() {
+ @Override
+ public int compare(Class<?> o1, Class<?> o2) {
+ return o1.getSimpleName().compareTo(o2.getSimpleName());
+ }
+ });
+ for (int i = 0; i < subsciberTypes.size(); i++) {
+ Class<?> clz = subsciberTypes.get(i);
+ output.append(innerPrefix);
output.append(clz.getSimpleName());
output.append("\n");
}
+ output.append(prefix);
output.append("Event map:");
output.append("\n");
- for (Class<?> clz : mEventTypeMap.keySet()) {
- output.append("\t");
+ ArrayList<Class<?>> classes = new ArrayList<>(mEventTypeMap.keySet());
+ Collections.sort(classes, new Comparator<Class<?>>() {
+ @Override
+ public int compare(Class<?> o1, Class<?> o2) {
+ return o1.getSimpleName().compareTo(o2.getSimpleName());
+ }
+ });
+ for (int i = 0; i < classes.size(); i++) {
+ Class<?> clz = classes.get(i);
+ output.append(innerPrefix);
output.append(clz.getSimpleName());
output.append(" -> ");
output.append("\n");
@@ -673,7 +698,7 @@
Object subscriber = handler.subscriber.getReference();
if (subscriber != null) {
String id = Integer.toHexString(System.identityHashCode(subscriber));
- output.append("\t\t");
+ output.append(innerInnerPrefix);
output.append(subscriber.getClass().getSimpleName());
output.append(" [0x" + id + ", #" + handler.priority + "]");
output.append("\n");
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/ResetBackgroundScrimEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/AppTransitionFinishedEvent.java
similarity index 72%
copy from packages/SystemUI/src/com/android/systemui/recents/events/ui/ResetBackgroundScrimEvent.java
copy to packages/SystemUI/src/com/android/systemui/recents/events/activity/AppTransitionFinishedEvent.java
index 863f40b..4738eed 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/ResetBackgroundScrimEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/AppTransitionFinishedEvent.java
@@ -11,16 +11,15 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
*/
-package com.android.systemui.recents.events.ui;
+package com.android.systemui.recents.events.activity;
import com.android.systemui.recents.events.EventBus;
/**
- * This is sent to reset the background scrim back to the initial state.
+ * Sent when an app transition has finished playing.
*/
-public class ResetBackgroundScrimEvent extends EventBus.Event {
- // Simple event
+public class AppTransitionFinishedEvent extends EventBus.Event {
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java
index 0ad4681..e3bc2a7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java
@@ -22,5 +22,15 @@
* This is sent when the Recents activity configuration has changed.
*/
public class ConfigurationChangedEvent extends EventBus.AnimatedEvent {
- // Simple event
+
+ public final boolean fromMultiWindow;
+ public final boolean fromOrientationChange;
+ public final boolean hasStackTasks;
+
+ public ConfigurationChangedEvent(boolean fromMultiWindow, boolean fromOrientationChange,
+ boolean hasStackTasks) {
+ this.fromMultiWindow = fromMultiWindow;
+ this.fromOrientationChange = fromOrientationChange;
+ this.hasStackTasks = hasStackTasks;
+ }
}
diff --git a/core/java/android/net/IConnectivityMetricsLoggerSubscriber.aidl b/packages/SystemUI/src/com/android/systemui/recents/events/activity/DockedFirstAnimationFrameEvent.java
similarity index 67%
copy from core/java/android/net/IConnectivityMetricsLoggerSubscriber.aidl
copy to packages/SystemUI/src/com/android/systemui/recents/events/activity/DockedFirstAnimationFrameEvent.java
index a2c62cd..32d9a70 100644
--- a/core/java/android/net/IConnectivityMetricsLoggerSubscriber.aidl
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/DockedFirstAnimationFrameEvent.java
@@ -11,15 +11,15 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
*/
-package android.net;
+package com.android.systemui.recents.events.activity;
-import android.net.ConnectivityMetricsEvent;
+import com.android.systemui.recents.events.EventBus.Event;
-/** {@hide} */
-oneway interface IConnectivityMetricsLoggerSubscriber {
-
- void onEvents(in ConnectivityMetricsEvent[] events);
+/**
+ * Sent when the window animation has started when docking a task
+ */
+public class DockedFirstAnimationFrameEvent extends Event {
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ForcedResizableEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ForcedResizableEvent.java
new file mode 100644
index 0000000..cdcabf0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ForcedResizableEvent.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.recents.events.activity;
+
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * Sent when recents received the information that an activity got forced resizable, and we need
+ * to inform the user about that.
+ */
+public class ForcedResizableEvent extends EventBus.Event {
+
+ public final String packageName;
+ public final int taskId;
+
+ public ForcedResizableEvent(String packageName, int taskId) {
+ this.packageName = packageName;
+ this.taskId = taskId;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java
index 19245d9..cf2a68e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java
@@ -24,8 +24,10 @@
public class MultiWindowStateChangedEvent extends EventBus.Event {
public final boolean inMultiWindow;
+ public final boolean hasStackTasks;
- public MultiWindowStateChangedEvent(boolean inMultiWindow) {
+ public MultiWindowStateChangedEvent(boolean inMultiWindow, boolean hasStackTasks) {
this.inMultiWindow = inMultiWindow;
+ this.hasStackTasks = hasStackTasks;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/ResetBackgroundScrimEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissAllTaskViewsEvent.java
similarity index 78%
rename from packages/SystemUI/src/com/android/systemui/recents/events/ui/ResetBackgroundScrimEvent.java
rename to packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissAllTaskViewsEvent.java
index 863f40b..f8b59c7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/ResetBackgroundScrimEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissAllTaskViewsEvent.java
@@ -17,10 +17,11 @@
package com.android.systemui.recents.events.ui;
import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.views.TaskView;
/**
- * This is sent to reset the background scrim back to the initial state.
+ * This event is sent to request that all the {@link TaskView}s are dismissed.
*/
-public class ResetBackgroundScrimEvent extends EventBus.Event {
+public class DismissAllTaskViewsEvent extends EventBus.AnimatedEvent {
// Simple event
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissTaskViewEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissTaskViewEvent.java
index 1165f4e..1f8c644 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissTaskViewEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissTaskViewEvent.java
@@ -17,7 +17,6 @@
package com.android.systemui.recents.events.ui;
import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.views.TaskView;
/**
@@ -26,10 +25,8 @@
public class DismissTaskViewEvent extends EventBus.AnimatedEvent {
public final TaskView taskView;
- public final Task task;
- public DismissTaskViewEvent(TaskView taskView, Task task) {
+ public DismissTaskViewEvent(TaskView taskView) {
this.taskView = taskView;
- this.task = task;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/UpdateBackgroundScrimEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/UpdateBackgroundScrimEvent.java
deleted file mode 100644
index fdd4c67..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/UpdateBackgroundScrimEvent.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents.events.ui;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * This is sent to request an update to the background scrim.
- */
-public class UpdateBackgroundScrimEvent extends EventBus.Event {
-
- public final float alpha;
-
- public UpdateBackgroundScrimEvent(float alpha) {
- this.alpha = alpha;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java
index b85ddac..216be61 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java
@@ -23,7 +23,7 @@
/**
* This event is sent when a user drags in/out of a drop target.
*/
-public class DragDropTargetChangedEvent extends EventBus.Event {
+public class DragDropTargetChangedEvent extends EventBus.AnimatedEvent {
// The task that is currently being dragged
public final Task task;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 8f149d7..1a4b40f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -18,6 +18,7 @@
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.HOME_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
@@ -52,6 +53,7 @@
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
+import android.os.IRemoteCallback;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
@@ -64,9 +66,8 @@
import android.util.Log;
import android.util.MutableBoolean;
import android.view.Display;
+import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.IDockedStackListener;
-import android.view.Surface;
-import android.view.View;
import android.view.WindowManager;
import android.view.WindowManager.KeyboardShortcutsReceiver;
import android.view.WindowManagerGlobal;
@@ -77,7 +78,9 @@
import com.android.systemui.R;
import com.android.systemui.recents.RecentsDebugFlags;
import com.android.systemui.recents.RecentsImpl;
+import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.tv.RecentsTvImpl;
+import com.android.systemui.recents.model.ThumbnailData;
import java.io.IOException;
import java.util.ArrayList;
@@ -140,6 +143,7 @@
public void onActivityPinned() { }
public void onPinnedActivityRestartAttempt() { }
public void onPinnedStackAnimationEnded() { }
+ public void onActivityForcedResizable(String packageName, int taskId) { }
}
/**
@@ -171,10 +175,17 @@
mHandler.removeMessages(H.ON_PINNED_STACK_ANIMATION_ENDED);
mHandler.sendEmptyMessage(H.ON_PINNED_STACK_ANIMATION_ENDED);
}
+
+ @Override
+ public void onActivityForcedResizable(String packageName, int taskId)
+ throws RemoteException {
+ mHandler.obtainMessage(H.ON_ACTIVITY_FORCED_RESIZABLE, taskId, 0, packageName)
+ .sendToTarget();
+ }
};
/**
- * List of {@link TaskStackListener} registered from {@link registerTaskStackListener}.
+ * List of {@link TaskStackListener} registered from {@link #registerTaskStackListener}.
*/
private List<TaskStackListener> mTaskStackListeners = new ArrayList<>();
@@ -275,7 +286,7 @@
int numTasksToQuery = Math.max(minNumTasksToQuery, numLatestTasks);
List<ActivityManager.RecentTaskInfo> tasks = mAm.getRecentTasksForUser(numTasksToQuery,
ActivityManager.RECENT_IGNORE_HOME_STACK_TASKS |
- ActivityManager.RECENT_INGORE_DOCKED_STACK_TASKS |
+ ActivityManager.RECENT_INGORE_DOCKED_STACK_TOP_TASK |
ActivityManager.RECENT_INGORE_PINNED_STACK_TASKS |
ActivityManager.RECENT_IGNORE_UNAVAILABLE |
ActivityManager.RECENT_INCLUDE_PROFILES |
@@ -392,13 +403,12 @@
}
/** Docks a task to the side of the screen and starts it. */
- public void startTaskInDockedMode(Context context, View view, int taskId, int createMode) {
+ public void startTaskInDockedMode(int taskId, int createMode) {
if (mIam == null) return;
try {
// TODO: Determine what animation we want for the incoming task
- final ActivityOptions options = ActivityOptions.makeThumbnailAspectScaleUpAnimation(
- view, null, 0, 0, view.getWidth(), view.getHeight(), null, null);
+ final ActivityOptions options = ActivityOptions.makeBasic();
options.setDockCreateMode(createMode);
options.setLaunchStackId(DOCKED_STACK_ID);
mIam.startActivityFromRecents(taskId, options.toBundle());
@@ -513,44 +523,47 @@
}
/** Returns the top task thumbnail for the given task id */
- public Bitmap getTaskThumbnail(int taskId) {
+ public ThumbnailData getTaskThumbnail(int taskId) {
if (mAm == null) return null;
+ ThumbnailData thumbnailData = new ThumbnailData();
// If we are mocking, then just return a dummy thumbnail
if (RecentsDebugFlags.Static.EnableMockTasks) {
- Bitmap thumbnail = Bitmap.createBitmap(mDummyThumbnailWidth, mDummyThumbnailHeight,
- Bitmap.Config.ARGB_8888);
- thumbnail.eraseColor(0xff333333);
- return thumbnail;
+ thumbnailData.thumbnail = Bitmap.createBitmap(mDummyThumbnailWidth,
+ mDummyThumbnailHeight, Bitmap.Config.ARGB_8888);
+ thumbnailData.thumbnail.eraseColor(0xff333333);
+ return thumbnailData;
}
- Bitmap thumbnail = getThumbnail(taskId);
- if (thumbnail != null) {
- thumbnail.setHasAlpha(false);
+ getThumbnail(taskId, thumbnailData);
+ if (thumbnailData.thumbnail != null) {
+ thumbnailData.thumbnail.setHasAlpha(false);
// We use a dumb heuristic for now, if the thumbnail is purely transparent in the top
// left pixel, then assume the whole thumbnail is transparent. Generally, proper
// screenshots are always composed onto a bitmap that has no alpha.
- if (Color.alpha(thumbnail.getPixel(0, 0)) == 0) {
- mBgProtectionCanvas.setBitmap(thumbnail);
- mBgProtectionCanvas.drawRect(0, 0, thumbnail.getWidth(), thumbnail.getHeight(),
- mBgProtectionPaint);
+ if (Color.alpha(thumbnailData.thumbnail.getPixel(0, 0)) == 0) {
+ mBgProtectionCanvas.setBitmap(thumbnailData.thumbnail);
+ mBgProtectionCanvas.drawRect(0, 0, thumbnailData.thumbnail.getWidth(),
+ thumbnailData.thumbnail.getHeight(), mBgProtectionPaint);
mBgProtectionCanvas.setBitmap(null);
Log.e(TAG, "Invalid screenshot detected from getTaskThumbnail()");
}
}
- return thumbnail;
+ return thumbnailData;
}
/**
* Returns a task thumbnail from the activity manager
*/
- public Bitmap getThumbnail(int taskId) {
+ public void getThumbnail(int taskId, ThumbnailData thumbnailDataOut) {
if (mAm == null) {
- return null;
+ return;
}
ActivityManager.TaskThumbnail taskThumbnail = mAm.getTaskThumbnail(taskId);
- if (taskThumbnail == null) return null;
+ if (taskThumbnail == null) {
+ return;
+ }
Bitmap thumbnail = taskThumbnail.mainThumbnail;
ParcelFileDescriptor descriptor = taskThumbnail.thumbnailFileDescriptor;
@@ -564,7 +577,8 @@
} catch (IOException e) {
}
}
- return thumbnail;
+ thumbnailDataOut.thumbnail = thumbnail;
+ thumbnailDataOut.thumbnailInfo = taskThumbnail.thumbnailInfo;
}
/**
@@ -950,11 +964,20 @@
}
/** Starts an activity from recents. */
- public boolean startActivityFromRecents(Context context, int taskId, String taskName,
+ public boolean startActivityFromRecents(Context context, Task.TaskKey taskKey, String taskName,
ActivityOptions options) {
if (mIam != null) {
try {
- mIam.startActivityFromRecents(taskId, options == null ? null : options.toBundle());
+ if (taskKey.stackId == DOCKED_STACK_ID) {
+ // We show non-visible docked tasks in Recents, but we always want to launch
+ // them in the fullscreen stack.
+ if (options == null) {
+ options = ActivityOptions.makeBasic();
+ }
+ options.setLaunchStackId(FULLSCREEN_WORKSPACE_STACK_ID);
+ }
+ mIam.startActivityFromRecents(
+ taskKey.id, options == null ? null : options.toBundle());
return true;
} catch (Exception e) {
Log.e(TAG, context.getString(R.string.recents_launch_error_message, taskName), e);
@@ -1040,11 +1063,34 @@
}
}
+ public void overridePendingAppTransitionMultiThumbFuture(
+ IAppTransitionAnimationSpecsFuture future, IRemoteCallback animStartedListener,
+ boolean scaleUp) {
+ try {
+ WindowManagerGlobal.getWindowManagerService()
+ .overridePendingAppTransitionMultiThumbFuture(future, animStartedListener,
+ scaleUp);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to override transition: " + e);
+ }
+ }
+
+ /**
+ * Returns whether the device has a transposed nav bar (on the right of the screen) in the
+ * current display orientation.
+ */
+ public boolean hasTransposedNavBar() {
+ Rect insets = new Rect();
+ getStableInsets(insets);
+ return insets.right > 0;
+ }
+
private final class H extends Handler {
private static final int ON_TASK_STACK_CHANGED = 1;
private static final int ON_ACTIVITY_PINNED = 2;
private static final int ON_PINNED_ACTIVITY_RESTART_ATTEMPT = 3;
private static final int ON_PINNED_STACK_ANIMATION_ENDED = 4;
+ private static final int ON_ACTIVITY_FORCED_RESIZABLE = 5;
@Override
public void handleMessage(Message msg) {
@@ -1073,6 +1119,13 @@
}
break;
}
+ case ON_ACTIVITY_FORCED_RESIZABLE: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onActivityForcedResizable(
+ (String) msg.obj, msg.arg1);
+ }
+ break;
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
index e28612a..69d98af 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
@@ -243,4 +243,14 @@
public static float dpToPx(Resources res, float dp) {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, res.getDisplayMetrics());
}
+
+ /**
+ * Returns a lightweight dump of a rect.
+ */
+ public static String dumpRect(Rect r) {
+ if (r == null) {
+ return "N:0,0-0,0";
+ }
+ return r.left + "," + r.top + "-" + r.right + "," + r.bottom;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index c863471..7aeff1f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -132,6 +132,8 @@
SparseIntArray affiliatedTaskCounts = new SparseIntArray();
String dismissDescFormat = mContext.getString(
R.string.accessibility_recents_item_will_be_dismissed);
+ String appInfoDescFormat = mContext.getString(
+ R.string.accessibility_recents_item_open_app_info);
long lastStackActiveTime = Prefs.getLong(mContext,
Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME, 0);
if (RecentsDebugFlags.Static.EnableMockTasks) {
@@ -160,8 +162,15 @@
long parentTaskLastActiveTime = parentTask != null
? parentTask.lastActiveTime
: prevLastActiveTime;
- t.lastActiveTime = parentTaskLastActiveTime +
- affiliatedTaskCounts.get(t.affiliatedTaskId, 0) + 1;
+ if (RecentsDebugFlags.Static.EnableAffiliatedTaskGroups) {
+ t.lastActiveTime = parentTaskLastActiveTime +
+ affiliatedTaskCounts.get(t.affiliatedTaskId, 0) + 1;
+ } else {
+ if (t.lastActiveTime == 0) {
+ t.lastActiveTime = parentTaskLastActiveTime -
+ affiliatedTaskCounts.get(t.affiliatedTaskId, 0) - 1;
+ }
+ }
}
// Compose the task key
@@ -181,8 +190,9 @@
// Load the title, icon, and color
ActivityInfo info = loader.getAndUpdateActivityInfo(taskKey);
String title = loader.getAndUpdateActivityTitle(taskKey, t.taskDescription);
- String contentDescription = loader.getAndUpdateContentDescription(taskKey, res);
- String dismissDescription = String.format(dismissDescFormat, contentDescription);
+ String titleDescription = loader.getAndUpdateContentDescription(taskKey, res);
+ String dismissDescription = String.format(dismissDescFormat, titleDescription);
+ String appInfoDescription = String.format(appInfoDescFormat, titleDescription);
Drawable icon = isStackTask
? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, res, false)
: null;
@@ -194,14 +204,13 @@
// Add the task to the stack
Task task = new Task(taskKey, t.affiliatedTaskId, t.affiliatedTaskColor, icon,
- thumbnail, title, contentDescription, dismissDescription, activityColor,
- backgroundColor, isLaunchTarget, isStackTask, isSystemApp, t.isDockable,
- t.bounds, t.taskDescription);
+ thumbnail, title, titleDescription, dismissDescription, appInfoDescription,
+ activityColor, backgroundColor, isLaunchTarget, isStackTask, isSystemApp,
+ t.isDockable, t.bounds, t.taskDescription);
allTasks.add(task);
affiliatedTaskCounts.put(taskKey.id, affiliatedTaskCounts.get(taskKey.id, 0) + 1);
affiliatedTasks.put(taskKey.id, taskKey);
-
prevLastActiveTime = t.lastActiveTime;
}
if (newLastStackActiveTime != -1) {
@@ -240,8 +249,8 @@
if (opts.loadIcons && (isRunningTask || isVisibleTask)) {
if (task.icon == null) {
- task.icon = loader.getAndUpdateActivityIcon(taskKey, task.taskDescription,
- res, true);
+ task.icon = loader.getAndUpdateActivityIcon(taskKey, task.taskDescription, res,
+ true);
}
}
if (opts.loadThumbnails && (isRunningTask || isVisibleThumbnail)) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index ee4d95e..82c81ae 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -95,7 +95,7 @@
TaskResourceLoadQueue mLoadQueue;
TaskKeyLruCache<Drawable> mIconCache;
- TaskKeyLruCache<Bitmap> mThumbnailCache;
+ TaskKeyLruCache<ThumbnailData> mThumbnailCache;
Bitmap mDefaultThumbnail;
BitmapDrawable mDefaultIcon;
@@ -104,7 +104,7 @@
/** Constructor, creates a new loading thread that loads task resources in the background */
public BackgroundTaskLoader(TaskResourceLoadQueue loadQueue,
- TaskKeyLruCache<Drawable> iconCache, TaskKeyLruCache<Bitmap> thumbnailCache,
+ TaskKeyLruCache<Drawable> iconCache, TaskKeyLruCache<ThumbnailData> thumbnailCache,
Bitmap defaultThumbnail, BitmapDrawable defaultIcon) {
mLoadQueue = loadQueue;
mIconCache = iconCache;
@@ -165,7 +165,7 @@
final Task t = mLoadQueue.nextTask();
if (t != null) {
Drawable cachedIcon = mIconCache.get(t.key);
- Bitmap cachedThumbnail = mThumbnailCache.get(t.key);
+ ThumbnailData cachedThumbnailData = mThumbnailCache.get(t.key);
// Load the icon if it is stale or we haven't cached one yet
if (cachedIcon == null) {
@@ -190,30 +190,32 @@
mIconCache.put(t.key, cachedIcon);
}
// Load the thumbnail if it is stale or we haven't cached one yet
- if (cachedThumbnail == null) {
+ if (cachedThumbnailData == null) {
if (config.svelteLevel < RecentsConfiguration.SVELTE_DISABLE_LOADING) {
if (DEBUG) Log.d(TAG, "Loading thumbnail: " + t.key);
- cachedThumbnail = ssp.getTaskThumbnail(t.key.id);
+ cachedThumbnailData = ssp.getTaskThumbnail(t.key.id);
}
- if (cachedThumbnail == null) {
- cachedThumbnail = mDefaultThumbnail;
+
+ if (cachedThumbnailData.thumbnail == null) {
+ cachedThumbnailData.thumbnail = mDefaultThumbnail;
}
+
// When svelte, we trim the memory to just the visible thumbnails when
// leaving, so don't thrash the cache as the user scrolls (just load
// them from scratch each time)
if (config.svelteLevel < RecentsConfiguration.SVELTE_LIMIT_CACHE) {
- mThumbnailCache.put(t.key, cachedThumbnail);
+ mThumbnailCache.put(t.key, cachedThumbnailData);
}
}
if (!mCancelled) {
// Notify that the task data has changed
final Drawable newIcon = cachedIcon;
- final Bitmap newThumbnail = cachedThumbnail == mDefaultThumbnail
- ? null : cachedThumbnail;
+ final ThumbnailData newThumbnailData = cachedThumbnailData;
mMainThreadHandler.post(new Runnable() {
@Override
public void run() {
- t.notifyTaskDataLoaded(newThumbnail, newIcon);
+ t.notifyTaskDataLoaded(newThumbnailData.thumbnail, newIcon,
+ newThumbnailData.thumbnailInfo);
}
});
}
@@ -252,7 +254,7 @@
// package in the cache has been updated, so that we may remove it.
private final LruCache<ComponentName, ActivityInfo> mActivityInfoCache;
private final TaskKeyLruCache<Drawable> mIconCache;
- private final TaskKeyLruCache<Bitmap> mThumbnailCache;
+ private final TaskKeyLruCache<ThumbnailData> mThumbnailCache;
private final TaskKeyLruCache<String> mActivityLabelCache;
private final TaskKeyLruCache<String> mContentDescriptionCache;
private final TaskResourceLoadQueue mLoadQueue;
@@ -272,7 +274,9 @@
new TaskKeyLruCache.EvictionCallback() {
@Override
public void onEntryEvicted(Task.TaskKey key) {
- mActivityInfoCache.remove(key.getComponent());
+ if (key != null) {
+ mActivityInfoCache.remove(key.getComponent());
+ }
}
};
@@ -356,9 +360,16 @@
*/
public void loadTaskData(Task t, boolean fetchAndInvalidateThumbnails) {
Drawable icon = mIconCache.getAndInvalidateIfModified(t.key);
- Bitmap thumbnail = mDefaultThumbnail;
+ Bitmap thumbnail = null;
+ ActivityManager.TaskThumbnailInfo thumbnailInfo = null;
if (fetchAndInvalidateThumbnails) {
- thumbnail = mThumbnailCache.getAndInvalidateIfModified(t.key);
+ ThumbnailData thumbnailData = mThumbnailCache.getAndInvalidateIfModified(t.key);
+ if (thumbnailData != null) {
+ thumbnail = thumbnailData.thumbnail;
+ thumbnailInfo = thumbnailData.thumbnailInfo;
+ }
+ } else {
+ thumbnail = mDefaultThumbnail;
}
// Grab the thumbnail/icon from the cache, if either don't exist, then trigger a reload and
@@ -368,7 +379,8 @@
if (requiresLoad) {
mLoadQueue.addTask(t);
}
- t.notifyTaskDataLoaded(thumbnail == mDefaultThumbnail ? null : thumbnail, icon);
+ t.notifyTaskDataLoaded(thumbnail == mDefaultThumbnail ? null : thumbnail, icon,
+ thumbnailInfo);
}
/** Releases the task resource data back into the pool. */
@@ -535,19 +547,19 @@
SystemServicesProxy ssp = Recents.getSystemServices();
// Return the cached thumbnail if it exists
- Bitmap thumbnail = mThumbnailCache.getAndInvalidateIfModified(taskKey);
- if (thumbnail != null) {
- return thumbnail;
+ ThumbnailData thumbnailData = mThumbnailCache.getAndInvalidateIfModified(taskKey);
+ if (thumbnailData != null) {
+ return thumbnailData.thumbnail;
}
if (loadIfNotCached) {
RecentsConfiguration config = Recents.getConfiguration();
if (config.svelteLevel < RecentsConfiguration.SVELTE_DISABLE_LOADING) {
// Load the thumbnail from the system
- thumbnail = ssp.getTaskThumbnail(taskKey.id);
- if (thumbnail != null) {
- mThumbnailCache.put(taskKey, thumbnail);
- return thumbnail;
+ thumbnailData = ssp.getTaskThumbnail(taskKey.id);
+ if (thumbnailData.thumbnail != null) {
+ mThumbnailCache.put(taskKey, thumbnailData);
+ return thumbnailData.thumbnail;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 3e858a8..24eeaf2 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -29,6 +29,7 @@
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Objects;
@@ -37,10 +38,13 @@
* A task represents the top most task in the system's task stack.
*/
public class Task {
+
+ public static final String TAG = "Task";
+
/* Task callbacks */
public interface TaskCallbacks {
/* Notifies when a task has been bound */
- public void onTaskDataLoaded(Task task);
+ public void onTaskDataLoaded(Task task, ActivityManager.TaskThumbnailInfo thumbnailInfo);
/* Notifies when a task has been unbound */
public void onTaskDataUnloaded();
/* Notifies when a task's stack id has changed. */
@@ -100,7 +104,8 @@
@Override
public String toString() {
- return "t" + id + ", s" + stackId + ", u" + userId;
+ return "id=" + id + " stackId=" + stackId + " user=" + userId + " lastActiveTime=" +
+ lastActiveTime;
}
private void updateHashCode() {
@@ -134,10 +139,12 @@
@ViewDebug.ExportedProperty(category="recents")
public String title;
@ViewDebug.ExportedProperty(category="recents")
- public String contentDescription;
+ public String titleDescription;
@ViewDebug.ExportedProperty(category="recents")
public String dismissDescription;
@ViewDebug.ExportedProperty(category="recents")
+ public String appInfoDescription;
+ @ViewDebug.ExportedProperty(category="recents")
public int colorPrimary;
@ViewDebug.ExportedProperty(category="recents")
public int colorBackground;
@@ -174,8 +181,8 @@
}
public Task(TaskKey key, int affiliationTaskId, int affiliationColor, Drawable icon,
- Bitmap thumbnail, String title, String contentDescription,
- String dismissDescription, int colorPrimary, int colorBackground,
+ Bitmap thumbnail, String title, String titleDescription, String dismissDescription,
+ String appInfoDescription, int colorPrimary, int colorBackground,
boolean isLaunchTarget, boolean isStackTask, boolean isSystemApp,
boolean isDockable, Rect bounds, ActivityManager.TaskDescription taskDescription) {
boolean isInAffiliationGroup = (affiliationTaskId != key.id);
@@ -186,8 +193,9 @@
this.icon = icon;
this.thumbnail = thumbnail;
this.title = title;
- this.contentDescription = contentDescription;
+ this.titleDescription = titleDescription;
this.dismissDescription = dismissDescription;
+ this.appInfoDescription = appInfoDescription;
this.colorPrimary = hasAffiliationGroupColor ? affiliationColor : colorPrimary;
this.colorBackground = colorBackground;
this.useLightOnPrimaryColor = Utilities.computeContrastBetweenColors(this.colorPrimary,
@@ -211,12 +219,14 @@
this.icon = o.icon;
this.thumbnail = o.thumbnail;
this.title = o.title;
- this.contentDescription = o.contentDescription;
+ this.titleDescription = o.titleDescription;
this.dismissDescription = o.dismissDescription;
+ this.appInfoDescription = o.appInfoDescription;
this.colorPrimary = o.colorPrimary;
this.colorBackground = o.colorBackground;
this.useLightOnPrimaryColor = o.useLightOnPrimaryColor;
this.bounds = o.bounds;
+ this.taskDescription = o.taskDescription;
this.isLaunchTarget = o.isLaunchTarget;
this.isStackTask = o.isStackTask;
this.isSystemApp = o.isSystemApp;
@@ -264,12 +274,13 @@
}
/** Notifies the callback listeners that this task has been loaded */
- public void notifyTaskDataLoaded(Bitmap thumbnail, Drawable applicationIcon) {
+ public void notifyTaskDataLoaded(Bitmap thumbnail, Drawable applicationIcon,
+ ActivityManager.TaskThumbnailInfo thumbnailInfo) {
this.icon = applicationIcon;
this.thumbnail = thumbnail;
int callbackCount = mCallbacks.size();
for (int i = 0; i < callbackCount; i++) {
- mCallbacks.get(i).onTaskDataLoaded(this);
+ mCallbacks.get(i).onTaskDataLoaded(this, thumbnailInfo);
}
}
@@ -300,4 +311,13 @@
public String toString() {
return "[" + key.toString() + "] " + title;
}
+
+ public void dump(String prefix, PrintWriter writer) {
+ writer.print(prefix); writer.print(key);
+ if (affiliationTaskId != key.id) {
+ writer.print(" "); writer.print("affTaskId=" + affiliationTaskId);
+ }
+ writer.print(" "); writer.print(title);
+ writer.println();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 930e2d0..fbb5987 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -55,6 +55,7 @@
import com.android.systemui.recents.views.DropTarget;
import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -208,6 +209,8 @@
*/
public class TaskStack {
+ private static final String TAG = "TaskStack";
+
/** Task stack callbacks */
public interface TaskStackCallbacks {
/**
@@ -220,6 +223,16 @@
*/
void onStackTaskRemoved(TaskStack stack, Task removedTask, boolean wasFrontMostTask,
Task newFrontMostTask, AnimationProps animation, boolean fromDockGesture);
+
+ /**
+ * Notifies when all tasks have been removed from the stack.
+ */
+ void onStackTasksRemoved(TaskStack stack);
+
+ /**
+ * Notifies when tasks in the stack have been updated.
+ */
+ void onStackTasksUpdated(TaskStack stack);
}
/**
@@ -429,12 +442,14 @@
mStackTaskList.setFilter(new TaskFilter() {
@Override
public boolean acceptTask(SparseArray<Task> taskIdMap, Task t, int index) {
- if (t.isAffiliatedTask()) {
- // If this task is affiliated with another parent in the stack, then the
- // historical state of this task depends on the state of the parent task
- Task parentTask = taskIdMap.get(t.affiliationTaskId);
- if (parentTask != null) {
- t = parentTask;
+ if (RecentsDebugFlags.Static.EnableAffiliatedTaskGroups) {
+ if (t.isAffiliatedTask()) {
+ // If this task is affiliated with another parent in the stack, then the
+ // historical state of this task depends on the state of the parent task
+ Task parentTask = taskIdMap.get(t.affiliationTaskId);
+ if (parentTask != null) {
+ t = parentTask;
+ }
}
}
return t.isStackTask;
@@ -503,6 +518,22 @@
}
/**
+ * Removes all tasks from the stack.
+ */
+ public void removeAllTasks() {
+ ArrayList<Task> tasks = mStackTaskList.getTasks();
+ for (int i = tasks.size() - 1; i >= 0; i--) {
+ Task t = tasks.get(i);
+ removeTaskImpl(mStackTaskList, t);
+ mRawTaskList.remove(t);
+ }
+ if (mCb != null) {
+ // Notify that all tasks have been removed
+ mCb.onStackTasksRemoved(this);
+ }
+ }
+
+ /**
* Sets a few tasks in one go, without calling any callbacks.
*
* @param tasks the new set of tasks to replace the current set.
@@ -558,14 +589,19 @@
mStackTaskList.set(allTasks);
mRawTaskList = allTasks;
+ // Update the affiliated groupings
+ createAffiliatedGroupings(context);
+
// Only callback for the newly added tasks after this stack has been updated
int addedTaskCount = addedTasks.size();
for (int i = 0; i < addedTaskCount; i++) {
mCb.onStackTaskAdded(this, addedTasks.get(i));
}
- // Update the affiliated groupings
- createAffiliatedGroupings(context);
+ // Notify that the task stack has been updated
+ if (notifyStackChanges) {
+ mCb.onStackTasksUpdated(this);
+ }
}
/**
@@ -692,7 +728,9 @@
/** Finds the task with the specified task id. */
public Task findTaskWithId(int taskId) {
ArrayList<Task> tasks = computeAllTasksList();
- for (Task task : tasks) {
+ int taskCount = tasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ Task task = tasks.get(i);
if (task.key.id == taskId) {
return task;
}
@@ -847,7 +885,10 @@
ArraySet<ComponentName> existingComponents = new ArraySet<>();
ArraySet<ComponentName> removedComponents = new ArraySet<>();
ArrayList<Task.TaskKey> taskKeys = getTaskKeys();
- for (Task.TaskKey t : taskKeys) {
+ int taskKeyCount = taskKeys.size();
+ for (int i = 0; i < taskKeyCount; i++) {
+ Task.TaskKey t = taskKeys.get(i);
+
// Skip if this doesn't apply to the current user
if (t.userId != userId) continue;
@@ -870,8 +911,10 @@
@Override
public String toString() {
String str = "Stack Tasks (" + mStackTaskList.size() + "):\n";
- for (Task t : mStackTaskList.getTasks()) {
- str += " " + t.toString() + "\n";
+ ArrayList<Task> tasks = mStackTaskList.getTasks();
+ int taskCount = tasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ str += " " + tasks.get(i).toString() + "\n";
}
return str;
}
@@ -888,4 +931,17 @@
}
return map;
}
+
+ public void dump(String prefix, PrintWriter writer) {
+ String innerPrefix = prefix + " ";
+
+ writer.print(prefix); writer.print(TAG);
+ writer.print(" numStackTasks="); writer.print(mStackTaskList.size());
+ writer.println();
+ ArrayList<Task> tasks = mStackTaskList.getTasks();
+ int taskCount = tasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ tasks.get(i).dump(innerPrefix, writer);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/ResetBackgroundScrimEvent.java b/packages/SystemUI/src/com/android/systemui/recents/model/ThumbnailData.java
similarity index 70%
copy from packages/SystemUI/src/com/android/systemui/recents/events/ui/ResetBackgroundScrimEvent.java
copy to packages/SystemUI/src/com/android/systemui/recents/model/ThumbnailData.java
index 863f40b..d0cdae5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/ResetBackgroundScrimEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/ThumbnailData.java
@@ -14,13 +14,15 @@
* limitations under the License.
*/
-package com.android.systemui.recents.events.ui;
+package com.android.systemui.recents.model;
-import com.android.systemui.recents.events.EventBus;
+import android.app.ActivityManager;
+import android.graphics.Bitmap;
/**
- * This is sent to reset the background scrim back to the initial state.
+ * Data for a single thumbnail.
*/
-public class ResetBackgroundScrimEvent extends EventBus.Event {
- // Simple event
+public class ThumbnailData {
+ public Bitmap thumbnail;
+ public ActivityManager.TaskThumbnailInfo thumbnailInfo;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
index d8e82d9..f9f851a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
@@ -57,6 +57,7 @@
import com.android.systemui.recents.tv.views.TaskStackHorizontalViewAdapter;
import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.tv.pip.PipManager;
+import com.android.systemui.tv.pip.PipRecentsOverlayManager;
import java.util.ArrayList;
import java.util.Collections;
@@ -78,12 +79,11 @@
private RecentsTvView mRecentsView;
private View mPipView;
- private View mPipShadeView;
private TaskStackHorizontalViewAdapter mTaskStackViewAdapter;
private FinishRecentsRunnable mFinishLaunchHomeRunnable;
- private PipManager mPipManager;
- private PipManager.Listener mPipListener = new PipManager.Listener() {
+ private final PipManager mPipManager = PipManager.getInstance();
+ private final PipManager.Listener mPipListener = new PipManager.Listener() {
@Override
public void onPipEntered() {
updatePipUI();
@@ -95,18 +95,45 @@
}
@Override
- public void onShowPipMenu() { }
+ public void onShowPipMenu() {
+ updatePipUI();
+ }
@Override
- public void onMoveToFullscreen() { }
+ public void onMoveToFullscreen() {
+ // Recents should be dismissed when PIP moves to fullscreen. If not, Recents will
+ // be unnecessarily shown in the scenario: PIP->Fullscreen->PIP.
+ dismissRecentsToLaunchTargetTaskOrHome();
+ }
@Override
public void onPipResizeAboutToStart() { }
-
- @Override
- public void onMediaControllerChanged() { }
};
- private boolean mHasPip;
+ private PipRecentsOverlayManager mPipRecentsOverlayManager;
+ private final PipRecentsOverlayManager.Callback mPipRecentsOverlayManagerCallback =
+ new PipRecentsOverlayManager.Callback() {
+ @Override
+ public void onClosed() {
+ dismissRecentsToLaunchTargetTaskOrHome();
+ }
+
+ @Override
+ public void onBackPressed() {
+ RecentsTvActivity.this.onBackPressed();
+ }
+
+ @Override
+ public void onRecentsFocused() {
+ mRecentsView.requestFocus();
+ }
+ };
+ private final View.OnFocusChangeListener mPipViewFocusChangeListener =
+ new View.OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ handlePipViewFocusChange(hasFocus);
+ }
+ };
/**
* A common Runnable to finish Recents by launching Home with an animation depending on the
@@ -238,7 +265,7 @@
finish();
return;
}
- mPipManager = PipManager.getInstance();
+ mPipRecentsOverlayManager = PipManager.getInstance().getPipRecentsOverlayManager();
// Register this activity with the event bus
EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
@@ -253,8 +280,19 @@
mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
+
mPipView = findViewById(R.id.pip);
- mPipShadeView = findViewById(R.id.pip_shade);
+ // Place mPipView at the PIP bounds for fine tuned focus handling.
+ Rect pipBounds = mPipManager.getRecentsFocusedPipBounds();
+ LayoutParams lp = (LayoutParams) mPipView.getLayoutParams();
+ lp.width = pipBounds.width();
+ lp.height = pipBounds.height();
+ lp.leftMargin = pipBounds.left;
+ lp.topMargin = pipBounds.top;
+ mPipView.setLayoutParams(lp);
+
+ mPipRecentsOverlayManager.setCallback(mPipRecentsOverlayManagerCallback);
+
getWindow().getAttributes().privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
@@ -265,8 +303,6 @@
Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent);
- mHasPip = false;
- updatePipUI();
mPipManager.addListener(mPipListener);
}
@@ -290,7 +326,7 @@
RecentsActivityLaunchState launchState = config.getLaunchState();
boolean wasLaunchedByAm = !launchState.launchedFromHome &&
!launchState.launchedFromApp;
- if (launchState.launchedHasConfigurationChanged || wasLaunchedByAm) {
+ if (wasLaunchedByAm) {
EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
}
@@ -298,9 +334,7 @@
SystemServicesProxy ssp = Recents.getSystemServices();
EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, true));
- mPipManager.onRecentsStarted();
- // Give focus to the recents row whenever its visible to an user.
- mRecentsView.requestFocus();
+ updatePipUI();
}
@Override
@@ -310,10 +344,21 @@
}
@Override
+ public void onResume() {
+ super.onResume();
+ mPipRecentsOverlayManager.onRecentsResumed();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mPipRecentsOverlayManager.onRecentsPaused();
+ }
+
+ @Override
protected void onStop() {
super.onStop();
- mPipManager.onRecentsStopped();
mIgnoreAltTabRelease = false;
// Notify that recents is now hidden
EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, false));
@@ -456,38 +501,33 @@
}
private void updatePipUI() {
- if (mHasPip == mPipManager.isPipShown()) {
- return;
- }
- mHasPip = mPipManager.isPipShown();
- if (mHasPip) {
- // Place mPipView at the PIP bounds for fine tuned focus handling.
- Rect pipBounds = mPipManager.getPipBounds();
- LayoutParams lp = (LayoutParams) mPipView.getLayoutParams();
- lp.width = pipBounds.width();
- lp.height = pipBounds.height();
- lp.leftMargin = pipBounds.left;
- lp.topMargin = pipBounds.top;
- mPipView.setLayoutParams(lp);
-
+ if (mPipManager.isPipShown()) {
mPipView.setVisibility(View.VISIBLE);
- mPipView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- mPipManager.resizePinnedStack(PipManager.STATE_PIP_MENU);
- }
- });
- mPipView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- mPipManager.onPipViewFocusChangedInRecents(hasFocus);
- mPipShadeView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
- }
- });
- mPipShadeView.setVisibility(View.GONE);
+ mPipView.setOnFocusChangeListener(mPipViewFocusChangeListener);
+ if (mPipView.hasFocus()) {
+ // This can happen only if the activity is resumed. Ask for reset.
+ handlePipViewFocusChange(true);
+ } else {
+ mPipView.requestFocus();
+ }
} else {
mPipView.setVisibility(View.GONE);
- mPipShadeView.setVisibility(View.GONE);
+ mPipRecentsOverlayManager.removePipRecentsOverlayView();
+ }
+ }
+
+ /**
+ * Handles the PIP view's focus change.
+ * This starts the relevant recents row animation
+ * and give focus to the recents overlay if needed.
+ */
+ private void handlePipViewFocusChange(boolean hasFocus) {
+ mRecentsView.startRecentsRowFocusAnimation(!hasFocus);
+ if (hasFocus) {
+ // When PIP view has focus, recents overlay view will takes the focus
+ // as if it's the part of the Recents UI.
+ mPipRecentsOverlayManager.requestFocus(
+ mTaskStackViewAdapter.getItemCount() > 0);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java
index aa27325..fb62aff 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java
@@ -24,10 +24,13 @@
import android.graphics.Rect;
import android.os.SystemClock;
import android.os.UserHandle;
-import com.android.systemui.recents.*;
+
+import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.RecentsActivityLaunchState;
+import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.RecentsImpl;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
-import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.TaskStack;
import com.android.systemui.recents.tv.views.TaskCardView;
@@ -101,8 +104,6 @@
launchState.launchedFromApp = fromThumbnail;
launchState.launchedToTaskId = (topTask != null) ? topTask.id : -1;
launchState.launchedWithAltTab = mTriggeredFromAltTab;
- launchState.launchedReuseTaskStackViews = mCanReuseTaskStackViews;
- launchState.launchedHasConfigurationChanged = false;
Intent intent = new Intent();
intent.setClassName(RECENTS_PACKAGE, RECENTS_TV_ACTIVITY);
@@ -115,7 +116,6 @@
} else {
mContext.startActivityAsUser(intent, UserHandle.CURRENT);
}
- mCanReuseTaskStackViews = true;
EventBus.getDefault().send(new RecentsActivityStartingEvent());
}
@@ -124,7 +124,7 @@
*/
private ActivityOptions getThumbnailTransitionActivityOptionsForTV(
ActivityManager.RunningTaskInfo topTask) {
- Bitmap thumbnail = mThumbnailTransitionBitmapCache;
+ Bitmap thumbnail = mThumbTransitionBitmapCache;
Rect rect = TaskCardView.getStartingCardThumbnailRect(mContext);
if (thumbnail != null) {
return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/RecentsRowFocusAnimationHolder.java b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/RecentsRowFocusAnimationHolder.java
new file mode 100644
index 0000000..28abc34
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/RecentsRowFocusAnimationHolder.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.recents.tv.animations;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.content.res.Resources;
+import android.view.View;
+
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.recents.tv.views.TaskCardView;
+
+/**
+ * Recents row's focus animation with PIP controls.
+ */
+public class RecentsRowFocusAnimationHolder {
+ private static final float DIM_ALPHA = 0.5f;
+
+ private View mView;
+ private View mTitleView;
+
+ private AnimatorSet mFocusGainAnimatorSet;
+ private AnimatorSet mFocusLoseAnimatorSet;
+
+ public RecentsRowFocusAnimationHolder(View view, View titleView) {
+ mView = view;
+ mTitleView = titleView;
+
+ Resources res = view.getResources();
+ int duration = res.getInteger(R.integer.recents_tv_pip_focus_anim_duration);
+
+ mFocusGainAnimatorSet = new AnimatorSet();
+ mFocusGainAnimatorSet.playTogether(
+ ObjectAnimator.ofFloat(mView, "alpha", 1f),
+ ObjectAnimator.ofFloat(mTitleView, "alpha", 1f));
+ mFocusGainAnimatorSet.setDuration(duration);
+ mFocusGainAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+
+ mFocusLoseAnimatorSet = new AnimatorSet();
+ mFocusLoseAnimatorSet.playTogether(
+ ObjectAnimator.ofFloat(mView, "alpha", DIM_ALPHA),
+ ObjectAnimator.ofFloat(mTitleView, "alpha", 0f));
+ mFocusLoseAnimatorSet.setDuration(duration);
+ mFocusLoseAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ }
+
+ /**
+ * Returns the Recents row's focus change animation.
+ */
+ public Animator getFocusChangeAnimator(boolean hasFocus) {
+ return hasFocus ? mFocusGainAnimatorSet : mFocusLoseAnimatorSet;
+ }
+
+ /**
+ * Resets the views to the initial state immediately.
+ */
+ public void reset() {
+ mView.setAlpha(1f);
+ mTitleView.setAlpha(1f);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvTransitionHelper.java
index bd3143f..a69f8a2 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvTransitionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvTransitionHelper.java
@@ -89,7 +89,7 @@
private void startTaskActivity(TaskStack stack, Task task, @Nullable TaskCardView taskView,
ActivityOptions opts,final ActivityOptions.OnAnimationStartedListener animStartedListener) {
SystemServicesProxy ssp = Recents.getSystemServices();
- if (ssp.startActivityFromRecents(mContext, task.key.id, task.title, opts)) {
+ if (ssp.startActivityFromRecents(mContext, task.key, task.title, opts)) {
// Keep track of the index of the task launch
int taskIndexFromFront = 0;
int taskIndex = stack.indexOfStackTask(task);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java
index 3d5176f..9da8eed 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java
@@ -37,6 +37,7 @@
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.tv.animations.RecentsRowFocusAnimationHolder;
import java.util.ArrayList;
import java.util.List;
@@ -52,6 +53,7 @@
private TaskStack mStack;
private TaskStackHorizontalGridView mTaskStackHorizontalView;
private View mEmptyView;
+ private RecentsRowFocusAnimationHolder mEmptyViewFocusAnimationHolder;
private boolean mAwaitingFirstLayout = true;
private Rect mSystemInsets = new Rect();
private RecentsTvTransitionHelper mTransitionHelper;
@@ -77,6 +79,8 @@
LayoutInflater inflater = LayoutInflater.from(context);
mEmptyView = inflater.inflate(R.layout.recents_empty, this, false);
addView(mEmptyView);
+ mEmptyViewFocusAnimationHolder = new RecentsRowFocusAnimationHolder(mEmptyView, null);
+
mHandler = new Handler();
mTransitionHelper = new RecentsTvTransitionHelper(mContext, mHandler);
}
@@ -94,7 +98,6 @@
mTaskStackHorizontalView.setStack(stack);
}
-
if (stack.getStackTaskCount() > 0) {
hideEmptyView();
} else {
@@ -109,7 +112,7 @@
Task task = mTaskStackHorizontalView.getFocusedTask();
if (task != null) {
SystemServicesProxy ssp = Recents.getSystemServices();
- ssp.startActivityFromRecents(getContext(), task.key.id, task.title, null);
+ ssp.startActivityFromRecents(getContext(), task.key, task.title, null);
return true;
}
}
@@ -123,7 +126,7 @@
Task task = stack.getLaunchTarget();
if (task != null) {
SystemServicesProxy ssp = Recents.getSystemServices();
- ssp.startActivityFromRecents(getContext(), task.key.id, task.title, null);
+ ssp.startActivityFromRecents(getContext(), task.key, task.title, null);
return true;
}
}
@@ -134,33 +137,40 @@
public boolean launchTask(Task task, Rect taskBounds, int destinationStack) {
if (mTaskStackHorizontalView != null) {
// Iterate the stack views and try and find the given task.
- List<TaskCardView> taskViews = mTaskStackHorizontalView.getTaskViews();
- int taskViewCount = taskViews.size();
- for (int j = 0; j < taskViewCount; j++) {
- TaskCardView tv = taskViews.get(j);
- if (tv.getTask() == task) {
- SystemServicesProxy ssp = Recents.getSystemServices();
- ssp.startActivityFromRecents(getContext(), task.key.id, task.title, null);
- return true;
- }
+ if (mTaskStackHorizontalView.getChildViewForTask(task) != null) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ ssp.startActivityFromRecents(getContext(), task.key, task.title, null);
+ return true;
}
}
return false;
}
/**
+ * Starts the focus change animation.
+ */
+ public void startRecentsRowFocusAnimation(boolean hasFocus) {
+ if (mEmptyView.getVisibility() == View.VISIBLE) {
+ mEmptyViewFocusAnimationHolder.getFocusChangeAnimator(hasFocus).start();
+ } else {
+ mTaskStackHorizontalView.startRecentsRowFocusAnimation(hasFocus);
+ }
+ }
+
+ /**
* Hides the task stack and shows the empty view.
*/
public void showEmptyView() {
mEmptyView.setVisibility(View.VISIBLE);
- mEmptyView.bringToFront();
+ mTaskStackHorizontalView.setVisibility(View.GONE);
}
/**
* Shows the task stack and hides the empty view.
*/
public void hideEmptyView() {
- mEmptyView.setVisibility(View.INVISIBLE);
+ mEmptyView.setVisibility(View.GONE);
+ mTaskStackHorizontalView.setVisibility(View.VISIBLE);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
index 3343aec..d605748 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
@@ -24,6 +24,7 @@
import android.util.TypedValue;
import android.view.Display;
import android.view.KeyEvent;
+import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -31,6 +32,7 @@
import com.android.systemui.R;
import com.android.systemui.recents.tv.animations.DismissAnimationsHolder;
+import com.android.systemui.recents.tv.animations.RecentsRowFocusAnimationHolder;
import com.android.systemui.recents.tv.animations.ViewFocusAnimator;
import com.android.systemui.recents.model.Task;
@@ -44,6 +46,7 @@
private ViewFocusAnimator mViewFocusAnimator;
private DismissAnimationsHolder mDismissAnimationsHolder;
+ private RecentsRowFocusAnimationHolder mRecentsRowFocusAnimationHolder;
public TaskCardView(Context context) {
this(context, null);
@@ -65,6 +68,8 @@
mTitleTextView = (TextView) findViewById(R.id.card_title_text);
mBadgeView = (ImageView) findViewById(R.id.card_extra_badge);
mDismissAnimationsHolder = new DismissAnimationsHolder(this);
+ View title = findViewById(R.id.card_info_field);
+ mRecentsRowFocusAnimationHolder = new RecentsRowFocusAnimationHolder(this, title);
}
public void init(Task task) {
@@ -193,6 +198,10 @@
mDismissAnimationsHolder.startDismissAnimation(listener);
}
+ public RecentsRowFocusAnimationHolder getRecentsRowFocusAnimationHolder() {
+ return mRecentsRowFocusAnimationHolder;
+ }
+
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
index 22ade9f..603721a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
@@ -15,7 +15,11 @@
*/
package com.android.systemui.recents.tv.views;
+import android.animation.Animator;
+import android.animation.AnimatorSet;
import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
import android.support.v17.leanback.widget.HorizontalGridView;
import android.util.AttributeSet;
import android.view.View;
@@ -36,10 +40,19 @@
* Horizontal Grid View Implementation to show the Task Stack for TV.
*/
public class TaskStackHorizontalGridView extends HorizontalGridView implements TaskStackCallbacks {
-
+ private static final int ANIMATION_DELAY_MS = 50;
+ private static final int MSG_START_RECENT_ROW_FOCUS_ANIMATION = 100;
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_START_RECENT_ROW_FOCUS_ANIMATION) {
+ startRecentsRowFocusAnimation(msg.arg1 == 1);
+ }
+ }
+ };
private TaskStack mStack;
- private ArrayList<TaskCardView> mTaskViews = new ArrayList<>();
private Task mFocusedTask;
+ private AnimatorSet mRecentsRowFocusAnimation;
public TaskStackHorizontalGridView(Context context) {
this(context, null);
@@ -62,10 +75,18 @@
super.onDetachedFromWindow();
EventBus.getDefault().unregister(this);
}
+
/**
* Resets this view for reuse.
*/
public void reset() {
+ for (int i = 0; i < getChildCount(); i++) {
+ ((TaskCardView) getChildAt(i)).getRecentsRowFocusAnimationHolder().reset();
+ }
+ if (mRecentsRowFocusAnimation != null && mRecentsRowFocusAnimation.isStarted()) {
+ mRecentsRowFocusAnimation.cancel();
+ }
+ mHandler.removeCallbacksAndMessages(null);
requestLayout();
}
@@ -119,10 +140,8 @@
* @return Child view for given task
*/
public TaskCardView getChildViewForTask(Task task) {
- List<TaskCardView> taskViews = getTaskViews();
- int taskViewCount = taskViews.size();
- for (int i = 0; i < taskViewCount; i++) {
- TaskCardView tv = taskViews.get(i);
+ for (int i = 0; i < getChildCount(); i++) {
+ TaskCardView tv = (TaskCardView) getChildAt(i);
if (tv.getTask() == task) {
return tv;
}
@@ -130,12 +149,36 @@
return null;
}
- public List<TaskCardView> getTaskViews() {
- return mTaskViews;
+ /**
+ * Starts the focus change animation.
+ */
+ public void startRecentsRowFocusAnimation(final boolean hasFocus) {
+ if (getChildCount() == 0) {
+ // Animation request may happen before view is attached.
+ // Post again with small dealy so animation can be run again later.
+ if (getAdapter().getItemCount() > 0) {
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(
+ MSG_START_RECENT_ROW_FOCUS_ANIMATION, hasFocus ? 1 : 0),
+ ANIMATION_DELAY_MS);
+ }
+ return;
+ }
+ if (mRecentsRowFocusAnimation != null && mRecentsRowFocusAnimation.isStarted()) {
+ mRecentsRowFocusAnimation.cancel();
+ }
+ Animator animator = ((TaskCardView) getChildAt(0)).getRecentsRowFocusAnimationHolder()
+ .getFocusChangeAnimator(hasFocus);
+ mRecentsRowFocusAnimation = new AnimatorSet();
+ AnimatorSet.Builder builder = mRecentsRowFocusAnimation.play(animator);
+ for (int i = 1; i < getChildCount(); i++) {
+ builder.with(((TaskCardView) getChildAt(i)).getRecentsRowFocusAnimationHolder()
+ .getFocusChangeAnimator(hasFocus));
+ }
+ mRecentsRowFocusAnimation.start();
}
@Override
- public void onStackTaskAdded(TaskStack stack, Task newTask){
+ public void onStackTaskAdded(TaskStack stack, Task newTask) {
getAdapter().notifyItemInserted(stack.getStackTasks().indexOf(newTask));
}
@@ -156,4 +199,14 @@
}
}
}
+
+ @Override
+ public void onStackTasksRemoved(TaskStack stack) {
+ // Do nothing
+ }
+
+ @Override
+ public void onStackTasksUpdated(TaskStack stack) {
+ // Do nothing
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
index 29da476..9eec2ce 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
@@ -24,9 +24,11 @@
import android.annotation.Nullable;
import android.app.ActivityManager.StackId;
import android.app.ActivityOptions;
+import android.app.ActivityOptions.OnAnimationStartedListener;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
@@ -35,7 +37,6 @@
import android.util.Log;
import android.view.AppTransitionAnimationSpec;
import android.view.IAppTransitionAnimationSpecsFuture;
-import android.view.WindowManagerGlobal;
import com.android.internal.annotations.GuardedBy;
import com.android.systemui.recents.Recents;
@@ -52,6 +53,7 @@
import com.android.systemui.recents.model.TaskStack;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -82,9 +84,9 @@
}
};
- public RecentsTransitionHelper(Context context, Handler handler) {
+ public RecentsTransitionHelper(Context context) {
mContext = context;
- mHandler = handler;
+ mHandler = new Handler();
}
/**
@@ -92,7 +94,7 @@
*/
public void launchTaskFromRecents(final TaskStack stack, @Nullable final Task task,
final TaskStackView stackView, final TaskView taskView,
- final boolean screenPinningRequested, final Rect bounds, int destinationStack) {
+ final boolean screenPinningRequested, final Rect bounds, final int destinationStack) {
final ActivityOptions opts = ActivityOptions.makeBasic();
if (bounds != null) {
opts.setLaunchBounds(bounds.isEmpty() ? null : bounds);
@@ -101,7 +103,12 @@
final ActivityOptions.OnAnimationStartedListener animStartedListener;
final IAppTransitionAnimationSpecsFuture transitionFuture;
if (taskView != null) {
- transitionFuture = getAppTransitionFuture(task, stackView, destinationStack);
+ transitionFuture = getAppTransitionFuture(new AnimationSpecComposer() {
+ @Override
+ public List<AppTransitionAnimationSpec> composeSpecs() {
+ return composeAnimationSpecs(task, stackView, destinationStack);
+ }
+ });
animStartedListener = new ActivityOptions.OnAnimationStartedListener() {
@Override
public void onAnimationStarted() {
@@ -154,6 +161,23 @@
}
}
+ public IRemoteCallback wrapStartedListener(final OnAnimationStartedListener listener) {
+ if (listener == null) {
+ return null;
+ }
+ return new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle data) throws RemoteException {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ listener.onAnimationStarted();
+ }
+ });
+ }
+ };
+ }
+
/**
* Starts the activity for the launch task.
*
@@ -164,7 +188,7 @@
ActivityOptions opts, IAppTransitionAnimationSpecsFuture transitionFuture,
final ActivityOptions.OnAnimationStartedListener animStartedListener) {
SystemServicesProxy ssp = Recents.getSystemServices();
- if (ssp.startActivityFromRecents(mContext, task.key.id, task.title, opts)) {
+ if (ssp.startActivityFromRecents(mContext, task.key, task.title, opts)) {
// Keep track of the index of the task launch
int taskIndexFromFront = 0;
int taskIndex = stack.indexOfStackTask(task);
@@ -181,40 +205,21 @@
}
if (transitionFuture != null) {
- IRemoteCallback.Stub callback = null;
- if (animStartedListener != null) {
- callback = new IRemoteCallback.Stub() {
- @Override
- public void sendResult(Bundle data) throws RemoteException {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- if (animStartedListener != null) {
- animStartedListener.onAnimationStarted();
- }
- }
- });
- }
- };
- }
- try {
- synchronized (this) {
- mAppTransitionAnimationSpecs = SPECS_WAITING;
- }
- WindowManagerGlobal.getWindowManagerService()
- .overridePendingAppTransitionMultiThumbFuture(transitionFuture,
- callback, true /* scaleUp */);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to override transition: " + e);
- }
+ ssp.overridePendingAppTransitionMultiThumbFuture(transitionFuture,
+ wrapStartedListener(animStartedListener), true /* scaleUp */);
}
}
/**
* Creates a future which will later be queried for animation specs for this current transition.
+ *
+ * @param composer The implementation that composes the specs on the UI thread.
*/
- private IAppTransitionAnimationSpecsFuture getAppTransitionFuture(final Task task,
- final TaskStackView stackView, final int destinationStack) {
+ public IAppTransitionAnimationSpecsFuture getAppTransitionFuture(
+ final AnimationSpecComposer composer) {
+ synchronized (this) {
+ mAppTransitionAnimationSpecs = SPECS_WAITING;
+ }
return new IAppTransitionAnimationSpecsFuture.Stub() {
@Override
public AppTransitionAnimationSpec[] get() throws RemoteException {
@@ -222,8 +227,7 @@
@Override
public void run() {
synchronized (RecentsTransitionHelper.this) {
- mAppTransitionAnimationSpecs = composeAnimationSpecs(task, stackView,
- destinationStack);
+ mAppTransitionAnimationSpecs = composer.composeSpecs();
RecentsTransitionHelper.this.notifyAll();
}
}
@@ -248,6 +252,18 @@
}
/**
+ * Composes the transition spec when docking a task, which includes a full task bitmap.
+ */
+ public List<AppTransitionAnimationSpec> composeDockAnimationSpec(TaskView taskView,
+ Rect bounds) {
+ mTmpTransform.fillIn(taskView);
+ Task task = taskView.getTask();
+ Bitmap thumbnail = RecentsTransitionHelper.composeTaskBitmap(taskView, mTmpTransform);
+ return Collections.singletonList(new AppTransitionAnimationSpec(task.key.id, thumbnail,
+ bounds));
+ }
+
+ /**
* Composes the animation specs for all the tasks in the target stack.
*/
private List<AppTransitionAnimationSpec> composeAnimationSpecs(final Task task,
@@ -319,17 +335,19 @@
return new AppTransitionAnimationSpec(task.key.id, null, taskRect);
}
- /**
- * Composes a single animation spec for the given {@link TaskView}
- */
- private static AppTransitionAnimationSpec composeAnimationSpec(TaskStackView stackView,
- TaskView taskView, TaskViewTransform transform, boolean addHeaderBitmap) {
- Bitmap b = null;
- if (addHeaderBitmap) {
- float scale = transform.scale;
- int fromHeaderWidth = (int) (transform.rect.width());
- int fromHeaderHeight = (int) (taskView.mHeaderView.getMeasuredHeight() * scale);
- b = Bitmap.createBitmap(fromHeaderWidth, fromHeaderHeight,
+ public static Bitmap composeTaskBitmap(TaskView taskView, TaskViewTransform transform) {
+ float scale = transform.scale;
+ int fromWidth = (int) (transform.rect.width() * scale);
+ int fromHeight = (int) (transform.rect.height() * scale);
+ if (fromWidth == 0 || fromHeight == 0) {
+ Log.e(TAG, "Could not compose thumbnail for task: " + taskView.getTask() +
+ " at transform: " + transform);
+
+ Bitmap b = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+ b.eraseColor(Color.TRANSPARENT);
+ return b;
+ } else {
+ Bitmap b = Bitmap.createBitmap(fromWidth, fromHeight,
Bitmap.Config.ARGB_8888);
if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) {
@@ -337,10 +355,40 @@
} else {
Canvas c = new Canvas(b);
c.scale(scale, scale);
- taskView.mHeaderView.draw(c);
+ taskView.draw(c);
c.setBitmap(null);
}
- b = b.createAshmemBitmap();
+ return b.createAshmemBitmap();
+ }
+ }
+
+ private static Bitmap composeHeaderBitmap(TaskView taskView,
+ TaskViewTransform transform) {
+ float scale = transform.scale;
+ int fromHeaderWidth = (int) (transform.rect.width());
+ int fromHeaderHeight = (int) (taskView.mHeaderView.getMeasuredHeight() * scale);
+ Bitmap b = Bitmap.createBitmap(fromHeaderWidth, fromHeaderHeight,
+ Bitmap.Config.ARGB_8888);
+
+ if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) {
+ b.eraseColor(0xFFff0000);
+ } else {
+ Canvas c = new Canvas(b);
+ c.scale(scale, scale);
+ taskView.mHeaderView.draw(c);
+ c.setBitmap(null);
+ }
+ return b.createAshmemBitmap();
+ }
+
+ /**
+ * Composes a single animation spec for the given {@link TaskView}
+ */
+ private static AppTransitionAnimationSpec composeAnimationSpec(TaskStackView stackView,
+ TaskView taskView, TaskViewTransform transform, boolean addHeaderBitmap) {
+ Bitmap b = null;
+ if (addHeaderBitmap) {
+ b = composeHeaderBitmap(taskView, transform);
}
Rect taskRect = new Rect();
@@ -351,4 +399,8 @@
}
return new AppTransitionAnimationSpec(taskView.getTask().key.id, b, taskRect);
}
+
+ public interface AnimationSpecComposer {
+ List<AppTransitionAnimationSpec> composeSpecs();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 70c4c81..21a43d5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -19,8 +19,8 @@
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
+import android.app.ActivityOptions.OnAnimationStartedListener;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -28,9 +28,10 @@
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
-import android.os.Handler;
import android.util.ArraySet;
import android.util.AttributeSet;
+import android.view.AppTransitionAnimationSpec;
+import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -52,14 +53,15 @@
import com.android.systemui.recents.RecentsDebugFlags;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
+import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent;
import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
import com.android.systemui.recents.events.activity.HideStackActionButtonEvent;
import com.android.systemui.recents.events.activity.LaunchTaskEvent;
import com.android.systemui.recents.events.activity.ShowStackActionButtonEvent;
+import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
+import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
import com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent;
import com.android.systemui.recents.events.ui.DraggingInRecentsEvent;
-import com.android.systemui.recents.events.ui.ResetBackgroundScrimEvent;
-import com.android.systemui.recents.events.ui.UpdateBackgroundScrimEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
@@ -68,9 +70,12 @@
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.views.RecentsTransitionHelper.AnimationSpecComposer;
import com.android.systemui.stackdivider.WindowManagerProxy;
import com.android.systemui.statusbar.FlingAnimationUtils;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -80,11 +85,14 @@
*/
public class RecentsView extends FrameLayout {
+ private static final String TAG = "RecentsView";
+
private static final int DOCK_AREA_OVERLAY_TRANSITION_DURATION = 135;
private static final int DEFAULT_UPDATE_SCRIM_DURATION = 200;
private static final float DEFAULT_SCRIM_ALPHA = 0.33f;
- private final Handler mHandler;
+ private static final int SHOW_STACK_ACTION_BUTTON_DURATION = 150;
+ private static final int HIDE_STACK_ACTION_BUTTON_DURATION = 100;
private TaskStack mStack;
private TaskStackView mTaskStackView;
@@ -98,7 +106,8 @@
private Rect mSystemInsets = new Rect();
private int mDividerSize;
- private ColorDrawable mBackgroundScrim = new ColorDrawable(Color.BLACK);
+ private Drawable mBackgroundScrim = new ColorDrawable(
+ Color.argb((int) (DEFAULT_SCRIM_ALPHA * 255), 0, 0, 0)).mutate();
private Animator mBackgroundScrimAnimator;
private RecentsTransitionHelper mTransitionHelper;
@@ -123,22 +132,22 @@
setWillNotDraw(false);
SystemServicesProxy ssp = Recents.getSystemServices();
- mHandler = new Handler();
- mTransitionHelper = new RecentsTransitionHelper(getContext(), mHandler);
+ mTransitionHelper = new RecentsTransitionHelper(getContext());
mDividerSize = ssp.getDockedDividerSize(context);
mTouchHandler = new RecentsViewTouchHandler(this);
mFlingAnimationUtils = new FlingAnimationUtils(context, 0.3f);
- final float cornerRadius = context.getResources().getDimensionPixelSize(
- R.dimen.recents_task_view_rounded_corners_radius);
LayoutInflater inflater = LayoutInflater.from(context);
if (RecentsDebugFlags.Static.EnableStackActionButton) {
- mStackActionButton = (TextView) inflater.inflate(R.layout.recents_stack_action_button, this,
- false);
+ float cornerRadius = context.getResources().getDimensionPixelSize(
+ R.dimen.recents_task_view_rounded_corners_radius);
+ mStackActionButton = (TextView) inflater.inflate(R.layout.recents_stack_action_button,
+ this, false);
+ mStackActionButton.forceHasOverlappingRendering(false);
mStackActionButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- // TODO: To be implemented
+ EventBus.getDefault().send(new DismissAllTaskViewsEvent());
}
});
addView(mStackActionButton);
@@ -152,23 +161,19 @@
}
mEmptyView = (TextView) inflater.inflate(R.layout.recents_empty, this, false);
addView(mEmptyView);
-
- setBackground(mBackgroundScrim);
}
- /** Set/get the bsp root node */
- public void onResume(boolean isResumingFromVisible, boolean multiWindowChange,
- TaskStack stack) {
+ /**
+ * Called from RecentsActivity when it is relaunched.
+ */
+ public void onReload(boolean isResumingFromVisible, boolean isTaskStackEmpty) {
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
- if (!multiWindowChange &&
- (mTaskStackView == null || !launchState.launchedReuseTaskStackViews)) {
+ if (mTaskStackView == null) {
isResumingFromVisible = false;
- removeView(mTaskStackView);
mTaskStackView = new TaskStackView(getContext());
mTaskStackView.setSystemInsets(mSystemInsets);
- mStack = mTaskStackView.getStack();
addView(mTaskStackView);
}
@@ -177,24 +182,30 @@
mLastTaskLaunchedWasFreeform = false;
// Update the stack
- mTaskStackView.onResume(isResumingFromVisible);
- mTaskStackView.setTasks(stack, isResumingFromVisible /* notifyStackChanges */,
- true /* relayoutTaskStack */, multiWindowChange);
+ mTaskStackView.onReload(isResumingFromVisible);
if (isResumingFromVisible) {
// If we are already visible, then restore the background scrim
- animateBackgroundScrim(DEFAULT_SCRIM_ALPHA, DEFAULT_UPDATE_SCRIM_DURATION);
+ animateBackgroundScrim(1f, DEFAULT_UPDATE_SCRIM_DURATION);
} else {
// If we are already occluded by the app, then set the final background scrim alpha now.
// Otherwise, defer until the enter animation completes to animate the scrim alpha with
// the tasks for the home animation.
- if (launchState.launchedWhileDocking || launchState.launchedFromApp
- || mStack.getTaskCount() == 0) {
- mBackgroundScrim.setAlpha((int) (DEFAULT_SCRIM_ALPHA * 255));
+ if (launchState.launchedViaDockGesture || launchState.launchedFromApp
+ || isTaskStackEmpty) {
+ mBackgroundScrim.setAlpha(255);
} else {
mBackgroundScrim.setAlpha(0);
}
}
+ }
+
+ /**
+ * Called from RecentsActivity when the task stack is updated.
+ */
+ public void updateStack(TaskStack stack) {
+ mStack = stack;
+ mTaskStackView.setTasks(stack, true /* allowNotifyStackChanges */);
// Update the top level view's visibilities
if (stack.getTaskCount() > 0) {
@@ -205,6 +216,20 @@
}
/**
+ * Returns the current TaskStack.
+ */
+ public TaskStack getStack() {
+ return mStack;
+ }
+
+ /*
+ * Returns the window background scrim.
+ */
+ public Drawable getBackgroundScrim() {
+ return mBackgroundScrim;
+ }
+
+ /**
* Returns whether the last task launched was in the freeform stack or not.
*/
public boolean isLastTaskLaunchedFreeform() {
@@ -322,10 +347,10 @@
if (RecentsDebugFlags.Static.EnableStackActionButton) {
// Measure the stack action button within the constraints of the space above the stack
- Rect actionButtonRect = mTaskStackView.mLayoutAlgorithm.mStackActionButtonRect;
+ Rect buttonBounds = mTaskStackView.mLayoutAlgorithm.mStackActionButtonRect;
measureChild(mStackActionButton,
- MeasureSpec.makeMeasureSpec(actionButtonRect.width(), MeasureSpec.AT_MOST),
- MeasureSpec.makeMeasureSpec(actionButtonRect.height(), MeasureSpec.AT_MOST));
+ MeasureSpec.makeMeasureSpec(buttonBounds.width(), MeasureSpec.AT_MOST),
+ MeasureSpec.makeMeasureSpec(buttonBounds.height(), MeasureSpec.AT_MOST));
}
setMeasuredDimension(width, height);
@@ -346,24 +371,19 @@
int topBottomInsets = mSystemInsets.top + mSystemInsets.bottom;
int childWidth = mEmptyView.getMeasuredWidth();
int childHeight = mEmptyView.getMeasuredHeight();
- int childLeft = left + Math.max(0, (right - left - leftRightInsets - childWidth)) / 2;
- int childTop = top + Math.max(0, (bottom - top - topBottomInsets - childHeight)) / 2;
+ int childLeft = left + mSystemInsets.left +
+ Math.max(0, (right - left - leftRightInsets - childWidth)) / 2;
+ int childTop = top + mSystemInsets.top +
+ Math.max(0, (bottom - top - topBottomInsets - childHeight)) / 2;
mEmptyView.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
}
if (RecentsDebugFlags.Static.EnableStackActionButton) {
// Layout the stack action button such that its drawable is start-aligned with the
// stack, vertically centered in the available space above the stack
- Rect actionButtonRect = mTaskStackView.mLayoutAlgorithm.mStackActionButtonRect;
- int buttonLeft = isLayoutRtl()
- ? actionButtonRect.right + mStackActionButton.getPaddingStart()
- - mStackActionButton.getMeasuredWidth()
- : actionButtonRect.left - mStackActionButton.getPaddingStart();
- int buttonTop = actionButtonRect.top +
- (actionButtonRect.height() - mStackActionButton.getMeasuredHeight()) / 2;
- mStackActionButton.layout(buttonLeft, buttonTop,
- buttonLeft + mStackActionButton.getMeasuredWidth(),
- buttonTop + mStackActionButton.getMeasuredHeight());
+ Rect buttonBounds = getStackActionButtonBoundsFromStackLayout();
+ mStackActionButton.layout(buttonBounds.left, buttonBounds.top, buttonBounds.right,
+ buttonBounds.bottom);
}
if (mAwaitingFirstLayout) {
@@ -457,6 +477,17 @@
false /* isDefaultDockState */, -1, true /* animateAlpha */,
true /* animateBounds */);
}
+ if (mStackActionButton != null) {
+ event.addPostAnimationCallback(new Runnable() {
+ @Override
+ public void run() {
+ // Move the clear all button to its new position
+ Rect buttonBounds = getStackActionButtonBoundsFromStackLayout();
+ mStackActionButton.setLeftTopRightBottom(buttonBounds.left, buttonBounds.top,
+ buttonBounds.right, buttonBounds.bottom);
+ }
+ });
+ }
}
public final void onBusEvent(final DragEndEvent event) {
@@ -484,31 +515,30 @@
event.taskView.setLeftTopRightBottom(taskViewRect.left, taskViewRect.top,
taskViewRect.right, taskViewRect.bottom);
- // Remove the task view after it is docked
- mTaskStackView.updateLayoutAlgorithm(false /* boundScroll */);
- stackLayout.getStackTransform(event.task, stackScroller.getStackScroll(), tmpTransform,
- null);
- tmpTransform.alpha = 0;
- tmpTransform.scale = 1f;
- tmpTransform.rect.set(taskViewRect);
- mTaskStackView.updateTaskViewToTransform(event.taskView, tmpTransform,
- new AnimationProps(125, Interpolators.ALPHA_OUT,
- new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- // Dock the task and launch it
- SystemServicesProxy ssp = Recents.getSystemServices();
- ssp.startTaskInDockedMode(getContext(), event.taskView,
- event.task.key.id, dockState.createMode);
+ final OnAnimationStartedListener startedListener = new OnAnimationStartedListener() {
+ @Override
+ public void onAnimationStarted() {
+ EventBus.getDefault().send(new DockedFirstAnimationFrameEvent());
+ mTaskStackView.getStack().removeTask(event.task, AnimationProps.IMMEDIATE,
+ true /* fromDockGesture */);
+ }
+ };
- // Animate the stack accordingly
- AnimationProps stackAnim = new AnimationProps(
- TaskStackView.DEFAULT_SYNC_STACK_DURATION,
- Interpolators.FAST_OUT_SLOW_IN);
- mTaskStackView.getStack().removeTask(event.task, stackAnim,
- true /* fromDockGesture */);
- }
- }));
+ // Dock the task and launch it
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ ssp.startTaskInDockedMode(event.task.key.id, dockState.createMode);
+ final Rect taskRect = getTaskRect(event.taskView);
+ IAppTransitionAnimationSpecsFuture future = mTransitionHelper.getAppTransitionFuture(
+ new AnimationSpecComposer() {
+ @Override
+ public List<AppTransitionAnimationSpec> composeSpecs() {
+ return mTransitionHelper.composeDockAnimationSpec(
+ event.taskView, taskRect);
+ }
+ });
+ ssp.overridePendingAppTransitionMultiThumbFuture(future,
+ mTransitionHelper.wrapStartedListener(startedListener),
+ true /* scaleUp */);
MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_DRAG_DROP);
} else {
@@ -518,6 +548,15 @@
}
}
+ private Rect getTaskRect(TaskView taskView) {
+ int[] location = taskView.getLocationOnScreen();
+ int viewX = location[0];
+ int viewY = location[1];
+ return new Rect(viewX, viewY,
+ (int) (viewX + taskView.getWidth() * taskView.getScaleX()),
+ (int) (viewY + taskView.getHeight() * taskView.getScaleY()));
+ }
+
public final void onBusEvent(DraggingInRecentsEvent event) {
if (mTaskStackView.getTaskViews().size() > 0) {
setTranslationY(event.distanceFromTop - mTaskStackView.getTaskViews().get(0).getY());
@@ -545,19 +584,23 @@
public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
- if (!launchState.launchedWhileDocking && !launchState.launchedFromApp
+ if (!launchState.launchedViaDockGesture && !launchState.launchedFromApp
&& mStack.getTaskCount() > 0) {
- animateBackgroundScrim(DEFAULT_SCRIM_ALPHA,
+ animateBackgroundScrim(1f,
TaskStackAnimationHelper.ENTER_FROM_HOME_TRANSLATION_DURATION);
}
}
- public final void onBusEvent(UpdateBackgroundScrimEvent event) {
- animateBackgroundScrim(event.alpha, DEFAULT_UPDATE_SCRIM_DURATION);
+ public final void onBusEvent(AllTaskViewsDismissedEvent event) {
+ hideStackActionButton(HIDE_STACK_ACTION_BUTTON_DURATION, true /* translate */);
}
- public final void onBusEvent(ResetBackgroundScrimEvent event) {
- animateBackgroundScrim(DEFAULT_SCRIM_ALPHA, DEFAULT_UPDATE_SCRIM_DURATION);
+ public final void onBusEvent(DismissAllTaskViewsEvent event) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ if (!ssp.hasDockedTask()) {
+ // Animate the background away only if we are dismissing Recents to home
+ animateBackgroundScrim(0f, DEFAULT_UPDATE_SCRIM_DURATION);
+ }
}
public final void onBusEvent(ShowStackActionButtonEvent event) {
@@ -565,7 +608,7 @@
return;
}
- showStackActionButton(150, event.translate);
+ showStackActionButton(SHOW_STACK_ACTION_BUTTON_DURATION, event.translate);
}
public final void onBusEvent(HideStackActionButtonEvent event) {
@@ -573,7 +616,7 @@
return;
}
- hideStackActionButton(100, true /* translate */);
+ hideStackActionButton(HIDE_STACK_ACTION_BUTTON_DURATION, true /* translate */);
}
/**
@@ -604,7 +647,6 @@
.alpha(1f)
.setDuration(duration)
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .withLayer()
.start();
}
});
@@ -650,7 +692,6 @@
postAnimationTrigger.decrement();
}
})
- .withLayer()
.start();
postAnimationTrigger.increment();
}
@@ -694,13 +735,49 @@
*/
private void animateBackgroundScrim(float alpha, int duration) {
Utilities.cancelAnimationWithoutCallbacks(mBackgroundScrimAnimator);
- int alphaInt = (int) (alpha * 255);
+ // Calculate the absolute alpha to animate from
+ int fromAlpha = (int) ((mBackgroundScrim.getAlpha() / (DEFAULT_SCRIM_ALPHA * 255)) * 255);
+ int toAlpha = (int) (alpha * 255);
mBackgroundScrimAnimator = ObjectAnimator.ofInt(mBackgroundScrim, Utilities.DRAWABLE_ALPHA,
- mBackgroundScrim.getAlpha(), alphaInt);
+ fromAlpha, toAlpha);
mBackgroundScrimAnimator.setDuration(duration);
- mBackgroundScrimAnimator.setInterpolator(alphaInt > mBackgroundScrim.getAlpha()
- ? Interpolators.ALPHA_OUT
- : Interpolators.ALPHA_IN);
+ mBackgroundScrimAnimator.setInterpolator(toAlpha > fromAlpha
+ ? Interpolators.ALPHA_IN
+ : Interpolators.ALPHA_OUT);
mBackgroundScrimAnimator.start();
}
+
+ /**
+ * @return the bounds of the stack action button.
+ */
+ private Rect getStackActionButtonBoundsFromStackLayout() {
+ Rect actionButtonRect = new Rect(mTaskStackView.mLayoutAlgorithm.mStackActionButtonRect);
+ int left = isLayoutRtl()
+ ? actionButtonRect.left - mStackActionButton.getPaddingLeft()
+ : actionButtonRect.right + mStackActionButton.getPaddingRight()
+ - mStackActionButton.getMeasuredWidth();
+ int top = actionButtonRect.top +
+ (actionButtonRect.height() - mStackActionButton.getMeasuredHeight()) / 2;
+ actionButtonRect.set(left, top, left + mStackActionButton.getMeasuredWidth(),
+ top + mStackActionButton.getMeasuredHeight());
+ return actionButtonRect;
+ }
+
+ public void dump(String prefix, PrintWriter writer) {
+ String innerPrefix = prefix + " ";
+ String id = Integer.toHexString(System.identityHashCode(this));
+
+ writer.print(prefix); writer.print(TAG);
+ writer.print(" awaitingFirstLayout="); writer.print(mAwaitingFirstLayout ? "Y" : "N");
+ writer.print(" insets="); writer.print(Utilities.dumpRect(mSystemInsets));
+ writer.print(" [0x"); writer.print(id); writer.print("]");
+ writer.println();
+
+ if (mStack != null) {
+ mStack.dump(innerPrefix, writer);
+ }
+ if (mTaskStackView != null) {
+ mTaskStackView.dump(innerPrefix, writer);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
index 6bdaaf9..07a1d4e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
@@ -16,39 +16,58 @@
package com.android.systemui.recents.views;
-import android.app.Activity;
import android.content.Context;
import android.view.View;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.RecentsActivity;
+import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
+import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
+import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
+import com.android.systemui.recents.misc.SystemServicesProxy;
/** Manages the scrims for the various system bars. */
public class SystemBarScrimViews {
- Context mContext;
+ private static final int DEFAULT_ANIMATION_DURATION = 150;
- View mNavBarScrimView;
+ private Context mContext;
- boolean mHasNavBarScrim;
- boolean mShouldAnimateNavBarScrim;
+ private View mNavBarScrimView;
- int mNavBarScrimEnterDuration;
+ private boolean mHasNavBarScrim;
+ private boolean mShouldAnimateNavBarScrim;
- public SystemBarScrimViews(Activity activity) {
+ private int mNavBarScrimEnterDuration;
+
+ public SystemBarScrimViews(RecentsActivity activity) {
mContext = activity;
mNavBarScrimView = activity.findViewById(R.id.nav_bar_scrim);
+ mNavBarScrimView.forceHasOverlappingRendering(false);
mNavBarScrimEnterDuration = activity.getResources().getInteger(
R.integer.recents_nav_bar_scrim_enter_duration);
}
/**
- * Prepares the scrim views for animating when entering Recents. This will be called before
- * the first draw.
+ * Updates the nav bar scrim.
*/
- public void prepareEnterRecentsAnimation(boolean hasNavBarScrim, boolean animateNavBarScrim) {
+ public void updateNavBarScrim(boolean animateNavBarScrim, boolean hasStackTasks,
+ AnimationProps animation) {
+ prepareEnterRecentsAnimation(isNavBarScrimRequired(hasStackTasks), animateNavBarScrim);
+ if (animateNavBarScrim && animation != null) {
+ animateNavBarScrimVisibility(true, animation);
+ }
+ }
+
+ /**
+ * Prepares the scrim views for animating when entering Recents. This will be called before
+ * the first draw, unless we are updating the scrim on configuration change.
+ */
+ private void prepareEnterRecentsAnimation(boolean hasNavBarScrim, boolean animateNavBarScrim) {
mHasNavBarScrim = hasNavBarScrim;
mShouldAnimateNavBarScrim = animateNavBarScrim;
@@ -59,7 +78,7 @@
/**
* Animates the nav bar scrim visibility.
*/
- public void animateNavBarScrimVisibility(boolean visible, AnimationProps animation) {
+ private void animateNavBarScrimVisibility(boolean visible, AnimationProps animation) {
int toY = 0;
if (visible) {
mNavBarScrimView.setVisibility(View.VISIBLE);
@@ -78,6 +97,14 @@
}
}
+ /**
+ * @return Whether to show the nav bar scrim.
+ */
+ private boolean isNavBarScrimRequired(boolean hasStackTasks) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ return hasStackTasks && !ssp.hasTransposedNavBar() && !ssp.hasDockedTask();
+ }
+
/**** EventBus events ****/
/**
@@ -100,11 +127,48 @@
*/
public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
if (mHasNavBarScrim) {
- AnimationProps animation = new AnimationProps()
- .setDuration(AnimationProps.BOUNDS,
- TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION)
- .setInterpolator(AnimationProps.BOUNDS, Interpolators.FAST_OUT_SLOW_IN);
+ AnimationProps animation = createBoundsAnimation(
+ TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION);
animateNavBarScrimVisibility(false, animation);
}
}
+
+ public final void onBusEvent(DismissAllTaskViewsEvent event) {
+ if (mHasNavBarScrim) {
+ AnimationProps animation = createBoundsAnimation(
+ TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION);
+ animateNavBarScrimVisibility(false, animation);
+ }
+ }
+
+ public final void onBusEvent(ConfigurationChangedEvent event) {
+ animateScrimToCurrentNavBarState(event.hasStackTasks);
+ }
+
+ public final void onBusEvent(MultiWindowStateChangedEvent event) {
+ animateScrimToCurrentNavBarState(event.hasStackTasks);
+ }
+
+ /**
+ * Animates the scrim to match the state of the current nav bar.
+ */
+ private void animateScrimToCurrentNavBarState(boolean hasStackTasks) {
+ boolean hasNavBarScrim = isNavBarScrimRequired(hasStackTasks);
+ if (mHasNavBarScrim != hasNavBarScrim) {
+ AnimationProps animation = hasNavBarScrim
+ ? createBoundsAnimation(DEFAULT_ANIMATION_DURATION)
+ : AnimationProps.IMMEDIATE;
+ animateNavBarScrimVisibility(hasNavBarScrim, animation);
+ }
+ mHasNavBarScrim = hasNavBarScrim;
+ }
+
+ /**
+ * @return a default animation to aniamte the bounds of the scrim.
+ */
+ private AnimationProps createBoundsAnimation(int duration) {
+ return new AnimationProps()
+ .setDuration(AnimationProps.BOUNDS, duration)
+ .setInterpolator(AnimationProps.BOUNDS, Interpolators.FAST_OUT_SLOW_IN);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
index 4155dd2..fe91f42 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
@@ -20,7 +20,6 @@
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.content.res.Resources;
-import android.graphics.RectF;
import android.util.Log;
import android.view.View;
import android.view.animation.Interpolator;
@@ -32,6 +31,7 @@
import com.android.systemui.recents.RecentsActivityLaunchState;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
+import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
@@ -152,32 +152,26 @@
if (hideTask) {
tv.setVisibility(View.INVISIBLE);
- } else if (launchState.launchedHasConfigurationChanged) {
- // Just load the views as-is
- } else if (launchState.launchedFromApp && !launchState.launchedWhileDocking) {
+ } else if (launchState.launchedFromApp && !launchState.launchedViaDockGesture) {
if (task.isLaunchTarget) {
tv.onPrepareLaunchTargetForEnterAnimation();
} else if (currentTaskOccludesLaunchTarget) {
// Move the task view slightly lower so we can animate it in
- RectF bounds = new RectF(mTmpTransform.rect);
- bounds.offset(0, taskViewAffiliateGroupEnterOffset);
+ mTmpTransform.rect.offset(0, taskViewAffiliateGroupEnterOffset);
+ mTmpTransform.alpha = 0f;
+ mStackView.updateTaskViewToTransform(tv, mTmpTransform,
+ AnimationProps.IMMEDIATE);
tv.setClipViewInStack(false);
- tv.setAlpha(0f);
- tv.setLeftTopRightBottom((int) bounds.left, (int) bounds.top,
- (int) bounds.right, (int) bounds.bottom);
}
} else if (launchState.launchedFromHome) {
// Move the task view off screen (below) so we can animate it in
- RectF bounds = new RectF(mTmpTransform.rect);
- bounds.offset(0, offscreenYOffset);
- tv.setAlpha(0f);
- tv.setLeftTopRightBottom((int) bounds.left, (int) bounds.top, (int) bounds.right,
- (int) bounds.bottom);
- } else if (launchState.launchedWhileDocking) {
- RectF bounds = new RectF(mTmpTransform.rect);
- bounds.offset(0, launchedWhileDockingOffset);
- tv.setLeftTopRightBottom((int) bounds.left, (int) bounds.top, (int) bounds.right,
- (int) bounds.bottom);
+ mTmpTransform.rect.offset(0, offscreenYOffset);
+ mTmpTransform.alpha = 0f;
+ mStackView.updateTaskViewToTransform(tv, mTmpTransform, AnimationProps.IMMEDIATE);
+ } else if (launchState.launchedViaDockGesture) {
+ mTmpTransform.rect.offset(0, launchedWhileDockingOffset);
+ mTmpTransform.alpha = 0f;
+ mStackView.updateTaskViewToTransform(tv, mTmpTransform, AnimationProps.IMMEDIATE);
}
}
}
@@ -225,7 +219,7 @@
stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
null);
- if (launchState.launchedFromApp && !launchState.launchedWhileDocking) {
+ if (launchState.launchedFromApp && !launchState.launchedViaDockGesture) {
if (task.isLaunchTarget) {
tv.onStartLaunchTargetEnterAnimation(mTmpTransform,
taskViewEnterFromAppDuration, mStackView.mScreenPinningEnabled,
@@ -264,7 +258,7 @@
if (i == taskViewCount - 1) {
tv.onStartFrontTaskEnterAnimation(mStackView.mScreenPinningEnabled);
}
- } else if (launchState.launchedWhileDocking) {
+ } else if (launchState.launchedViaDockGesture) {
// Animate the tasks up
AnimationProps taskAnimation = new AnimationProps()
.setDuration(AnimationProps.BOUNDS, (int) (ENTER_WHILE_DOCKING_DURATION +
@@ -284,7 +278,6 @@
public void startExitToHomeAnimation(boolean animated,
ReferenceCountedTrigger postAnimationTrigger) {
TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
- TaskStackViewScroller stackScroller = mStackView.getScroller();
TaskStack stack = mStackView.getStack();
// Break early if there are no tasks
@@ -320,8 +313,7 @@
taskAnimation = AnimationProps.IMMEDIATE;
}
- stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
- null);
+ mTmpTransform.fillIn(tv);
mTmpTransform.alpha = 0f;
mTmpTransform.rect.offset(0, offscreenYOffset);
mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
@@ -335,8 +327,6 @@
public void startLaunchTaskAnimation(TaskView launchingTaskView, boolean screenPinningRequested,
final ReferenceCountedTrigger postAnimationTrigger) {
Resources res = mStackView.getResources();
- TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
- TaskStackViewScroller stackScroller = mStackView.getScroller();
int taskViewExitToAppDuration = res.getInteger(
R.integer.recents_task_exit_to_app_duration);
@@ -354,6 +344,12 @@
if (tv == launchingTaskView) {
tv.setClipViewInStack(false);
+ postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+ @Override
+ public void run() {
+ tv.setClipViewInStack(true);
+ }
+ });
tv.onStartLaunchTargetLaunchAnimation(taskViewExitToAppDuration,
screenPinningRequested, postAnimationTrigger);
} else if (currentTaskOccludesLaunchTarget) {
@@ -363,8 +359,7 @@
postAnimationTrigger.decrementOnAnimationEnd());
postAnimationTrigger.increment();
- stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
- null);
+ mTmpTransform.fillIn(tv);
mTmpTransform.alpha = 0f;
mTmpTransform.rect.offset(0, taskViewAffiliateGroupEnterOffset);
mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
@@ -375,18 +370,17 @@
/**
* Starts the delete animation for the specified {@link TaskView}.
*/
- public void startDeleteTaskAnimation(Task deleteTask, final TaskView deleteTaskView,
+ public void startDeleteTaskAnimation(final TaskView deleteTaskView,
final ReferenceCountedTrigger postAnimationTrigger) {
Resources res = mStackView.getResources();
TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
- TaskStackViewScroller stackScroller = mStackView.getScroller();
int taskViewRemoveAnimDuration = res.getInteger(
R.integer.recents_animate_task_view_remove_duration);
- int taskViewRemoveAnimTranslationXPx = res.getDimensionPixelSize(
- R.dimen.recents_task_view_remove_anim_translation_x);
+ int offscreenXOffset = mStackView.getMeasuredWidth() - stackLayout.mTaskRect.left;
- // Disabling clipping with the stack while the view is animating away
+ // Disabling clipping with the stack while the view is animating away, this will get
+ // restored when the task is next picked up from the view pool
deleteTaskView.setClipViewInStack(false);
// Compose the new animation and transform and star the animation
@@ -395,21 +389,62 @@
@Override
public void onAnimationEnd(Animator animation) {
postAnimationTrigger.decrement();
-
- // Re-enable clipping with the stack (we will reuse this view)
- deleteTaskView.setClipViewInStack(true);
}
});
postAnimationTrigger.increment();
- stackLayout.getStackTransform(deleteTask, stackScroller.getStackScroll(), mTmpTransform,
- null);
+ mTmpTransform.fillIn(deleteTaskView);
mTmpTransform.alpha = 0f;
- mTmpTransform.rect.offset(taskViewRemoveAnimTranslationXPx, 0);
+ mTmpTransform.rect.offset(offscreenXOffset, 0);
mStackView.updateTaskViewToTransform(deleteTaskView, mTmpTransform, taskAnimation);
}
/**
+ * Starts the delete animation for all the {@link TaskView}s.
+ */
+ public void startDeleteAllTasksAnimation(final List<TaskView> taskViews,
+ final ReferenceCountedTrigger postAnimationTrigger) {
+ Resources res = mStackView.getResources();
+ TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
+
+ int taskViewRemoveAnimDuration = res.getInteger(
+ R.integer.recents_animate_task_views_remove_all_duration);
+ int offscreenXOffset = mStackView.getMeasuredWidth() - stackLayout.mTaskRect.left;
+
+ int taskViewCount = taskViews.size();
+ int startDelayMax = 125;
+
+ for (int i = taskViewCount - 1; i >= 0; i--) {
+ TaskView tv = taskViews.get(i);
+ int indexFromFront = taskViewCount - i - 1;
+ float x = Interpolators.ACCELERATE.getInterpolation((float) indexFromFront /
+ taskViewCount);
+ int startDelay = (int) Utilities.mapRange(x, 0, startDelayMax);
+
+ // Disabling clipping with the stack while the view is animating away
+ tv.setClipViewInStack(false);
+
+ // Compose the new animation and transform and star the animation
+ AnimationProps taskAnimation = new AnimationProps(startDelay,
+ taskViewRemoveAnimDuration, Interpolators.FAST_OUT_LINEAR_IN,
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ postAnimationTrigger.decrement();
+
+ // Re-enable clipping with the stack (we will reuse this view)
+ tv.setClipViewInStack(true);
+ }
+ });
+ postAnimationTrigger.increment();
+
+ mTmpTransform.fillIn(tv);
+ mTmpTransform.rect.offset(offscreenXOffset, 0);
+ mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
+ }
+ }
+
+ /**
* Starts the animation to focus the next {@link TaskView} when paging through recents.
*
* @return whether or not this will trigger a scroll in the stack
@@ -421,7 +456,8 @@
TaskStack stack = mStackView.getStack();
final float curScroll = stackScroller.getStackScroll();
- final float newScroll = stackLayout.getStackScrollForTask(newFocusedTask);
+ final float newScroll = stackScroller.getBoundedStackScroll(
+ stackLayout.getStackScrollForTask(newFocusedTask));
boolean willScrollToFront = newScroll > curScroll;
boolean willScroll = Float.compare(newScroll, curScroll) != 0;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
index 93c5fc9..b75a91e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -29,6 +29,7 @@
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.RecentsActivity;
import com.android.systemui.recents.RecentsActivityLaunchState;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.RecentsDebugFlags;
@@ -38,6 +39,7 @@
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
+import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -108,6 +110,8 @@
*/
public class TaskStackLayoutAlgorithm {
+ private static final String TAG = "TaskStackLayoutAlgorithm";
+
// The distribution of view bounds alpha
// XXX: This is a hack because you can currently set the max alpha to be > 1f
public static final float OUTLINE_ALPHA_MIN_VALUE = 0f;
@@ -530,18 +534,24 @@
? stack.indexOfStackTask(launchTask)
: mNumStackTasks - 1;
if (getInitialFocusState() == STATE_FOCUSED) {
+ int maxBottomOffset = mStackBottomOffset + mTaskRect.height();
+ float maxBottomNormX = getNormalizedXFromFocusedY(maxBottomOffset, FROM_BOTTOM);
+ mFocusedRange.offset(0f);
mMinScrollP = 0;
- mMaxScrollP = Math.max(mMinScrollP, mNumStackTasks - 1);
+ mMaxScrollP = Math.max(mMinScrollP, (mNumStackTasks - 1) -
+ Math.max(0, mFocusedRange.getAbsoluteX(maxBottomNormX)));
if (launchState.launchedFromHome) {
mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP);
} else {
mInitialScrollP = Utilities.clamp(launchTaskIndex - 1, mMinScrollP, mMaxScrollP);
}
+ mInitialNormX = null;
} else if (!ssp.hasFreeformWorkspaceSupport() && mNumStackTasks == 1) {
// If there is one stack task, ignore the min/max/initial scroll positions
mMinScrollP = 0;
mMaxScrollP = 0;
mInitialScrollP = 0;
+ mInitialNormX = null;
} else {
// Set the max scroll to be the point where the front most task is visible with the
// stack bottom offset
@@ -552,20 +562,25 @@
mMaxScrollP = Math.max(mMinScrollP, (mNumStackTasks - 1) -
Math.max(0, mUnfocusedRange.getAbsoluteX(maxBottomNormX)));
boolean scrollToFront = launchState.launchedFromHome ||
- launchState.launchedFromAppDocked;
- if (scrollToFront) {
+ launchState.launchedViaDockGesture;
+ if (launchState.launchedWithAltTab) {
+ mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP);
+ mInitialNormX = null;
+ } else if (scrollToFront) {
mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP);
mInitialNormX = null;
} else {
- float normX = getNormalizedXFromUnfocusedY(mInitialTopOffset, FROM_TOP);
- mInitialScrollP = Math.max(mMinScrollP, Math.min(mMaxScrollP, (mNumStackTasks - 2)) -
- Math.max(0, mUnfocusedRange.getAbsoluteX(normX)));
+ // We are overriding the initial two task positions, so set the initial scroll
+ // position to match the second task (aka focused task) position
+ float initialTopNormX = getNormalizedXFromUnfocusedY(mInitialTopOffset, FROM_TOP);
+ mInitialScrollP = Math.max(mMinScrollP, Math.min(mMaxScrollP, (mNumStackTasks - 2))
+ - Math.max(0, mUnfocusedRange.getAbsoluteX(initialTopNormX)));
// Set the initial scroll to the predefined state (which differs from the stack)
mInitialNormX = new float[] {
getNormalizedXFromUnfocusedY(mSystemInsets.bottom + mInitialBottomOffset,
FROM_BOTTOM),
- normX
+ initialTopNormX
};
}
}
@@ -648,8 +663,9 @@
* Returns the default focus state.
*/
public int getInitialFocusState() {
+ RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
RecentsDebugFlags debugFlags = Recents.getDebugFlags();
- if (debugFlags.isPagingEnabled()) {
+ if (debugFlags.isPagingEnabled() || launchState.launchedWithAltTab) {
return STATE_FOCUSED;
} else {
return STATE_UNFOCUSED;
@@ -671,7 +687,6 @@
}
/**
- *
* Returns the current stack state.
*/
public StackState getStackState() {
@@ -801,8 +816,9 @@
public TaskViewTransform getStackTransformScreenCoordinates(Task task, float stackScroll,
TaskViewTransform transformOut, TaskViewTransform frontTransform) {
Rect windowRect = Recents.getSystemServices().getWindowRect();
- TaskViewTransform transform = getStackTransform(task, stackScroll, transformOut,
- frontTransform);
+ TaskViewTransform transform = getStackTransform(task, stackScroll, mFocusState,
+ transformOut, frontTransform, true /* forceUpdate */,
+ false /* ignoreTaskOverrides */);
transform.rect.offset(windowRect.left, windowRect.top);
return transform;
}
@@ -852,7 +868,7 @@
// in screen space
float tmpP = (mMinScrollP - stackScroll) / mNumStackTasks;
int centerYOffset = (mStackRect.top - mTaskRect.top) +
- (mStackRect.height() - mTaskRect.height()) / 2;
+ (mStackRect.height() - mSystemInsets.bottom - mTaskRect.height()) / 2;
y = centerYOffset + getYForDeltaP(tmpP, 0);
z = mMaxTranslationZ;
dimAlpha = 0f;
@@ -953,21 +969,26 @@
*/
public void getTaskStackBounds(Rect windowRect, int topInset, int rightInset,
Rect taskStackBounds) {
- RecentsConfiguration config = Recents.getConfiguration();
- if (config.hasTransposedNavBar) {
- taskStackBounds.set(windowRect.left, windowRect.top + topInset,
- windowRect.right - rightInset, windowRect.bottom);
- } else {
- taskStackBounds.set(windowRect.left, windowRect.top + topInset,
- windowRect.right - rightInset, windowRect.bottom);
- }
+ taskStackBounds.set(windowRect.left, windowRect.top + topInset,
+ windowRect.right - rightInset, windowRect.bottom);
// Ensure that the new width is at most the smaller display edge size
- Rect displayRect = Recents.getSystemServices().getDisplayRect();
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ Rect displayRect = ssp.getDisplayRect();
int sideMargin = getScaleForExtent(windowRect, displayRect, mBaseSideMargin, mMinMargin,
WIDTH);
- int targetStackWidth = Math.min(taskStackBounds.width() - 2 * sideMargin,
- Math.min(displayRect.width(), displayRect.height()));
+ int targetStackWidth = taskStackBounds.width() - 2 * sideMargin;
+ if (ssp.getDisplayOrientation() == Configuration.ORIENTATION_LANDSCAPE) {
+ // If we are in landscape, calculate the width of the stack in portrait and ensure that
+ // we are not larger than that size
+ Rect portraitDisplayRect = new Rect(0, 0,
+ Math.min(displayRect.width(), displayRect.height()),
+ Math.max(displayRect.width(), displayRect.height()));
+ int portraitSideMargin = getScaleForExtent(portraitDisplayRect, portraitDisplayRect,
+ mBaseSideMargin, mMinMargin, WIDTH);
+ targetStackWidth = Math.min(targetStackWidth,
+ portraitDisplayRect.width() - 2 * portraitSideMargin);
+ }
taskStackBounds.inset((taskStackBounds.width() - targetStackWidth) / 2, 0);
}
@@ -1017,6 +1038,18 @@
}
/**
+ * Returns the normalized x on the focused curve given an absolute Y position (relative to the
+ * stack height).
+ */
+ private float getNormalizedXFromFocusedY(float y, @AnchorSide int fromSide) {
+ float offset = (fromSide == FROM_TOP)
+ ? mStackRect.height() - y
+ : y;
+ float offsetPct = offset / mStackRect.height();
+ return mFocusedCurveInterpolator.getX(offsetPct);
+ }
+
+ /**
* Creates a new path for the focused curve.
*/
private Path constructFocusedCurve() {
@@ -1026,10 +1059,13 @@
float topPeekHeightPct = (float) mFocusedTopPeekHeight / mStackRect.height();
float bottomPeekHeightPct = (float) (mStackBottomOffset + mFocusedBottomPeekHeight) /
mStackRect.height();
+ float minBottomPeekHeightPct = (float) (mFocusedTopPeekHeight + mTaskRect.height() -
+ mMinMargin) / mStackRect.height();
Path p = new Path();
p.moveTo(0f, 1f);
p.lineTo(0.5f, 1f - topPeekHeightPct);
- p.lineTo(1f - (0.5f / mFocusedRange.relativeMax), bottomPeekHeightPct);
+ p.lineTo(1f - (0.5f / mFocusedRange.relativeMax), Math.max(1f - minBottomPeekHeightPct,
+ bottomPeekHeightPct));
p.lineTo(1f, 0f);
return p;
}
@@ -1094,9 +1130,11 @@
private int getScaleForExtent(Rect instance, Rect other, int value, int minValue,
@Extent int extent) {
if (extent == WIDTH) {
- return Math.max(minValue, (int) (((float) instance.width() / other.width()) * value));
+ float scale = Utilities.clamp01((float) instance.width() / other.width());
+ return Math.max(minValue, (int) (scale * value));
} else if (extent == HEIGHT) {
- return Math.max(minValue, (int) (((float) instance.height() / other.height()) * value));
+ float scale = Utilities.clamp01((float) instance.height() / other.height());
+ return Math.max(minValue, (int) (scale * value));
}
return value;
}
@@ -1121,4 +1159,44 @@
mBackOfStackTransform.visible = true;
mFrontOfStackTransform.visible = true;
}
-}
+
+ public void dump(String prefix, PrintWriter writer) {
+ String innerPrefix = prefix + " ";
+
+ writer.print(prefix); writer.print(TAG);
+ writer.write(" numStackTasks="); writer.write(mNumStackTasks);
+ writer.println();
+
+ writer.print(innerPrefix);
+ writer.print("insets="); writer.print(Utilities.dumpRect(mSystemInsets));
+ writer.print(" stack="); writer.print(Utilities.dumpRect(mStackRect));
+ writer.print(" task="); writer.print(Utilities.dumpRect(mTaskRect));
+ writer.print(" freeform="); writer.print(Utilities.dumpRect(mFreeformRect));
+ writer.print(" actionButton="); writer.print(Utilities.dumpRect(mStackActionButtonRect));
+ writer.println();
+
+ writer.print(innerPrefix);
+ writer.print("minScroll="); writer.print(mMinScrollP);
+ writer.print(" maxScroll="); writer.print(mMaxScrollP);
+ writer.print(" initialScroll="); writer.print(mInitialScrollP);
+ writer.println();
+
+ writer.print(innerPrefix);
+ writer.print("focusState="); writer.print(mFocusState);
+ writer.println();
+
+ if (mTaskIndexOverrideMap.size() > 0) {
+ for (int i = mTaskIndexOverrideMap.size() - 1; i >= 0; i--) {
+ int taskId = mTaskIndexOverrideMap.keyAt(i);
+ float x = mTaskIndexMap.get(taskId);
+ float overrideX = mTaskIndexOverrideMap.get(taskId, 0f);
+
+ writer.print(innerPrefix);
+ writer.print("taskId= "); writer.print(taskId);
+ writer.print(" x= "); writer.print(x);
+ writer.print(" overrideX= "); writer.print(overrideX);
+ writer.println();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index c9cc1e9..13c8403 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -22,11 +22,13 @@
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
+import android.annotation.IntDef;
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
@@ -59,8 +61,8 @@
import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
import com.android.systemui.recents.events.activity.EnterRecentsTaskStackAnimationCompletedEvent;
import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
-import com.android.systemui.recents.events.activity.HideStackActionButtonEvent;
import com.android.systemui.recents.events.activity.HideRecentsEvent;
+import com.android.systemui.recents.events.activity.HideStackActionButtonEvent;
import com.android.systemui.recents.events.activity.IterateRecentsEvent;
import com.android.systemui.recents.events.activity.LaunchNextTaskRequestEvent;
import com.android.systemui.recents.events.activity.LaunchTaskEvent;
@@ -70,6 +72,7 @@
import com.android.systemui.recents.events.activity.ShowStackActionButtonEvent;
import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
+import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
import com.android.systemui.recents.events.ui.UpdateFreeformTaskViewVisibilityEvent;
@@ -87,6 +90,9 @@
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
@@ -97,6 +103,8 @@
TaskStackLayoutAlgorithm.TaskStackLayoutAlgorithmCallbacks,
ViewPool.ViewPoolConsumer<TaskView, Task> {
+ private static final String TAG = "TaskStackView";
+
private final static String KEY_SAVED_STATE_SUPER = "saved_instance_state_super";
private final static String KEY_SAVED_STATE_LAYOUT_FOCUSED_STATE =
"saved_instance_state_layout_focused_state";
@@ -109,17 +117,28 @@
public static final int DEFAULT_SYNC_STACK_DURATION = 200;
private static final int DRAG_SCALE_DURATION = 175;
- private static final float DRAG_SCALE_FACTOR = 1.05f;
+ static final float DRAG_SCALE_FACTOR = 1.05f;
private static final int LAUNCH_NEXT_SCROLL_BASE_DURATION = 216;
private static final int LAUNCH_NEXT_SCROLL_INCR_DURATION = 32;
- private static final ArraySet<Task.TaskKey> EMPTY_TASK_SET = new ArraySet<>();
+ // The actions to perform when resetting to initial state,
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({INITIAL_STATE_UPDATE_NONE, INITIAL_STATE_UPDATE_ALL, INITIAL_STATE_UPDATE_LAYOUT_ONLY})
+ public @interface InitialStateAction {}
+ /** Do not update the stack and layout to the initial state. */
+ private static final int INITIAL_STATE_UPDATE_NONE = 0;
+ /** Update both the stack and layout to the initial state. */
+ private static final int INITIAL_STATE_UPDATE_ALL = 1;
+ /** Update only the layout to the initial state. */
+ private static final int INITIAL_STATE_UPDATE_LAYOUT_ONLY = 2;
LayoutInflater mInflater;
TaskStack mStack = new TaskStack();
@ViewDebug.ExportedProperty(deepExport=true, prefix="layout_")
TaskStackLayoutAlgorithm mLayoutAlgorithm;
+ // The stable layout algorithm is only used to calculate the task rect with the stable bounds
+ TaskStackLayoutAlgorithm mStableLayoutAlgorithm;
@ViewDebug.ExportedProperty(deepExport=true, prefix="scroller_")
TaskStackViewScroller mStackScroller;
@ViewDebug.ExportedProperty(deepExport=true, prefix="touch_")
@@ -148,6 +167,9 @@
@ViewDebug.ExportedProperty(category="recents")
boolean mAwaitingFirstLayout = true;
@ViewDebug.ExportedProperty(category="recents")
+ @InitialStateAction
+ int mInitialState = INITIAL_STATE_UPDATE_ALL;
+ @ViewDebug.ExportedProperty(category="recents")
boolean mInMeasureLayout = false;
@ViewDebug.ExportedProperty(category="recents")
boolean mEnterAnimationComplete = false;
@@ -223,6 +245,7 @@
mViewPool = new ViewPool<>(context, this);
mInflater = LayoutInflater.from(context);
mLayoutAlgorithm = new TaskStackLayoutAlgorithm(context, this);
+ mStableLayoutAlgorithm = new TaskStackLayoutAlgorithm(context, null);
mStackScroller = new TaskStackViewScroller(context, this, mLayoutAlgorithm);
mTouchHandler = new TaskStackViewTouchHandler(context, this, mStackScroller);
mAnimationHelper = new TaskStackAnimationHelper(context, this);
@@ -255,41 +278,6 @@
}
}
- /**
- * Called only if we are resuming Recents.
- */
- void onResume(boolean isResumingFromVisible) {
- if (!isResumingFromVisible) {
- // Reset the focused task
- resetFocusedTask(getFocusedTask());
- }
-
- // Reset the state of each of the task views
- List<TaskView> taskViews = new ArrayList<>();
- taskViews.addAll(getTaskViews());
- taskViews.addAll(mViewPool.getViews());
- for (int i = taskViews.size() - 1; i >= 0; i--) {
- taskViews.get(i).onResume(isResumingFromVisible);
- }
-
- // Reset the stack state
- readSystemFlags();
- mTaskViewsClipDirty = true;
- mEnterAnimationComplete = false;
- mUIDozeTrigger.stopDozing();
- if (isResumingFromVisible) {
- // Animate in the freeform workspace
- int ffBgAlpha = mLayoutAlgorithm.getStackState().freeformBackgroundAlpha;
- animateFreeformWorkspaceBackgroundAlpha(ffBgAlpha, new AnimationProps(150,
- Interpolators.FAST_OUT_SLOW_IN));
- } else {
- mStackScroller.reset();
- mLayoutAlgorithm.reset();
- mAwaitingFirstLayout = true;
- requestLayout();
- }
- }
-
@Override
protected void onAttachedToWindow() {
EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
@@ -304,34 +292,54 @@
}
/**
+ * Called from RecentsActivity when it is relaunched.
+ */
+ void onReload(boolean isResumingFromVisible) {
+ if (!isResumingFromVisible) {
+ // Reset the focused task
+ resetFocusedTask(getFocusedTask());
+ }
+
+ // Reset the state of each of the task views
+ List<TaskView> taskViews = new ArrayList<>();
+ taskViews.addAll(getTaskViews());
+ taskViews.addAll(mViewPool.getViews());
+ for (int i = taskViews.size() - 1; i >= 0; i--) {
+ taskViews.get(i).onReload(isResumingFromVisible);
+ }
+
+ // Reset the stack state
+ readSystemFlags();
+ mTaskViewsClipDirty = true;
+ mEnterAnimationComplete = false;
+ mUIDozeTrigger.stopDozing();
+ if (isResumingFromVisible) {
+ // Animate in the freeform workspace
+ int ffBgAlpha = mLayoutAlgorithm.getStackState().freeformBackgroundAlpha;
+ animateFreeformWorkspaceBackgroundAlpha(ffBgAlpha, new AnimationProps(150,
+ Interpolators.FAST_OUT_SLOW_IN));
+ } else {
+ mStackScroller.reset();
+ mStableLayoutAlgorithm.reset();
+ mLayoutAlgorithm.reset();
+ }
+
+ // Since we always animate to the same place in (the initial state), always reset the stack
+ // to the initial state when resuming
+ mAwaitingFirstLayout = true;
+ mInitialState = INITIAL_STATE_UPDATE_ALL;
+ requestLayout();
+ }
+
+ /**
* Sets the stack tasks of this TaskStackView from the given TaskStack.
*/
- public void setTasks(TaskStack stack, boolean notifyStackChanges, boolean relayoutTaskStack,
- boolean multiWindowChange) {
+ public void setTasks(TaskStack stack, boolean allowNotifyStackChanges) {
boolean isInitialized = mLayoutAlgorithm.isInitialized();
+ // Only notify if we are already initialized, otherwise, everything will pick up all the
+ // new and old tasks when we next layout
mStack.setTasks(getContext(), stack.computeAllTasksList(),
- notifyStackChanges && isInitialized);
- if (isInitialized) {
- // Only update the layout if we are notifying, otherwise, we will update it in the next
- // measure/layout pass
- updateLayoutAlgorithm(false /* boundScroll */, EMPTY_TASK_SET);
- if (!multiWindowChange) {
- updateToInitialState();
- }
-
- if (relayoutTaskStack) {
- relayoutTaskViews(AnimationProps.IMMEDIATE);
-
- // Rebind all the task views. This will not trigger new resources to be loaded
- // unless they have actually changed
- List<TaskView> taskViews = getTaskViews();
- int taskViewCount = taskViews.size();
- for (int i = 0; i < taskViewCount; i++) {
- TaskView tv = taskViews.get(i);
- bindTaskView(tv, tv.getTask());
- }
- }
- }
+ allowNotifyStackChanges && isInitialized);
}
/** Returns the task stack. */
@@ -342,8 +350,10 @@
/**
* Updates this TaskStackView to the initial state.
*/
- public void updateToInitialState() {
- mStackScroller.setStackScrollToInitialState();
+ public void updateToInitialState(boolean scrollToInitialState) {
+ if (scrollToInitialState) {
+ mStackScroller.setStackScrollToInitialState();
+ }
mLayoutAlgorithm.updateToInitialState(mStack.getStackTasks());
}
@@ -591,17 +601,14 @@
if (tv == null) {
tv = mViewPool.pickUpViewFromPool(task, task);
if (task.isFreeformTask()) {
- tv.updateViewPropertiesToTaskTransform(transform, AnimationProps.IMMEDIATE,
- mRequestUpdateClippingListener);
+ updateTaskViewToTransform(tv, transform, AnimationProps.IMMEDIATE);
} else {
if (transform.rect.top <= mLayoutAlgorithm.mStackRect.top) {
- tv.updateViewPropertiesToTaskTransform(
- mLayoutAlgorithm.getBackOfStackTransform(),
- AnimationProps.IMMEDIATE, mRequestUpdateClippingListener);
+ updateTaskViewToTransform(tv, mLayoutAlgorithm.getBackOfStackTransform(),
+ AnimationProps.IMMEDIATE);
} else {
- tv.updateViewPropertiesToTaskTransform(
- mLayoutAlgorithm.getFrontOfStackTransform(),
- AnimationProps.IMMEDIATE, mRequestUpdateClippingListener);
+ updateTaskViewToTransform(tv, mLayoutAlgorithm.getFrontOfStackTransform(),
+ AnimationProps.IMMEDIATE);
}
}
} else {
@@ -771,8 +778,6 @@
* Updates the clip for each of the task views from back to front.
*/
private void clipTaskViews() {
- RecentsConfiguration config = Recents.getConfiguration();
-
// Update the clip on each task child
List<TaskView> taskViews = getTaskViews();
TaskView tmpTv = null;
@@ -1177,6 +1182,7 @@
*/
public void setSystemInsets(Rect systemInsets) {
if (!systemInsets.equals(mLayoutAlgorithm.mSystemInsets)) {
+ mStableLayoutAlgorithm.setSystemInsets(systemInsets);
mLayoutAlgorithm.setSystemInsets(systemInsets);
requestLayout();
}
@@ -1205,21 +1211,21 @@
}
// Compute the rects in the stack algorithm
+ mStableLayoutAlgorithm.initialize(mStableWindowRect, mStableStackBounds,
+ TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
mLayoutAlgorithm.initialize(mWindowRect, mStackBounds,
TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
- updateLayoutAlgorithm(false /* boundScroll */, EMPTY_TASK_SET);
+ updateLayoutAlgorithm(false /* boundScroll */, mIgnoreTasks);
// If this is the first layout, then scroll to the front of the stack, then update the
// TaskViews with the stack so that we can lay them out
- // TODO: The second check is a workaround for wacky layouts that we get while docking via
- // long pressing the recents button
- if (mAwaitingFirstLayout ||
- (mStackScroller.getStackScroll() == mLayoutAlgorithm.mInitialScrollP)) {
- updateToInitialState();
+ if (mAwaitingFirstLayout || mInitialState != INITIAL_STATE_UPDATE_NONE) {
+ updateToInitialState(mInitialState != INITIAL_STATE_UPDATE_LAYOUT_ONLY);
+ mInitialState = INITIAL_STATE_UPDATE_NONE;
}
// Rebind all the views, including the ignore ones
- bindVisibleTaskViews(mStackScroller.getStackScroll(), EMPTY_TASK_SET,
+ bindVisibleTaskViews(mStackScroller.getStackScroll(), mIgnoreTasks,
false /* ignoreTaskOverrides */);
// Measure each of the TaskViews
@@ -1244,12 +1250,11 @@
} else {
mTmpRect.setEmpty();
}
+ Rect taskRect = mStableLayoutAlgorithm.mTaskRect;
tv.measure(
- MeasureSpec.makeMeasureSpec(
- mLayoutAlgorithm.mTaskRect.width() + mTmpRect.left + mTmpRect.right,
+ MeasureSpec.makeMeasureSpec(taskRect.width() + mTmpRect.left + mTmpRect.right,
MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(
- mLayoutAlgorithm.mTaskRect.height() + mTmpRect.top + mTmpRect.bottom,
+ MeasureSpec.makeMeasureSpec(taskRect.height() + mTmpRect.top + mTmpRect.bottom,
MeasureSpec.EXACTLY));
}
@@ -1261,7 +1266,7 @@
mTmpTaskViews.addAll(mViewPool.getViews());
int taskViewCount = mTmpTaskViews.size();
for (int i = 0; i < taskViewCount; i++) {
- layoutTaskView(mTmpTaskViews.get(i));
+ layoutTaskView(changed, mTmpTaskViews.get(i));
}
if (changed) {
@@ -1269,8 +1274,9 @@
mStackScroller.boundScroll();
}
}
+
// Relayout all of the task views including the ignored ones
- relayoutTaskViews(AnimationProps.IMMEDIATE, EMPTY_TASK_SET);
+ relayoutTaskViews(AnimationProps.IMMEDIATE, mIgnoreTasks);
clipTaskViews();
if (mAwaitingFirstLayout || !mEnterAnimationComplete) {
@@ -1282,16 +1288,21 @@
/**
* Lays out a TaskView.
*/
- private void layoutTaskView(TaskView tv) {
- if (tv.getBackground() != null) {
- tv.getBackground().getPadding(mTmpRect);
+ private void layoutTaskView(boolean changed, TaskView tv) {
+ if (changed) {
+ if (tv.getBackground() != null) {
+ tv.getBackground().getPadding(mTmpRect);
+ } else {
+ mTmpRect.setEmpty();
+ }
+ Rect taskRect = mStableLayoutAlgorithm.mTaskRect;
+ tv.cancelTransformAnimation();
+ tv.layout(taskRect.left - mTmpRect.left, taskRect.top - mTmpRect.top,
+ taskRect.right + mTmpRect.right, taskRect.bottom + mTmpRect.bottom);
} else {
- mTmpRect.setEmpty();
+ // If the layout has not changed, then just lay it out again in-place
+ tv.layout(tv.getLeft(), tv.getTop(), tv.getRight(), tv.getBottom());
}
- Rect taskRect = mLayoutAlgorithm.mTaskRect;
- tv.cancelTransformAnimation();
- tv.layout(taskRect.left - mTmpRect.left, taskRect.top - mTmpRect.top,
- taskRect.right + mTmpRect.right, taskRect.bottom + mTmpRect.bottom);
}
/** Handler for the first layout. */
@@ -1315,7 +1326,8 @@
}
// Update the stack action button visibility
- if (mStackScroller.getStackScroll() < SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD) {
+ if (mStackScroller.getStackScroll() < SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
+ mStack.getTaskCount() > 0) {
EventBus.getDefault().send(new ShowStackActionButtonEvent(false /* translate */));
} else {
EventBus.getDefault().send(new HideStackActionButtonEvent());
@@ -1439,6 +1451,42 @@
}
}
+ @Override
+ public void onStackTasksRemoved(TaskStack stack) {
+ // Reset the focused task
+ resetFocusedTask(getFocusedTask());
+
+ // Return all the views to the pool
+ List<TaskView> taskViews = new ArrayList<>();
+ taskViews.addAll(getTaskViews());
+ for (int i = taskViews.size() - 1; i >= 0; i--) {
+ mViewPool.returnViewToPool(taskViews.get(i));
+ }
+
+ // Remove all the ignore tasks
+ mIgnoreTasks.clear();
+
+ // If there are no remaining tasks, then just close recents
+ EventBus.getDefault().send(new AllTaskViewsDismissedEvent(
+ R.string.recents_empty_message_dismissed_all));
+ }
+
+ @Override
+ public void onStackTasksUpdated(TaskStack stack) {
+ // Update the layout and immediately layout
+ updateLayoutAlgorithm(false /* boundScroll */);
+ relayoutTaskViews(AnimationProps.IMMEDIATE);
+
+ // Rebind all the task views. This will not trigger new resources to be loaded
+ // unless they have actually changed
+ List<TaskView> taskViews = getTaskViews();
+ int taskViewCount = taskViews.size();
+ for (int i = 0; i < taskViewCount; i++) {
+ TaskView tv = taskViews.get(i);
+ bindTaskView(tv, tv.getTask());
+ }
+ }
+
/**** ViewPoolConsumer Implementation ****/
@Override
@@ -1488,7 +1536,7 @@
}
addViewInLayout(tv, insertIndex, params, true /* preventRequestLayout */);
measureTaskView(tv);
- layoutTaskView(tv);
+ layoutTaskView(true /* changed */, tv);
}
} else {
attachViewToParent(tv, insertIndex, tv.getLayoutParams());
@@ -1574,7 +1622,8 @@
if (mEnterAnimationComplete) {
if (prevScroll > SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
- curScroll <= SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD) {
+ curScroll <= SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
+ mStack.getTaskCount() > 0) {
EventBus.getDefault().send(new ShowStackActionButtonEvent(true /* translate */));
} else if (prevScroll < HIDE_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
curScroll >= HIDE_STACK_ACTION_BUTTON_SCROLL_THRESHOLD) {
@@ -1682,14 +1731,42 @@
}
}
- public final void onBusEvent(final DismissTaskViewEvent event) {
+ public final void onBusEvent(DismissTaskViewEvent event) {
// For visible children, defer removing the task until after the animation
- mAnimationHelper.startDeleteTaskAnimation(event.task, event.taskView,
- event.getAnimationTrigger());
+ mAnimationHelper.startDeleteTaskAnimation(event.taskView, event.getAnimationTrigger());
+ }
+
+ public final void onBusEvent(final DismissAllTaskViewsEvent event) {
+ // Keep track of the tasks which will have their data removed
+ ArrayList<Task> tasks = new ArrayList<>(mStack.getStackTasks());
+ mAnimationHelper.startDeleteAllTasksAnimation(getTaskViews(), event.getAnimationTrigger());
+ event.addPostAnimationCallback(new Runnable() {
+ @Override
+ public void run() {
+ // Announce for accessibility
+ announceForAccessibility(getContext().getString(
+ R.string.accessibility_recents_all_items_dismissed));
+
+ // Remove all tasks and delete the task data for all tasks
+ mStack.removeAllTasks();
+ for (int i = tasks.size() - 1; i >= 0; i--) {
+ EventBus.getDefault().send(new DeleteTaskDataEvent(tasks.get(i)));
+ }
+
+ MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_DISMISS_ALL);
+ }
+ });
+
}
public final void onBusEvent(TaskViewDismissedEvent event) {
- removeTaskViewFromStack(event.taskView, event.task);
+ // Announce for accessibility
+ announceForAccessibility(getContext().getString(
+ R.string.accessibility_recents_item_dismissed, event.task.title));
+
+ // Remove the task from the stack
+ mStack.removeTask(event.task, new AnimationProps(DEFAULT_SYNC_STACK_DURATION,
+ Interpolators.FAST_OUT_SLOW_IN), false /* fromDockGesture */);
EventBus.getDefault().send(new DeleteTaskDataEvent(event.task));
MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_DISMISS,
@@ -1911,23 +1988,27 @@
}
public final void onBusEvent(ConfigurationChangedEvent event) {
+ mStableLayoutAlgorithm.reloadOnConfigurationChange(getContext());
mLayoutAlgorithm.reloadOnConfigurationChange(getContext());
- mLayoutAlgorithm.initialize(mWindowRect, mStackBounds,
- TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
- }
- /**
- * Removes the task from the stack, and updates the focus to the next task in the stack if the
- * removed TaskView was focused.
- */
- private void removeTaskViewFromStack(TaskView tv, Task task) {
- // Announce for accessibility
- tv.announceForAccessibility(getContext().getString(
- R.string.accessibility_recents_item_dismissed, task.title));
+ // Notify the task views of the configuration change so they can reload their resources
+ if (!event.fromMultiWindow) {
+ mTmpTaskViews.clear();
+ mTmpTaskViews.addAll(getTaskViews());
+ mTmpTaskViews.addAll(mViewPool.getViews());
+ int taskViewCount = mTmpTaskViews.size();
+ for (int i = 0; i < taskViewCount; i++) {
+ mTmpTaskViews.get(i).onConfigurationChanged();
+ }
+ }
- // Remove the task from the stack
- mStack.removeTask(task, new AnimationProps(DEFAULT_SYNC_STACK_DURATION,
- Interpolators.FAST_OUT_SLOW_IN), false /* fromDockGesture */);
+ // Trigger a new layout and update to the initial state if necessary
+ if (event.fromMultiWindow) {
+ mInitialState = INITIAL_STATE_UPDATE_ALL;
+ } else if (event.fromOrientationChange) {
+ mInitialState = INITIAL_STATE_UPDATE_LAYOUT_ONLY;
+ }
+ requestLayout();
}
/**
@@ -1989,4 +2070,37 @@
mScreenPinningEnabled = ssp.getSystemSetting(getContext(),
Settings.System.LOCK_TO_APP_ENABLED) != 0;
}
+
+ public void dump(String prefix, PrintWriter writer) {
+ String innerPrefix = prefix + " ";
+ String id = Integer.toHexString(System.identityHashCode(this));
+
+ writer.print(prefix); writer.print(TAG);
+ writer.print(" hasDefRelayout=");
+ writer.print(mDeferredTaskViewLayoutAnimation != null ? "Y" : "N");
+ writer.print(" clipDirty="); writer.print(mTaskViewsClipDirty ? "Y" : "N");
+ writer.print(" awaitingFirstLayout="); writer.print(mAwaitingFirstLayout ? "Y" : "N");
+ writer.print(" initialState="); writer.print(mInitialState);
+ writer.print(" inMeasureLayout="); writer.print(mInMeasureLayout ? "Y" : "N");
+ writer.print(" enterAnimCompleted="); writer.print(mEnterAnimationComplete ? "Y" : "N");
+ writer.print(" touchExplorationOn="); writer.print(mTouchExplorationEnabled ? "Y" : "N");
+ writer.print(" screenPinningOn="); writer.print(mScreenPinningEnabled ? "Y" : "N");
+ writer.print(" numIgnoreTasks="); writer.print(mIgnoreTasks.size());
+ writer.print(" numViewPool="); writer.print(mViewPool.getViews().size());
+ writer.print(" stableStackBounds="); writer.print(Utilities.dumpRect(mStableStackBounds));
+ writer.print(" stackBounds="); writer.print(Utilities.dumpRect(mStackBounds));
+ writer.print(" stableWindow="); writer.print(Utilities.dumpRect(mStableWindowRect));
+ writer.print(" window="); writer.print(Utilities.dumpRect(mWindowRect));
+ writer.print(" [0x"); writer.print(id); writer.print("]");
+ writer.println();
+
+ if (mFocusedTask != null) {
+ writer.print(innerPrefix);
+ writer.print("Focused task: ");
+ mFocusedTask.dump(innerPrefix, writer);
+ }
+
+ mLayoutAlgorithm.dump(innerPrefix, writer);
+ mStackScroller.dump(innerPrefix, writer);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
index 583fb88..19b3c94 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
@@ -30,6 +30,8 @@
import com.android.systemui.R;
import com.android.systemui.recents.misc.Utilities;
+import java.io.PrintWriter;
+
/* The scrolling logic for a TaskStackView */
public class TaskStackViewScroller {
@@ -246,4 +248,10 @@
mScroller.abortAnimation();
}
}
+
+ public void dump(String prefix, PrintWriter writer) {
+ writer.print(prefix); writer.print(TAG);
+ writer.print(" stackScroll:"); writer.print(mStackScrollP);
+ writer.println();
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index aed19c3..ee0de1a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -356,6 +356,11 @@
return;
}
+ // Disallow tapping above and below the stack to dismiss recents
+ if (x > mSv.mLayoutAlgorithm.mStackRect.left && x < mSv.mLayoutAlgorithm.mStackRect.right) {
+ return;
+ }
+
// If tapping on the freeform workspace background, just launch the first freeform task
SystemServicesProxy ssp = Recents.getSystemServices();
if (ssp.hasFreeformWorkspaceSupport()) {
@@ -507,13 +512,13 @@
tv.setClipViewInStack(true);
// Re-enable touch events from this task view
tv.setTouchEnabled(true);
+ // Remove the task view from the stack
+ EventBus.getDefault().send(new TaskViewDismissedEvent(tv.getTask(), tv));
// Update the scroll to the final scroll position from onBeginDrag()
mSv.getScroller().setStackScroll(mTargetStackScroll, null);
// Update the focus state to the final focus state
mSv.getStackAlgorithm().setFocusState(TaskStackLayoutAlgorithm.STATE_UNFOCUSED);
mSv.getStackAlgorithm().clearUnfocusedTaskOverrides();
- // Remove the task view from the stack
- EventBus.getDefault().send(new TaskViewDismissedEvent(tv.getTask(), tv));
// Stop tracking this deletion animation
mSwipeHelperAnimations.remove(v);
// Keep track of deletions by keyboard
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 107d8d4..f8ed700 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -22,7 +22,9 @@
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
+import android.app.ActivityManager;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Outline;
import android.graphics.Paint;
@@ -128,8 +130,6 @@
@ViewDebug.ExportedProperty(category="recents")
float mDimAlpha;
- PorterDuffColorFilter mDimColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_ATOP);
- Paint mDimLayerPaint = new Paint();
float mActionButtonTranslationZ;
@ViewDebug.ExportedProperty(deepExport=true, prefix="task_")
@@ -192,15 +192,15 @@
mCb = cb;
}
- /** Resets this TaskView for reuse. */
- void onResume(boolean isResumingFromVisible) {
+ /**
+ * Called from RecentsActivity when it is relaunched.
+ */
+ void onReload(boolean isResumingFromVisible) {
resetNoUserInteractionState();
readSystemFlags();
if (!isResumingFromVisible) {
resetViewProperties();
- setClipViewInStack(false);
}
- setCallbacks(null);
}
/** Gets the task */
@@ -238,6 +238,13 @@
mActionButtonTranslationZ = mActionButtonView.getTranslationZ();
}
+ /**
+ * Update the task view when the configuration changes.
+ */
+ void onConfigurationChanged() {
+ mHeaderView.onConfigurationChanged();
+ }
+
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
@@ -377,7 +384,7 @@
void dismissTask() {
// Animate out the view and call the callback
final TaskView tv = this;
- DismissTaskViewEvent dismissEvent = new DismissTaskViewEvent(tv, mTask);
+ DismissTaskViewEvent dismissEvent = new DismissTaskViewEvent(tv);
dismissEvent.addPostAnimationCallback(new Runnable() {
@Override
public void run() {
@@ -575,9 +582,9 @@
}
@Override
- public void onTaskDataLoaded(Task task) {
+ public void onTaskDataLoaded(Task task, ActivityManager.TaskThumbnailInfo thumbnailInfo) {
// Bind each of the views to the new task data
- mThumbnailView.rebindToTask(mTask, mIsDisabledInSafeMode);
+ mThumbnailView.rebindToTask(mTask, thumbnailInfo, mIsDisabledInSafeMode);
mHeaderView.rebindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode);
mTaskDataLoaded = true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index a2e9573..570ff8a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -34,9 +34,11 @@
import android.os.CountDownTimer;
import android.support.v4.graphics.ColorUtils;
import android.util.AttributeSet;
+import android.view.Gravity;
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.ViewDebug;
+import android.view.ViewGroup;
import android.view.ViewStub;
import android.widget.FrameLayout;
import android.widget.ImageView;
@@ -153,6 +155,8 @@
// Header drawables
@ViewDebug.ExportedProperty(category="recents")
Rect mTaskViewRect = new Rect();
+ int mHeaderBarHeight;
+ int mHeaderButtonPadding;
int mCornerRadius;
int mHighlightHeight;
@ViewDebug.ExportedProperty(category="recents")
@@ -245,6 +249,67 @@
}
mFocusTimerIndicatorStub = (ViewStub) findViewById(R.id.focus_timer_indicator_stub);
mAppOverlayViewStub = (ViewStub) findViewById(R.id.app_overlay_stub);
+
+ onConfigurationChanged();
+ }
+
+ /**
+ * Programmatically sets the layout params for a header bar layout. This is necessary because
+ * we can't get resources based on the current configuration, but instead need to get them
+ * based on the device configuration.
+ */
+ private void updateLayoutParams(View icon, View title, View secondaryButton, View button) {
+ FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, mHeaderBarHeight, Gravity.TOP);
+ setLayoutParams(lp);
+ lp = new FrameLayout.LayoutParams(mHeaderBarHeight, mHeaderBarHeight, Gravity.START);
+ icon.setLayoutParams(lp);
+ lp = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.START | Gravity.CENTER_VERTICAL);
+ lp.setMarginStart(mHeaderBarHeight);
+ lp.setMarginEnd(mMoveTaskButton != null
+ ? 2 * mHeaderBarHeight
+ : mHeaderBarHeight);
+ title.setLayoutParams(lp);
+ if (secondaryButton != null) {
+ lp = new FrameLayout.LayoutParams(mHeaderBarHeight, mHeaderBarHeight, Gravity.END);
+ lp.setMarginEnd(mHeaderBarHeight);
+ secondaryButton.setLayoutParams(lp);
+ secondaryButton.setPadding(mHeaderButtonPadding, mHeaderButtonPadding,
+ mHeaderButtonPadding, mHeaderButtonPadding);
+ }
+ lp = new FrameLayout.LayoutParams(mHeaderBarHeight, mHeaderBarHeight, Gravity.END);
+ button.setLayoutParams(lp);
+ button.setPadding(mHeaderButtonPadding, mHeaderButtonPadding, mHeaderButtonPadding,
+ mHeaderButtonPadding);
+ }
+
+ /**
+ * Update the header view when the configuration changes.
+ */
+ void onConfigurationChanged() {
+ // Update the dimensions of everything in the header. We do this because we need to use
+ // resources for the display, and not the current configuration.
+ Resources res = getResources();
+ mHeaderBarHeight = TaskStackLayoutAlgorithm.getDimensionForDevice(res,
+ R.dimen.recents_task_view_header_height,
+ R.dimen.recents_task_view_header_height,
+ R.dimen.recents_task_view_header_height,
+ R.dimen.recents_task_view_header_height_tablet_land,
+ R.dimen.recents_task_view_header_height,
+ R.dimen.recents_task_view_header_height_tablet_land);
+ mHeaderButtonPadding = TaskStackLayoutAlgorithm.getDimensionForDevice(res,
+ R.dimen.recents_task_view_header_button_padding,
+ R.dimen.recents_task_view_header_button_padding,
+ R.dimen.recents_task_view_header_button_padding,
+ R.dimen.recents_task_view_header_button_padding_tablet_land,
+ R.dimen.recents_task_view_header_button_padding,
+ R.dimen.recents_task_view_header_button_padding_tablet_land);
+ updateLayoutParams(mIconView, findViewById(R.id.title_container), mMoveTaskButton,
+ mDismissButton);
+ if (mAppOverlayView != null) {
+ updateLayoutParams(mAppIconView, mAppTitleView, null, mAppInfoView);
+ }
}
@Override
@@ -394,7 +459,7 @@
if (!mTitleView.getText().toString().equals(t.title)) {
mTitleView.setText(t.title);
}
- mTitleView.setContentDescription(t.contentDescription);
+ mTitleView.setContentDescription(t.titleDescription);
mTitleView.setTextColor(t.useLightOnPrimaryColor ?
mTaskBarViewLightTextColor : mTaskBarViewDarkTextColor);
if (!t.isDockable && ssp.hasDockedTask()) {
@@ -436,6 +501,7 @@
// In accessibility, a single click on the focused app info button will show it
if (touchExplorationEnabled) {
+ mIconView.setContentDescription(t.appInfoDescription);
mIconView.setOnClickListener(this);
}
}
@@ -579,6 +645,7 @@
mAppInfoView = (ImageView) mAppOverlayView.findViewById(R.id.app_info);
mAppInfoView.setOnClickListener(this);
mAppTitleView = (TextView) mAppOverlayView.findViewById(R.id.app_title);
+ updateLayoutParams(mAppIconView, mAppTitleView, null, mAppInfoView);
}
// Update the overlay contents for the current app
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
index e46708e..e5ac0d3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -16,7 +16,9 @@
package com.android.systemui.recents.views;
+import android.app.ActivityManager;
import android.content.Context;
+import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
@@ -34,6 +36,8 @@
import android.view.ViewDebug;
import com.android.systemui.R;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.model.Task;
@@ -43,17 +47,24 @@
*/
public class TaskViewThumbnail extends View {
-
private static final ColorMatrix TMP_FILTER_COLOR_MATRIX = new ColorMatrix();
private static final ColorMatrix TMP_BRIGHTNESS_COLOR_MATRIX = new ColorMatrix();
private Task mTask;
+ private Rect mDisplayRect = new Rect();
+ private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
+
// Drawing
@ViewDebug.ExportedProperty(category="recents")
+ Rect mTaskViewRect = new Rect();
+ @ViewDebug.ExportedProperty(category="recents")
Rect mThumbnailRect = new Rect();
@ViewDebug.ExportedProperty(category="recents")
- Rect mTaskViewRect = new Rect();
+ float mThumbnailScale;
+ float mFullscreenThumbnailScale;
+ ActivityManager.TaskThumbnailInfo mThumbnailInfo;
+
int mCornerRadius;
@ViewDebug.ExportedProperty(category="recents")
float mDimAlpha;
@@ -97,6 +108,8 @@
mCornerRadius = getResources().getDimensionPixelSize(
R.dimen.recents_task_view_rounded_corners_radius);
mBgFillPaint.setColor(Color.WHITE);
+ mFullscreenThumbnailScale = context.getResources().getFraction(
+ com.android.internal.R.fraction.thumbnail_fullscreen_scale, 1, 1);
}
/**
@@ -114,57 +127,78 @@
}
@Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ mOrientation = ssp.getDisplayOrientation();
+ mDisplayRect = ssp.getDisplayRect();
+ }
+
+ @Override
protected void onDraw(Canvas canvas) {
if (mInvisible) {
return;
}
- int thumbnailHeight = (int) (((float) mTaskViewRect.width() / mThumbnailRect.width()) *
- mThumbnailRect.height());
- if (thumbnailHeight >= mTaskViewRect.height()) {
- // The thumbnail fills the full task view bounds, so just draw it
- canvas.drawRoundRect(0, 0, mTaskViewRect.width(), mTaskViewRect.height(),
- mCornerRadius, mCornerRadius, mDrawPaint);
- } else {
- int count = 0;
- if (thumbnailHeight > 0) {
- // The thumbnail only covers part of the task view bounds, so fill in the
- // non-thumbnail space with the default background color. This is the equivalent of
- // the GL border texture mode.
- count = canvas.save();
+ int viewWidth = mTaskViewRect.width();
+ int viewHeight = mTaskViewRect.height();
+ if (mBitmapShader != null) {
+
+ // We are drawing the thumbnail in the same orientation, so just fit the width
+ int thumbnailWidth = (int) (mThumbnailRect.width() * mThumbnailScale);
+ int thumbnailHeight = (int) (mThumbnailRect.height() * mThumbnailScale);
+
+ if (thumbnailWidth >= viewWidth && thumbnailHeight >= viewHeight) {
+ // Thumbnail fills the full task view bounds, so just draw it
+ canvas.drawRoundRect(0, 0, viewWidth, viewHeight, mCornerRadius, mCornerRadius,
+ mDrawPaint);
+ } else {
+ // Thumbnail does not fill the full task view bounds, so just draw it and fill the
+ // empty areas with the background color
+ int count = canvas.save();
// Since we only want the top corners to be rounded, draw slightly beyond the
// thumbnail height, but clip to the thumbnail height
- canvas.clipRect(0, 0, mTaskViewRect.width(), thumbnailHeight, Region.Op.REPLACE);
- canvas.drawRoundRect(0, 0, mTaskViewRect.width(), thumbnailHeight + mCornerRadius,
+ canvas.clipRect(0, 0, thumbnailWidth, thumbnailHeight, Region.Op.REPLACE);
+ canvas.drawRoundRect(0, 0,
+ thumbnailWidth + (thumbnailWidth < viewWidth ? mCornerRadius : 0),
+ thumbnailHeight + (thumbnailHeight < viewHeight ? mCornerRadius : 0),
mCornerRadius, mCornerRadius, mDrawPaint);
- }
- // In the remaining space, draw the background color
- canvas.clipRect(0, thumbnailHeight, mTaskViewRect.width(), mTaskViewRect.height(),
- Region.Op.REPLACE);
- canvas.drawRoundRect(0, Math.max(0, thumbnailHeight - mCornerRadius),
- mTaskViewRect.width(), mTaskViewRect.height(), mCornerRadius, mCornerRadius,
- mBgFillPaint);
+ // In the remaining space, draw the background color
+ if (thumbnailWidth < viewWidth) {
+ canvas.clipRect(thumbnailWidth, 0, viewWidth, viewHeight, Region.Op.REPLACE);
+ canvas.drawRoundRect(Math.max(0, thumbnailWidth - mCornerRadius), 0,
+ viewWidth, viewHeight, mCornerRadius, mCornerRadius, mBgFillPaint);
+ }
+ if (thumbnailWidth > 0 && thumbnailHeight < viewHeight) {
+ canvas.clipRect(0, thumbnailHeight, viewWidth, viewHeight, Region.Op.REPLACE);
+ canvas.drawRoundRect(0, Math.max(0, thumbnailHeight - mCornerRadius),
+ viewWidth, viewHeight, mCornerRadius, mCornerRadius, mBgFillPaint);
+ }
- if (thumbnailHeight > 0) {
canvas.restoreToCount(count);
}
+ } else {
+ canvas.drawRoundRect(0, 0, viewWidth, viewHeight, mCornerRadius, mCornerRadius,
+ mBgFillPaint);
}
}
/** Sets the thumbnail to a given bitmap. */
- void setThumbnail(Bitmap bm) {
+ void setThumbnail(Bitmap bm, ActivityManager.TaskThumbnailInfo thumbnailInfo) {
if (bm != null) {
- mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP,
- Shader.TileMode.CLAMP);
+ mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mDrawPaint.setShader(mBitmapShader);
mThumbnailRect.set(0, 0, bm.getWidth(), bm.getHeight());
+ mThumbnailInfo = thumbnailInfo;
updateThumbnailScale();
} else {
mBitmapShader = null;
mDrawPaint.setShader(null);
mThumbnailRect.setEmpty();
+ mThumbnailInfo = null;
}
}
@@ -210,20 +244,41 @@
* Updates the scale of the bitmap relative to this view.
*/
public void updateThumbnailScale() {
+ mThumbnailScale = 1f;
if (mBitmapShader != null) {
- float thumbnailScale;
- if (!mTask.isFreeformTask() || mTask.bounds == null) {
- // If this is a stack task, or a stack task moved into the freeform workspace, then
- // just scale this thumbnail to fit the width of the view
- thumbnailScale = (float) mTaskViewRect.width() / mThumbnailRect.width();
+ // We consider this a stack task if it is not freeform (ie. has no bounds) or has been
+ // dragged into the stack from the freeform workspace
+ boolean isStackTask = !mTask.isFreeformTask() || mTask.bounds == null;
+ if (mTaskViewRect.isEmpty() || mThumbnailInfo == null ||
+ mThumbnailInfo.taskWidth == 0 || mThumbnailInfo.taskHeight == 0) {
+ // If we haven't measured or the thumbnail is invalid, skip the thumbnail drawing
+ // and only draw the background color
+ mThumbnailScale = 0f;
+ } else if (isStackTask) {
+ float invThumbnailScale = 1f / mFullscreenThumbnailScale;
+ if (mOrientation == Configuration.ORIENTATION_PORTRAIT) {
+ if (mThumbnailInfo.screenOrientation == Configuration.ORIENTATION_PORTRAIT) {
+ // If we are in the same orientation as the screenshot, just scale it to the
+ // width of the task view
+ mThumbnailScale = (float) mTaskViewRect.width() / mThumbnailRect.width();
+ } else {
+ // Scale the landscape thumbnail up to app size, then scale that to the task
+ // view size to match other portrait screenshots
+ mThumbnailScale = invThumbnailScale *
+ ((float) mTaskViewRect.width() / mDisplayRect.width());
+ }
+ } else {
+ // Otherwise, scale the screenshot to fit 1:1 in the current orientation
+ mThumbnailScale = invThumbnailScale;
+ }
} else {
// Otherwise, if this is a freeform task with task bounds, then scale the thumbnail
// to fit the entire bitmap into the task bounds
- thumbnailScale = Math.min(
+ mThumbnailScale = Math.min(
(float) mTaskViewRect.width() / mThumbnailRect.width(),
(float) mTaskViewRect.height() / mThumbnailRect.height());
}
- mScaleMatrix.setScale(thumbnailScale, thumbnailScale);
+ mScaleMatrix.setScale(mThumbnailScale, mThumbnailScale);
mBitmapShader.setLocalMatrix(mScaleMatrix);
}
if (!mInvisible) {
@@ -261,22 +316,23 @@
}
/** Binds the thumbnail view to the task */
- void rebindToTask(Task t, boolean disabledInSafeMode) {
+ void rebindToTask(Task t, ActivityManager.TaskThumbnailInfo thumbnailInfo,
+ boolean disabledInSafeMode) {
mTask = t;
mDisabledInSafeMode = disabledInSafeMode;
if (t.thumbnail != null) {
- setThumbnail(t.thumbnail);
- if (t.colorBackground != 0) {
- mBgFillPaint.setColor(t.colorBackground);
- }
+ setThumbnail(t.thumbnail, thumbnailInfo);
} else {
- setThumbnail(null);
+ setThumbnail(null, null);
+ }
+ if (t.colorBackground != 0) {
+ mBgFillPaint.setColor(t.colorBackground);
}
}
/** Unbinds the thumbnail view from the task */
void unbindFromTask() {
mTask = null;
- setThumbnail(null);
+ setThumbnail(null, null);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
index dc76e61..b512393 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
@@ -226,4 +226,9 @@
v.getViewBounds().setClipBottom(0);
v.setLeftTopRightBottom(0, 0, 0, 0);
}
+
+ @Override
+ public String toString() {
+ return "R: " + rect + " V: " + visible;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index dd59fac..d294c80 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -34,24 +34,22 @@
* Controls the docked stack divider.
*/
public class Divider extends SystemUI {
- private static final String TAG = "Divider";
- private int mDividerWindowWidth;
private DividerWindowManager mWindowManager;
private DividerView mView;
private DockDividerVisibilityListener mDockDividerVisibilityListener;
private boolean mVisible = false;
private boolean mMinimized = false;
+ private ForcedResizableInfoActivityController mForcedResizableController;
@Override
public void start() {
mWindowManager = new DividerWindowManager(mContext);
- mDividerWindowWidth = mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.docked_stack_divider_thickness);
update(mContext.getResources().getConfiguration());
putComponent(Divider.class, this);
mDockDividerVisibilityListener = new DockDividerVisibilityListener();
SystemServicesProxy ssp = Recents.getSystemServices();
ssp.registerDockedStackListener(mDockDividerVisibilityListener);
+ mForcedResizableController = new ForcedResizableInfoActivityController(mContext);
}
@Override
@@ -68,9 +66,11 @@
mView = (DividerView)
LayoutInflater.from(mContext).inflate(R.layout.docked_stack_divider, null);
mView.setVisibility(mVisible ? View.VISIBLE : View.INVISIBLE);
+ final int size = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.docked_stack_divider_thickness);
final boolean landscape = configuration.orientation == ORIENTATION_LANDSCAPE;
- final int width = landscape ? mDividerWindowWidth : MATCH_PARENT;
- final int height = landscape ? MATCH_PARENT : mDividerWindowWidth;
+ final int width = landscape ? size : MATCH_PARENT;
+ final int height = landscape ? MATCH_PARENT : size;
mWindowManager.add(mView, width, height);
mView.setWindowManager(mWindowManager);
}
@@ -95,6 +95,9 @@
if (mVisible != visible) {
mVisible = visible;
mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
+
+ // Update state because animations won't finish.
+ mView.setMinimizedDockStack(mMinimized);
}
}
});
@@ -117,6 +120,15 @@
});
}
+ private void notifyDockedStackExistsChanged(final boolean exists) {
+ mView.post(new Runnable() {
+ @Override
+ public void run() {
+ mForcedResizableController.notifyDockedStackExistsChanged(exists);
+ }
+ });
+ }
+
class DockDividerVisibilityListener extends IDockedStackListener.Stub {
@Override
@@ -126,6 +138,7 @@
@Override
public void onDockedStackExistsChanged(boolean exists) throws RemoteException {
+ notifyDockedStackExistsChanged(exists);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 132c09f..3005535 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -66,6 +66,8 @@
import com.android.systemui.recents.events.activity.UndockingTaskEvent;
import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.stackdivider.events.StartedDragingEvent;
+import com.android.systemui.stackdivider.events.StoppedDragingEvent;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.phone.NavigationBarGestureHelper;
@@ -81,6 +83,7 @@
private static final String TAG = "DividerView";
private static final int TASK_POSITION_SAME = Integer.MAX_VALUE;
+ private static final boolean SWAPPING_ENABLED = false;
/**
* How much the background gets scaled when we are in the minimized dock state.
@@ -131,6 +134,7 @@
private ValueAnimator mCurrentAnimator;
private boolean mEntranceAnimationRunning;
private GestureDetector mGestureDetector;
+ private boolean mDockedStackMinimized;
private final AccessibilityDelegate mHandleDelegate = new AccessibilityDelegate() {
@Override
@@ -168,6 +172,13 @@
}
};
+ private final Runnable mResetBackgroundRunnable = new Runnable() {
+ @Override
+ public void run() {
+ resetBackground();
+ }
+ };
+
public DividerView(Context context) {
super(context);
}
@@ -211,12 +222,14 @@
mGestureDetector = new GestureDetector(mContext, new SimpleOnGestureListener() {
@Override
public boolean onSingleTapUp(MotionEvent e) {
- updateDockSide();
- SystemServicesProxy ssp = Recents.getSystemServices();
- if (mDockSide != WindowManager.DOCKED_INVALID
- && !ssp.isRecentsTopMost(ssp.getTopMostTask(), null /* isTopHome */)) {
- mWindowManagerProxy.swapTasks();
- return true;
+ if (SWAPPING_ENABLED) {
+ updateDockSide();
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ if (mDockSide != WindowManager.DOCKED_INVALID
+ && !ssp.isRecentsTopMost(ssp.getTopMostTask(), null /* isTopHome */)) {
+ mWindowManagerProxy.swapTasks();
+ return true;
+ }
}
return false;
}
@@ -227,16 +240,6 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
EventBus.getDefault().register(this);
- getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
-
- @Override
- public void onGlobalLayout() {
- getViewTreeObserver().removeOnGlobalLayoutListener(this);
- mWindowManagerProxy.setTouchRegion(new Rect(mHandle.getLeft(), mHandle.getTop(),
- mHandle.getLeft() + mHandle.getWidth(),
- mHandle.getTop() + mHandle.getHeight()));
- }
- });
}
@Override
@@ -261,6 +264,15 @@
return super.onApplyWindowInsets(insets);
}
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ if (changed) {
+ mWindowManagerProxy.setTouchRegion(new Rect(mHandle.getLeft(), mHandle.getTop(),
+ mHandle.getRight(), mHandle.getBottom()));
+ }
+ }
+
public void setWindowManager(DividerWindowManager windowManager) {
mWindowManager = windowManager;
}
@@ -281,6 +293,7 @@
mWindowManager.setSlippery(false);
liftBackground();
}
+ EventBus.getDefault().send(new StartedDragingEvent());
return mDockSide != WindowManager.DOCKED_INVALID;
}
@@ -380,13 +393,6 @@
x = (int) event.getRawX();
y = (int) event.getRawY();
- if (mMoving && mDockSide != WindowManager.DOCKED_INVALID) {
- int position = calculatePosition(x, y);
- SnapTarget snapTarget = mSnapAlgorithm.calculateSnapTarget(position,
- 0 /* velocity */, false /* hardDismiss */);
- resizeStack(calculatePosition(x, y), snapTarget.position, snapTarget);
- }
-
mVelocityTracker.computeCurrentVelocity(1000);
int position = calculatePosition(x, y);
stopDragging(position, isHorizontalDivision() ? mVelocityTracker.getYVelocity()
@@ -439,6 +445,7 @@
mDockSide = WindowManager.DOCKED_INVALID;
mCurrentAnimator = null;
mEntranceAnimationRunning = false;
+ EventBus.getDefault().send(new StoppedDragingEvent());
}
});
mCurrentAnimator = anim;
@@ -519,16 +526,19 @@
public void setMinimizedDockStack(boolean minimized) {
updateDockSide();
mHandle.setAlpha(minimized ? 0f : 1f);
- if (mDockSide == WindowManager.DOCKED_TOP) {
+ if (!minimized) {
+ resetBackground();
+ } else if (mDockSide == WindowManager.DOCKED_TOP) {
mBackground.setPivotY(0);
- mBackground.setScaleY(minimized ? MINIMIZE_DOCK_SCALE : 1f);
+ mBackground.setScaleY(MINIMIZE_DOCK_SCALE);
} else if (mDockSide == WindowManager.DOCKED_LEFT
|| mDockSide == WindowManager.DOCKED_RIGHT) {
mBackground.setPivotX(mDockSide == WindowManager.DOCKED_LEFT
? 0
: mBackground.getWidth());
- mBackground.setScaleX(minimized ? MINIMIZE_DOCK_SCALE : 1f);
+ mBackground.setScaleX(MINIMIZE_DOCK_SCALE);
}
+ mDockedStackMinimized = minimized;
}
public void setMinimizedDockStack(boolean minimized, long animDuration) {
@@ -550,10 +560,21 @@
mBackground.animate()
.scaleX(minimized ? MINIMIZE_DOCK_SCALE : 1f);
}
+ if (!minimized) {
+ mBackground.animate().withEndAction(mResetBackgroundRunnable);
+ }
mBackground.animate()
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.setDuration(animDuration)
.start();
+ mDockedStackMinimized = minimized;
+ }
+
+ private void resetBackground() {
+ mBackground.setPivotX(mBackground.getWidth() / 2);
+ mBackground.setPivotY(mBackground.getHeight() / 2);
+ mBackground.setScaleX(1f);
+ mBackground.setScaleY(1f);
}
@Override
@@ -730,21 +751,22 @@
mSnapAlgorithm.calculateDismissingFraction(position)));
SnapTarget dismissTarget = null;
SnapTarget splitTarget = null;
- if ((snapTarget.flag == SnapTarget.FLAG_DISMISS_START
- || snapTarget == mSnapAlgorithm.getFirstSplitTarget())
+ int start = 0;
+ if (position <= mSnapAlgorithm.getLastSplitTarget().position
&& dockSideTopLeft(dockSide)) {
dismissTarget = mSnapAlgorithm.getDismissStartTarget();
splitTarget = mSnapAlgorithm.getFirstSplitTarget();
- } else if ((snapTarget.flag == SnapTarget.FLAG_DISMISS_END
- || snapTarget == mSnapAlgorithm.getLastSplitTarget())
+ start = taskPosition;
+ } else if (position >= mSnapAlgorithm.getLastSplitTarget().position
&& dockSideBottomRight(dockSide)) {
dismissTarget = mSnapAlgorithm.getDismissEndTarget();
splitTarget = mSnapAlgorithm.getLastSplitTarget();
+ start = splitTarget.position;
}
if (dismissTarget != null && fraction > 0f
&& isDismissing(splitTarget, position, dockSide)) {
fraction = calculateParallaxDismissingFraction(fraction, dockSide);
- int offsetPosition = (int) (taskPosition +
+ int offsetPosition = (int) (start +
fraction * (dismissTarget.position - splitTarget.position));
int width = taskRect.width();
int height = taskRect.height();
@@ -792,11 +814,12 @@
}
private int getStackIdForDismissTarget(SnapTarget dismissTarget) {
- if (dismissTarget.flag == SnapTarget.FLAG_DISMISS_START &&
- (mDockSide == WindowManager.DOCKED_LEFT || mDockSide == WindowManager.DOCKED_TOP)) {
+ if ((dismissTarget.flag == SnapTarget.FLAG_DISMISS_START && dockSideTopLeft(mDockSide))
+ || (dismissTarget.flag == SnapTarget.FLAG_DISMISS_END
+ && dockSideBottomRight(mDockSide))) {
return StackId.DOCKED_STACK_ID;
} else {
- return StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+ return StackId.HOME_STACK_ID;
}
}
@@ -862,7 +885,7 @@
public final void onBusEvent(UndockingTaskEvent undockingTaskEvent) {
int dockSide = mWindowManagerProxy.getDockSide();
- if (dockSide != WindowManager.DOCKED_INVALID) {
+ if (dockSide != WindowManager.DOCKED_INVALID && !mDockedStackMinimized) {
startDragging(false /* animate */, false /* touching */);
SnapTarget target = dockSideTopLeft(dockSide)
? mSnapAlgorithm.getDismissEndTarget()
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivity.java b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivity.java
new file mode 100644
index 0000000..f728dab
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivity.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.stackdivider;
+
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnTouchListener;
+
+import com.android.systemui.R;
+
+/**
+ * Translucent activity that gets started on top of a task in multi-window to inform the user that
+ * we forced the activity below to be resizable.
+ */
+public class ForcedResizableInfoActivity extends Activity implements OnTouchListener {
+
+ private static final long DISMISS_DELAY = 2500;
+
+ private final Runnable mFinishRunnable = new Runnable() {
+ @Override
+ public void run() {
+ finish();
+ }
+ };
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.forced_resizable_activity);
+ getWindow().getDecorView().setOnTouchListener(this);
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ getWindow().getDecorView().postDelayed(mFinishRunnable, DISMISS_DELAY);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ finish();
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ finish();
+ return true;
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ finish();
+ return true;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
new file mode 100644
index 0000000..9b56037
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.stackdivider;
+
+import android.app.ActivityOptions;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.AppTransitionFinishedEvent;
+import com.android.systemui.recents.events.activity.ForcedResizableEvent;
+import com.android.systemui.stackdivider.events.StartedDragingEvent;
+import com.android.systemui.stackdivider.events.StoppedDragingEvent;
+
+/**
+ * Controller that decides when to show the {@link ForcedResizableInfoActivity}.
+ */
+public class ForcedResizableInfoActivityController {
+
+ private static final String SELF_PACKAGE_NAME = "com.android.systemui";
+
+ private static final int TIMEOUT = 1000;
+ private final Context mContext;
+ private final Handler mHandler = new Handler();
+ private final ArraySet<Integer> mPendingTaskIds = new ArraySet<>();
+ private final ArraySet<String> mPackagesShownInSession = new ArraySet<>();
+ private boolean mDividerDraging;
+
+ private final Runnable mTimeoutRunnable = new Runnable() {
+ @Override
+ public void run() {
+ showPending();
+ }
+ };
+
+ public ForcedResizableInfoActivityController(Context context) {
+ mContext = context;
+ EventBus.getDefault().register(this);
+ }
+
+ public void notifyDockedStackExistsChanged(boolean exists) {
+ if (!exists) {
+ mPackagesShownInSession.clear();
+ }
+ }
+
+ public final void onBusEvent(ForcedResizableEvent forcedResizableEvent) {
+ if (debounce(forcedResizableEvent.packageName)) {
+ return;
+ }
+ mPendingTaskIds.add(forcedResizableEvent.taskId);
+ postTimeout();
+ }
+
+ public final void onBusEvent(AppTransitionFinishedEvent event) {
+ if (!mDividerDraging) {
+ showPending();
+ }
+ }
+
+ public final void onBusEvent(StartedDragingEvent event) {
+ mDividerDraging = true;
+ mHandler.removeCallbacks(mTimeoutRunnable);
+ }
+
+ public final void onBusEvent(StoppedDragingEvent event) {
+ mDividerDraging = false;
+ showPending();
+ }
+
+ private void showPending() {
+ mHandler.removeCallbacks(mTimeoutRunnable);
+ for (int i = mPendingTaskIds.size() - 1; i >= 0; i--) {
+ Intent intent = new Intent(mContext, ForcedResizableInfoActivity.class);
+ ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchTaskId(mPendingTaskIds.valueAt(i));
+ mContext.startActivity(intent, options.toBundle());
+ }
+ mPendingTaskIds.clear();
+ }
+
+ private void postTimeout() {
+ mHandler.removeCallbacks(mTimeoutRunnable);
+ mHandler.postDelayed(mTimeoutRunnable, TIMEOUT);
+ }
+
+ private boolean debounce(String packageName) {
+ if (packageName == null) {
+ return false;
+ }
+
+ // We launch ForcedResizableInfoActivity into a task that was forced resizable, so that
+ // triggers another notification. So ignore our own activity.
+ if (SELF_PACKAGE_NAME.equals(packageName)) {
+ return true;
+ }
+ boolean debounce = mPackagesShownInSession.contains(packageName);
+ mPackagesShownInSession.add(packageName);
+ return debounce;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/ResetBackgroundScrimEvent.java b/packages/SystemUI/src/com/android/systemui/stackdivider/events/StartedDragingEvent.java
similarity index 72%
copy from packages/SystemUI/src/com/android/systemui/recents/events/ui/ResetBackgroundScrimEvent.java
copy to packages/SystemUI/src/com/android/systemui/stackdivider/events/StartedDragingEvent.java
index 863f40b..5d19851 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/ResetBackgroundScrimEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/events/StartedDragingEvent.java
@@ -11,16 +11,15 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
*/
-package com.android.systemui.recents.events.ui;
+package com.android.systemui.stackdivider.events;
import com.android.systemui.recents.events.EventBus;
/**
- * This is sent to reset the background scrim back to the initial state.
+ * Sent when the divider is being draged either manually or by an animation.
*/
-public class ResetBackgroundScrimEvent extends EventBus.Event {
- // Simple event
+public class StartedDragingEvent extends EventBus.Event {
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/ResetBackgroundScrimEvent.java b/packages/SystemUI/src/com/android/systemui/stackdivider/events/StoppedDragingEvent.java
similarity index 72%
copy from packages/SystemUI/src/com/android/systemui/recents/events/ui/ResetBackgroundScrimEvent.java
copy to packages/SystemUI/src/com/android/systemui/stackdivider/events/StoppedDragingEvent.java
index 863f40b..c50d6d6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/ResetBackgroundScrimEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/events/StoppedDragingEvent.java
@@ -11,16 +11,15 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
*/
-package com.android.systemui.recents.events.ui;
+package com.android.systemui.stackdivider.events;
import com.android.systemui.recents.events.EventBus;
/**
- * This is sent to reset the background scrim back to the initial state.
+ * Sent when the divider isn't draging anymore.
*/
-public class ResetBackgroundScrimEvent extends EventBus.Event {
- // Simple event
+public class StoppedDragingEvent extends EventBus.Event {
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 2acaca1..1b2393a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -19,7 +19,9 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.app.ActivityManager;
+import android.app.ActivityManager.StackId;
import android.app.ActivityManagerNative;
+import android.app.ActivityOptions;
import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationManager;
@@ -89,6 +91,7 @@
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardHostView.OnDismissAction;
import com.android.systemui.DejankUtils;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -98,6 +101,7 @@
import com.android.systemui.assist.AssistManager;
import com.android.systemui.recents.Recents;
import com.android.systemui.statusbar.NotificationData.Entry;
+import com.android.systemui.statusbar.NotificationGuts.OnGutsClosedListener;
import com.android.systemui.statusbar.phone.NavigationBarView;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -105,7 +109,6 @@
import com.android.systemui.statusbar.policy.PreviewInflater;
import com.android.systemui.statusbar.policy.RemoteInputView;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout.GearDisplayedListener;
import com.android.systemui.statusbar.stack.StackStateAnimator;
import java.util.ArrayList;
@@ -113,12 +116,12 @@
import java.util.Locale;
import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH;
-import static com.android.keyguard.KeyguardHostView.OnDismissAction;
public abstract class BaseStatusBar extends SystemUI implements
CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener,
ExpandableNotificationRow.ExpansionLogger, NotificationData.Environment,
- ExpandableNotificationRow.OnExpandClickListener, GearDisplayedListener {
+ ExpandableNotificationRow.OnExpandClickListener,
+ OnGutsClosedListener {
public static final String TAG = "StatusBar";
public static final boolean DEBUG = false;
public static final boolean MULTIUSER_DEBUG = false;
@@ -127,6 +130,8 @@
SystemProperties.getBoolean("debug.enable_remote_input", true);
public static final boolean ENABLE_CHILD_NOTIFICATIONS
= SystemProperties.getBoolean("debug.child_notifs", true);
+ public static final boolean FORCE_REMOTE_INPUT_HISTORY =
+ SystemProperties.getBoolean("debug.force_remoteinput_history", false);
protected static final int MSG_SHOW_RECENT_APPS = 1019;
protected static final int MSG_HIDE_RECENT_APPS = 1020;
@@ -182,6 +187,13 @@
protected boolean mVisible;
protected ArraySet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new ArraySet<>();
+ /**
+ * Notifications with keys in this set are not actually around anymore. We kept them around
+ * when they were canceled in response to a remote input interaction. This allows us to show
+ * what you replied and allows you to continue typing into it.
+ */
+ protected ArraySet<String> mKeysKeptForRemoteInput = new ArraySet<>();
+
// mScreenOnFromKeyguard && mVisible.
private boolean mVisibleToUser;
@@ -231,7 +243,6 @@
// which notification is currently being longpress-examined by the user
private NotificationGuts mNotificationGutsExposed;
- private ExpandableNotificationRow mNotificationGearDisplayed;
private KeyboardShortcuts mKeyboardShortcuts;
@@ -339,7 +350,7 @@
}, afterKeyguardGone);
return true;
} else {
- return super.onClickHandler(view, pendingIntent, fillInIntent);
+ return superOnClickHandler(view, pendingIntent, fillInIntent);
}
}
@@ -376,7 +387,8 @@
private boolean superOnClickHandler(View view, PendingIntent pendingIntent,
Intent fillInIntent) {
- return super.onClickHandler(view, pendingIntent, fillInIntent);
+ return super.onClickHandler(view, pendingIntent, fillInIntent,
+ StackId.FULLSCREEN_WORKSPACE_STACK_ID);
}
private boolean handleRemoteInput(View view, PendingIntent pendingIntent, Intent fillInIntent) {
@@ -567,6 +579,7 @@
public void run() {
processForRemoteInput(sbn.getNotification());
String key = sbn.getKey();
+ mKeysKeptForRemoteInput.remove(key);
boolean isUpdate = mNotificationData.get(key) != null;
// In case we don't allow child notifications, we ignore children of
// notifications that have a summary, since we're not going to show them
@@ -905,7 +918,7 @@
}
}
- protected View bindVetoButtonClickListener(View row, StatusBarNotification n) {
+ protected View bindVetoButtonClickListener(View row, final StatusBarNotification n) {
View vetoButton = row.findViewById(R.id.veto);
final String _pkg = n.getPackageName();
final String _tag = n.getTag();
@@ -918,6 +931,11 @@
mContext.getString(R.string.accessibility_notification_dismissed));
try {
mBarService.onNotificationClear(_pkg, _tag, _id, _userId);
+ if (FORCE_REMOTE_INPUT_HISTORY
+ && mKeysKeptForRemoteInput.contains(n.getKey())) {
+ removeNotification(n.getKey(), null);
+ mKeysKeptForRemoteInput.remove(n.getKey());
+ }
} catch (RemoteException ex) {
// system process is dead if we're here.
@@ -976,7 +994,7 @@
}
TaskStackBuilder.create(mContext)
.addNextIntentWithParentStack(intent)
- .startActivities(null,
+ .startActivities(getActivityOptions(),
new UserHandle(UserHandle.getUserId(appUid)));
overrideActivityPendingAppTransition(keyguardShowing);
} catch (RemoteException e) {
@@ -995,6 +1013,7 @@
PackageManager pmUser = getPackageManagerForUser(mContext, sbn.getUser().getIdentifier());
row.setTag(sbn.getPackageName());
final NotificationGuts guts = row.getGuts();
+ guts.setClosedListener(this);
final String pkg = sbn.getPackageName();
String appname = pkg;
Drawable pkgicon = null;
@@ -1022,6 +1041,7 @@
settingsButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTE_INFO);
+ guts.resetFalsingCheck();
startAppNotificationSettingsActivity(pkg, appUidF);
}
});
@@ -1032,26 +1052,41 @@
row.findViewById(R.id.done).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- guts.saveImportance(sbn);
-
- int[] rowLocation = new int[2];
- int[] doneLocation = new int[2];
- row.getLocationOnScreen(rowLocation);
- v.getLocationOnScreen(doneLocation);
-
- final int centerX = v.getWidth() / 2;
- final int centerY = v.getHeight() / 2;
- final int x = doneLocation[0] - rowLocation[0] + centerX;
- final int y = doneLocation[1] - rowLocation[1] + centerY;
- dismissPopups(x, y);
+ // If the user has security enabled, show challenge if the setting is changed.
+ if (guts.hasImportanceChanged() && isLockscreenPublicMode() &&
+ (mState == StatusBarState.KEYGUARD
+ || mState == StatusBarState.SHADE_LOCKED)) {
+ OnDismissAction dismissAction = new OnDismissAction() {
+ @Override
+ public boolean onDismiss() {
+ saveImportanceCloseControls(sbn, row, guts, v);
+ return true;
+ }
+ };
+ onLockedNotificationImportanceChange(dismissAction);
+ } else {
+ saveImportanceCloseControls(sbn, row, guts, v);
+ }
}
});
-
guts.bindImportance(pmUser, sbn, row, mNotificationData.getImportance(sbn.getKey()));
}
- protected GearDisplayedListener getGearDisplayedListener() {
- return this;
+ private void saveImportanceCloseControls(StatusBarNotification sbn,
+ ExpandableNotificationRow row, NotificationGuts guts, View done) {
+ guts.resetFalsingCheck();
+ guts.saveImportance(sbn);
+
+ int[] rowLocation = new int[2];
+ int[] doneLocation = new int[2];
+ row.getLocationOnScreen(rowLocation);
+ done.getLocationOnScreen(doneLocation);
+
+ final int centerX = done.getWidth() / 2;
+ final int centerY = done.getHeight() / 2;
+ final int x = doneLocation[0] - rowLocation[0] + centerX;
+ final int y = doneLocation[1] - rowLocation[1] + centerY;
+ dismissPopups(x, y);
}
protected SwipeHelper.LongPressListener getNotificationLongClicker() {
@@ -1089,7 +1124,8 @@
// Post to ensure the the guts are properly laid out.
guts.post(new Runnable() {
public void run() {
- dismissPopups(-1 /* x */, -1 /* y */, false /* resetGear */);
+ dismissPopups(-1 /* x */, -1 /* y */, false /* resetGear */,
+ false /* animate */);
guts.setVisibility(View.VISIBLE);
final double horz = Math.max(guts.getWidth() - x, x);
final double vert = Math.max(guts.getHeight() - y, y);
@@ -1107,7 +1143,8 @@
}
});
a.start();
- guts.setExposed(true);
+ guts.setExposed(true /* exposed */,
+ mState == StatusBarState.KEYGUARD /* needsFalsingProtection */);
row.closeRemoteInput();
mStackScroller.onHeightChanged(null, true /* needsAnimation */);
mNotificationGutsExposed = guts;
@@ -1118,53 +1155,34 @@
};
}
- @Override
- public void onGearDisplayed(ExpandableNotificationRow row) {
- MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR,
- row.getStatusBarNotification().getPackageName());
- mNotificationGearDisplayed = row;
+ /**
+ * Returns the exposed NotificationGuts or null if none are exposed.
+ */
+ public NotificationGuts getExposedGuts() {
+ return mNotificationGutsExposed;
}
public void dismissPopups() {
- dismissPopups(-1 /* x */, -1 /* y */, true /* resetGear */);
+ dismissPopups(-1 /* x */, -1 /* y */, true /* resetGear */, false /* animate */);
}
private void dismissPopups(int x, int y) {
- dismissPopups(x, y, true /* resetGear */);
+ dismissPopups(x, y, true /* resetGear */, false /* animate */);
}
- public void dismissPopups(int x, int y, boolean resetGear) {
+ public void dismissPopups(int x, int y, boolean resetGear, boolean animate) {
if (mNotificationGutsExposed != null) {
- final NotificationGuts v = mNotificationGutsExposed;
- mNotificationGutsExposed = null;
+ mNotificationGutsExposed.closeControls(x, y, true /* notify */);
+ }
+ if (resetGear) {
+ mStackScroller.resetExposedGearView(animate, true /* force */);
+ }
+ }
- if (v.getWindowToken() == null) return;
- if (x == -1 || y == -1) {
- x = (v.getLeft() + v.getRight()) / 2;
- y = (v.getTop() + v.getHeight() / 2);
- }
- final double horz = Math.max(v.getWidth() - x, x);
- final double vert = Math.max(v.getHeight() - y, y);
- final float r = (float) Math.hypot(horz, vert);
- final Animator a = ViewAnimationUtils.createCircularReveal(v,
- x, y, r, 0);
- a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
- a.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
- a.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- v.setVisibility(View.GONE);
- }
- });
- a.start();
- v.setExposed(false);
- mStackScroller.onHeightChanged(null, true /* needsAnimation */);
- }
- if (resetGear && mNotificationGearDisplayed != null) {
- mNotificationGearDisplayed.resetTranslation();
- mNotificationGearDisplayed = null;
- }
+ @Override
+ public void onGutsClosed(NotificationGuts guts) {
+ mStackScroller.onHeightChanged(null, true /* needsAnimation */);
+ mNotificationGutsExposed = null;
}
@Override
@@ -1439,6 +1457,8 @@
}
}
+ protected void onLockedNotificationImportanceChange(OnDismissAction dismissAction) {}
+
protected void onLockedRemoteInput(ExpandableNotificationRow row, View clickedView) {}
@Override
@@ -1726,7 +1746,7 @@
} catch (RemoteException e) {
}
try {
- intent.send();
+ intent.send(null, 0, null, null, null, null, getActivityOptions());
} catch (PendingIntent.CanceledException e) {
// the stack trace isn't very helpful here.
// Just log the exception message.
@@ -1826,15 +1846,17 @@
.getIdentifier();
if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
&& mKeyguardManager.isDeviceLocked(userId)) {
- // Show work challenge, do not run pendingintent and
- // remove notification
- startWorkChallenge(userId, intent.getIntentSender(),
- notificationKey);
- return;
+ if (startWorkChallengeIfNecessary(userId,
+ intent.getIntentSender(), notificationKey)) {
+ // Show work challenge, do not run pendingintent and
+ // remove notification
+ return;
+ }
}
}
try {
- intent.send();
+ intent.send(null, 0, null, null, null, null,
+ getActivityOptions());
} catch (PendingIntent.CanceledException e) {
// the stack trace isn't very helpful here.
// Just log the exception message.
@@ -1867,21 +1889,25 @@
}, afterKeyguardGone);
}
- public void startWorkChallenge(int userId, IntentSender intendSender,
+ public boolean startWorkChallengeIfNecessary(int userId, IntentSender intendSender,
String notificationKey) {
+ final Intent newIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null,
+ null, userId);
+ if (newIntent == null) {
+ return false;
+ }
final Intent callBackIntent = new Intent(
WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION);
callBackIntent.putExtra(Intent.EXTRA_INTENT, intendSender);
callBackIntent.putExtra(Intent.EXTRA_INDEX, notificationKey);
callBackIntent.setPackage(mContext.getPackageName());
- final Intent newIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null,
- null, userId);
newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS | Intent.FLAG_ACTIVITY_CLEAR_TASK);
newIntent.putExtra(Intent.EXTRA_INTENT, PendingIntent
.getBroadcast(mContext, 0, callBackIntent, 0).getIntentSender());
mContext.startActivity(newIntent);
+ return true;
}
public void register(ExpandableNotificationRow row, StatusBarNotification sbn) {
@@ -1910,6 +1936,14 @@
}
}
+ protected Bundle getActivityOptions() {
+ // Anything launched from the notification shade should always go into the
+ // fullscreen stack.
+ ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchStackId(StackId.FULLSCREEN_WORKSPACE_STACK_ID);
+ return options.toBundle();
+ }
+
protected void visibilityChanged(boolean visible) {
if (mVisible != visible) {
mVisible = visible;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 0ffab5b..99b6397 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -73,6 +73,7 @@
private static final int MSG_REMOVE_QS_TILE = 28 << MSG_SHIFT;
private static final int MSG_CLICK_QS_TILE = 29 << MSG_SHIFT;
private static final int MSG_TOGGLE_APP_SPLIT_SCREEN = 30 << MSG_SHIFT;
+ private static final int MSG_APP_TRANSITION_FINISHED = 31 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -91,36 +92,37 @@
* These methods are called back on the main thread.
*/
public interface Callbacks {
- public void setIcon(String slot, StatusBarIcon icon);
- public void removeIcon(String slot);
- public void disable(int state1, int state2, boolean animate);
- public void animateExpandNotificationsPanel();
- public void animateCollapsePanels(int flags);
- public void animateExpandSettingsPanel(String obj);
- public void setSystemUiVisibility(int vis, int fullscreenStackVis,
+ void setIcon(String slot, StatusBarIcon icon);
+ void removeIcon(String slot);
+ void disable(int state1, int state2, boolean animate);
+ void animateExpandNotificationsPanel();
+ void animateCollapsePanels(int flags);
+ void animateExpandSettingsPanel(String obj);
+ void setSystemUiVisibility(int vis, int fullscreenStackVis,
int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds);
- public void topAppWindowChanged(boolean visible);
- public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
+ void topAppWindowChanged(boolean visible);
+ void setImeWindowStatus(IBinder token, int vis, int backDisposition,
boolean showImeSwitcher);
- public void showRecentApps(boolean triggeredFromAltTab);
- public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
- public void toggleRecentApps();
- public void toggleSplitScreen();
- public void preloadRecentApps();
- public void toggleKeyboardShortcutsMenu(int deviceId);
- public void cancelPreloadRecentApps();
- public void setWindowState(int window, int state);
- public void buzzBeepBlinked();
- public void notificationLightOff();
- public void notificationLightPulse(int argb, int onMillis, int offMillis);
- public void showScreenPinningRequest();
- public void appTransitionPending();
- public void appTransitionCancelled();
- public void appTransitionStarting(long startTime, long duration);
- public void showAssistDisclosure();
- public void startAssist(Bundle args);
- public void onCameraLaunchGestureDetected(int source);
- public void requestTvPictureInPicture();
+ void showRecentApps(boolean triggeredFromAltTab);
+ void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
+ void toggleRecentApps();
+ void toggleSplitScreen();
+ void preloadRecentApps();
+ void toggleKeyboardShortcutsMenu(int deviceId);
+ void cancelPreloadRecentApps();
+ void setWindowState(int window, int state);
+ void buzzBeepBlinked();
+ void notificationLightOff();
+ void notificationLightPulse(int argb, int onMillis, int offMillis);
+ void showScreenPinningRequest();
+ void appTransitionPending();
+ void appTransitionCancelled();
+ void appTransitionStarting(long startTime, long duration);
+ void appTransitionFinished();
+ void showAssistDisclosure();
+ void startAssist(Bundle args);
+ void onCameraLaunchGestureDetected(int source);
+ void requestTvPictureInPicture();
void addQsTile(ComponentName tile);
void remQsTile(ComponentName tile);
@@ -324,6 +326,14 @@
}
}
+ @Override
+ public void appTransitionFinished() {
+ synchronized (mLock) {
+ mHandler.removeMessages(MSG_APP_TRANSITION_FINISHED);
+ mHandler.sendEmptyMessage(MSG_APP_TRANSITION_FINISHED);
+ }
+ }
+
public void showAssistDisclosure() {
synchronized (mLock) {
mHandler.removeMessages(MSG_ASSIST_DISCLOSURE);
@@ -452,6 +462,9 @@
Pair<Long, Long> data = (Pair<Long, Long>) msg.obj;
mCallbacks.appTransitionStarting(data.first, data.second);
break;
+ case MSG_APP_TRANSITION_FINISHED:
+ mCallbacks.appTransitionFinished();
+ break;
case MSG_ASSIST_DISCLOSURE:
mCallbacks.showAssistDisclosure();
break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
index 7f87c3c..2dabf5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
@@ -170,22 +170,22 @@
: RUBBERBAND_FACTOR_STATIC;
float rubberband = heightDelta * rubberbandFactor;
if (expandable
- && (rubberband + child.getMinExpandHeight()) > child.getMaxContentHeight()) {
+ && (rubberband + child.getCollapsedHeight()) > child.getMaxContentHeight()) {
float overshoot =
- (rubberband + child.getMinExpandHeight()) - child.getMaxContentHeight();
+ (rubberband + child.getCollapsedHeight()) - child.getMaxContentHeight();
overshoot *= (1 - RUBBERBAND_FACTOR_STATIC);
rubberband -= overshoot;
}
- child.setActualHeight((int) (child.getMinExpandHeight() + rubberband));
+ child.setActualHeight((int) (child.getCollapsedHeight() + rubberband));
}
private void cancelExpansion(final ExpandableView child) {
- if (child.getActualHeight() == child.getMinExpandHeight()) {
+ if (child.getActualHeight() == child.getCollapsedHeight()) {
mCallback.setUserLockedChild(child, false);
return;
}
ObjectAnimator anim = ObjectAnimator.ofInt(child, "actualHeight",
- child.getActualHeight(), child.getMinExpandHeight());
+ child.getActualHeight(), child.getCollapsedHeight());
anim.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
anim.setDuration(SPRING_BACK_ANIMATION_LENGTH_MS);
anim.addListener(new AnimatorListenerAdapter() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index f9edeb3..7ca7d12 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -470,7 +470,7 @@
if(mExpandedWhenPinned) {
return Math.max(getMaxExpandHeight(), mHeadsUpHeight);
} else if (atLeastMinHeight) {
- return Math.max(getMinHeight(), mHeadsUpHeight);
+ return Math.max(getCollapsedHeight(), mHeadsUpHeight);
} else {
return mHeadsUpHeight;
}
@@ -1040,12 +1040,12 @@
} else if (isExpanded()) {
return Math.max(getMaxExpandHeight(), mHeadsUpHeight);
} else {
- return Math.max(getMinHeight(), mHeadsUpHeight);
+ return Math.max(getCollapsedHeight(), mHeadsUpHeight);
}
} else if (isExpanded()) {
return getMaxExpandHeight();
} else {
- return getMinHeight();
+ return getCollapsedHeight();
}
}
@@ -1301,9 +1301,9 @@
}
@Override
- public int getMinExpandHeight() {
+ public int getCollapsedHeight() {
if (mIsSummaryWithChildren && !mShowingPublic) {
- return mChildrenContainer.getMinExpandHeight();
+ return mChildrenContainer.getCollapsedHeight();
}
return getMinHeight();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index 91418ad..6dcd61f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -153,11 +153,11 @@
}
/**
- * @return The minimum height this child chan be expanded to. Note that this might be different
- * than {@link #getMinHeight()} because some elements can't be collapsed by an expand gesture
- * to it's absolute minimal height
+ * @return The collapsed height of this view. Note that this might be different
+ * than {@link #getMinHeight()} because some elements like groups may have different sizes when
+ * they are system expanded.
*/
- public int getMinExpandHeight() {
+ public int getCollapsedHeight() {
return getHeight();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutKeysLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutKeysLayout.java
index ba3e774..6746a67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutKeysLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutKeysLayout.java
@@ -29,13 +29,16 @@
*/
public final class KeyboardShortcutKeysLayout extends ViewGroup {
private int mLineHeight;
+ private final Context mContext;
public KeyboardShortcutKeysLayout(Context context) {
super(context);
+ this.mContext = context;
}
public KeyboardShortcutKeysLayout(Context context, AttributeSet attrs) {
super(context, attrs);
+ this.mContext = context;
}
@Override
@@ -104,7 +107,9 @@
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childCount = getChildCount();
int fullRowWidth = r - l;
- int xPos = getPaddingLeft();
+ int xPos = isRTL()
+ ? fullRowWidth - getPaddingRight()
+ : getPaddingLeft();
int yPos = getPaddingTop();
int lastHorizontalSpacing = 0;
// The index of the child which starts the current row.
@@ -117,18 +122,25 @@
int currentChildWidth = currentChild.getMeasuredWidth();
LayoutParams lp = (LayoutParams) currentChild.getLayoutParams();
- // If the current child does not fit on this row.
- if (xPos + currentChildWidth > fullRowWidth) {
+ boolean childDoesNotFitOnRow = isRTL()
+ ? xPos - getPaddingLeft() - currentChildWidth < 0
+ : xPos + currentChildWidth > fullRowWidth;
+
+ if (childDoesNotFitOnRow) {
// Layout all the children on this row but the current one.
layoutChildrenOnRow(rowStartIdx, i, fullRowWidth, xPos, yPos,
lastHorizontalSpacing);
// Update the positions for starting on the new row.
- xPos = getPaddingLeft();
+ xPos = isRTL()
+ ? fullRowWidth - getPaddingRight()
+ : getPaddingLeft();
yPos += mLineHeight;
rowStartIdx = i;
}
- xPos += currentChildWidth + lp.mHorizontalSpacing;
+ xPos = isRTL()
+ ? xPos - currentChildWidth - lp.mHorizontalSpacing
+ : xPos + currentChildWidth + lp.mHorizontalSpacing;
lastHorizontalSpacing = lp.mHorizontalSpacing;
}
}
@@ -148,21 +160,41 @@
private void layoutChildrenOnRow(int startIndex, int endIndex, int fullRowWidth, int xPos,
int yPos, int lastHorizontalSpacing) {
- int freeSpace = fullRowWidth - xPos + lastHorizontalSpacing;
- xPos = getPaddingLeft() + freeSpace;
+ if (!isRTL()) {
+ xPos = getPaddingLeft() + fullRowWidth - xPos + lastHorizontalSpacing;
+ }
for (int j = startIndex; j < endIndex; ++j) {
View currentChild = getChildAt(j);
+ int currentChildWidth = currentChild.getMeasuredWidth();
+ LayoutParams lp = (LayoutParams) currentChild.getLayoutParams();
+ if (isRTL() && j == startIndex) {
+ xPos = fullRowWidth - xPos - getPaddingRight() - currentChildWidth
+ - lp.mHorizontalSpacing;
+ }
+
currentChild.layout(
xPos,
yPos,
- xPos + currentChild.getMeasuredWidth(),
+ xPos + currentChildWidth,
yPos + currentChild.getMeasuredHeight());
- xPos += currentChild.getMeasuredWidth()
- + ((LayoutParams) currentChild.getLayoutParams()).mHorizontalSpacing;
+
+ if (isRTL()) {
+ int nextChildWidth = j < endIndex - 1
+ ? getChildAt(j + 1).getMeasuredWidth()
+ : 0;
+ xPos -= nextChildWidth + lp.mHorizontalSpacing;
+ } else {
+ xPos += currentChildWidth + lp.mHorizontalSpacing;
+ }
}
}
+ private boolean isRTL() {
+ return mContext.getResources().getConfiguration().getLayoutDirection()
+ == View.LAYOUT_DIRECTION_RTL;
+ }
+
public static class LayoutParams extends ViewGroup.LayoutParams {
public final int mHorizontalSpacing;
public final int mVerticalSpacing;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index 8fe60a0..977a77d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -21,6 +21,9 @@
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
import android.hardware.input.InputManager;
import android.os.Handler;
import android.os.Looper;
@@ -37,6 +40,7 @@
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager.KeyboardShortcutsReceiver;
+import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -52,158 +56,13 @@
/**
* Contains functionality for handling keyboard shortcuts.
*/
-public class KeyboardShortcuts {
+public final class KeyboardShortcuts {
private static final String TAG = KeyboardShortcuts.class.getSimpleName();
- private static final SparseArray<String> SPECIAL_CHARACTER_NAMES = new SparseArray<>();
- private static final SparseArray<String> MODIFIER_NAMES = new SparseArray<>();
-
- private static void loadSpecialCharacterNames(Context context) {
- SPECIAL_CHARACTER_NAMES.put(
- KeyEvent.KEYCODE_HOME, context.getString(R.string.keyboard_key_home));
- SPECIAL_CHARACTER_NAMES.put(
- KeyEvent.KEYCODE_BACK, context.getString(R.string.keyboard_key_back));
- SPECIAL_CHARACTER_NAMES.put(
- KeyEvent.KEYCODE_DPAD_UP, context.getString(R.string.keyboard_key_dpad_up));
- SPECIAL_CHARACTER_NAMES.put(
- KeyEvent.KEYCODE_DPAD_DOWN, context.getString(R.string.keyboard_key_dpad_down));
- SPECIAL_CHARACTER_NAMES.put(
- KeyEvent.KEYCODE_DPAD_LEFT, context.getString(R.string.keyboard_key_dpad_left));
- SPECIAL_CHARACTER_NAMES.put(
- KeyEvent.KEYCODE_DPAD_RIGHT, context.getString(R.string.keyboard_key_dpad_right));
- SPECIAL_CHARACTER_NAMES.put(
- KeyEvent.KEYCODE_DPAD_CENTER, context.getString(R.string.keyboard_key_dpad_center));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_PERIOD, ".");
- SPECIAL_CHARACTER_NAMES.put(
- KeyEvent.KEYCODE_TAB, context.getString(R.string.keyboard_key_tab));
- SPECIAL_CHARACTER_NAMES.put(
- KeyEvent.KEYCODE_SPACE, context.getString(R.string.keyboard_key_space));
- SPECIAL_CHARACTER_NAMES.put(
- KeyEvent.KEYCODE_ENTER, context.getString(R.string.keyboard_key_enter));
- SPECIAL_CHARACTER_NAMES.put(
- KeyEvent.KEYCODE_DEL, context.getString(R.string.keyboard_key_backspace));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE,
- context.getString(R.string.keyboard_key_media_play_pause));
- SPECIAL_CHARACTER_NAMES.put(
- KeyEvent.KEYCODE_MEDIA_STOP, context.getString(R.string.keyboard_key_media_stop));
- SPECIAL_CHARACTER_NAMES.put(
- KeyEvent.KEYCODE_MEDIA_NEXT, context.getString(R.string.keyboard_key_media_next));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_MEDIA_PREVIOUS,
- context.getString(R.string.keyboard_key_media_previous));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_MEDIA_REWIND,
- context.getString(R.string.keyboard_key_media_rewind));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD,
- context.getString(R.string.keyboard_key_media_fast_forward));
- SPECIAL_CHARACTER_NAMES.put(
- KeyEvent.KEYCODE_PAGE_UP, context.getString(R.string.keyboard_key_page_up));
- SPECIAL_CHARACTER_NAMES.put(
- KeyEvent.KEYCODE_PAGE_DOWN, context.getString(R.string.keyboard_key_page_down));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_A,
- context.getString(R.string.keyboard_key_button_template, "A"));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_B,
- context.getString(R.string.keyboard_key_button_template, "B"));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_C,
- context.getString(R.string.keyboard_key_button_template, "C"));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_X,
- context.getString(R.string.keyboard_key_button_template, "X"));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_Y,
- context.getString(R.string.keyboard_key_button_template, "Y"));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_Z,
- context.getString(R.string.keyboard_key_button_template, "Z"));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_L1,
- context.getString(R.string.keyboard_key_button_template, "L1"));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_R1,
- context.getString(R.string.keyboard_key_button_template, "R1"));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_L2,
- context.getString(R.string.keyboard_key_button_template, "L2"));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_R2,
- context.getString(R.string.keyboard_key_button_template, "R2"));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_START,
- context.getString(R.string.keyboard_key_button_template, "Start"));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_SELECT,
- context.getString(R.string.keyboard_key_button_template, "Select"));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_MODE,
- context.getString(R.string.keyboard_key_button_template, "Mode"));
- SPECIAL_CHARACTER_NAMES.put(
- KeyEvent.KEYCODE_FORWARD_DEL, context.getString(R.string.keyboard_key_forward_del));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_ESCAPE, "Esc");
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_SYSRQ, "SysRq");
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BREAK, "Break");
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_SCROLL_LOCK, "Scroll Lock");
- SPECIAL_CHARACTER_NAMES.put(
- KeyEvent.KEYCODE_MOVE_HOME, context.getString(R.string.keyboard_key_move_home));
- SPECIAL_CHARACTER_NAMES.put(
- KeyEvent.KEYCODE_MOVE_END, context.getString(R.string.keyboard_key_move_end));
- SPECIAL_CHARACTER_NAMES.put(
- KeyEvent.KEYCODE_INSERT, context.getString(R.string.keyboard_key_insert));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F1, "F1");
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F2, "F2");
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F3, "F3");
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F4, "F4");
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F5, "F5");
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F6, "F6");
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F7, "F7");
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F8, "F8");
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F9, "F9");
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F10, "F10");
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F11, "F11");
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F12, "F12");
- SPECIAL_CHARACTER_NAMES.put(
- KeyEvent.KEYCODE_NUM_LOCK, context.getString(R.string.keyboard_key_num_lock));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_0,
- context.getString(R.string.keyboard_key_numpad_template, "0"));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_1,
- context.getString(R.string.keyboard_key_numpad_template, "1"));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_2,
- context.getString(R.string.keyboard_key_numpad_template, "2"));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_3,
- context.getString(R.string.keyboard_key_numpad_template, "3"));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_4,
- context.getString(R.string.keyboard_key_numpad_template, "4"));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_5,
- context.getString(R.string.keyboard_key_numpad_template, "5"));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_6,
- context.getString(R.string.keyboard_key_numpad_template, "6"));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_7,
- context.getString(R.string.keyboard_key_numpad_template, "7"));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_8,
- context.getString(R.string.keyboard_key_numpad_template, "8"));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_9,
- context.getString(R.string.keyboard_key_numpad_template, "9"));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_DIVIDE,
- context.getString(R.string.keyboard_key_numpad_template, "/"));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_MULTIPLY,
- context.getString(R.string.keyboard_key_numpad_template, "*"));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_SUBTRACT,
- context.getString(R.string.keyboard_key_numpad_template, "-"));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_ADD,
- context.getString(R.string.keyboard_key_numpad_template, "+"));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_DOT,
- context.getString(R.string.keyboard_key_numpad_template, "."));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_COMMA,
- context.getString(R.string.keyboard_key_numpad_template, ","));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_ENTER,
- context.getString(R.string.keyboard_key_numpad_template,
- context.getString(R.string.keyboard_key_enter)));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_EQUALS,
- context.getString(R.string.keyboard_key_numpad_template, "="));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_LEFT_PAREN,
- context.getString(R.string.keyboard_key_numpad_template, "("));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_RIGHT_PAREN,
- context.getString(R.string.keyboard_key_numpad_template, ")"));
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_ZENKAKU_HANKAKU, "半角/全角");
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_EISU, "英数");
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_MUHENKAN, "無変換");
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_HENKAN, "変換");
- SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_KATAKANA_HIRAGANA, "かな");
-
- MODIFIER_NAMES.put(KeyEvent.META_META_ON, "Meta");
- MODIFIER_NAMES.put(KeyEvent.META_CTRL_ON, "Ctrl");
- MODIFIER_NAMES.put(KeyEvent.META_ALT_ON, "Alt");
- MODIFIER_NAMES.put(KeyEvent.META_SHIFT_ON, "Shift");
- MODIFIER_NAMES.put(KeyEvent.META_SYM_ON, "Sym");
- MODIFIER_NAMES.put(KeyEvent.META_FUNCTION_ON, "Fn");
- }
+ private final SparseArray<String> mSpecialCharacterNames = new SparseArray<>();
+ private final SparseArray<String> mModifierNames = new SparseArray<>();
+ private final SparseArray<Drawable> mSpecialCharacterDrawables = new SparseArray<>();
+ private final SparseArray<Drawable> mModifierDrawables = new SparseArray<>();
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final Context mContext;
@@ -218,9 +77,170 @@
public KeyboardShortcuts(Context context) {
this.mContext = new ContextThemeWrapper(context, android.R.style.Theme_Material_Light);
- if (SPECIAL_CHARACTER_NAMES.size() == 0) {
- loadSpecialCharacterNames(context);
- }
+ loadResources(context);
+ }
+
+ private void loadResources(Context context) {
+ mSpecialCharacterNames.put(
+ KeyEvent.KEYCODE_HOME, context.getString(R.string.keyboard_key_home));
+ mSpecialCharacterNames.put(
+ KeyEvent.KEYCODE_BACK, context.getString(R.string.keyboard_key_back));
+ mSpecialCharacterNames.put(
+ KeyEvent.KEYCODE_DPAD_UP, context.getString(R.string.keyboard_key_dpad_up));
+ mSpecialCharacterNames.put(
+ KeyEvent.KEYCODE_DPAD_DOWN, context.getString(R.string.keyboard_key_dpad_down));
+ mSpecialCharacterNames.put(
+ KeyEvent.KEYCODE_DPAD_LEFT, context.getString(R.string.keyboard_key_dpad_left));
+ mSpecialCharacterNames.put(
+ KeyEvent.KEYCODE_DPAD_RIGHT, context.getString(R.string.keyboard_key_dpad_right));
+ mSpecialCharacterNames.put(
+ KeyEvent.KEYCODE_DPAD_CENTER, context.getString(R.string.keyboard_key_dpad_center));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_PERIOD, ".");
+ mSpecialCharacterNames.put(
+ KeyEvent.KEYCODE_TAB, context.getString(R.string.keyboard_key_tab));
+ mSpecialCharacterNames.put(
+ KeyEvent.KEYCODE_SPACE, context.getString(R.string.keyboard_key_space));
+ mSpecialCharacterNames.put(
+ KeyEvent.KEYCODE_ENTER, context.getString(R.string.keyboard_key_enter));
+ mSpecialCharacterNames.put(
+ KeyEvent.KEYCODE_DEL, context.getString(R.string.keyboard_key_backspace));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE,
+ context.getString(R.string.keyboard_key_media_play_pause));
+ mSpecialCharacterNames.put(
+ KeyEvent.KEYCODE_MEDIA_STOP, context.getString(R.string.keyboard_key_media_stop));
+ mSpecialCharacterNames.put(
+ KeyEvent.KEYCODE_MEDIA_NEXT, context.getString(R.string.keyboard_key_media_next));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_MEDIA_PREVIOUS,
+ context.getString(R.string.keyboard_key_media_previous));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_MEDIA_REWIND,
+ context.getString(R.string.keyboard_key_media_rewind));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD,
+ context.getString(R.string.keyboard_key_media_fast_forward));
+ mSpecialCharacterNames.put(
+ KeyEvent.KEYCODE_PAGE_UP, context.getString(R.string.keyboard_key_page_up));
+ mSpecialCharacterNames.put(
+ KeyEvent.KEYCODE_PAGE_DOWN, context.getString(R.string.keyboard_key_page_down));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_A,
+ context.getString(R.string.keyboard_key_button_template, "A"));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_B,
+ context.getString(R.string.keyboard_key_button_template, "B"));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_C,
+ context.getString(R.string.keyboard_key_button_template, "C"));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_X,
+ context.getString(R.string.keyboard_key_button_template, "X"));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_Y,
+ context.getString(R.string.keyboard_key_button_template, "Y"));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_Z,
+ context.getString(R.string.keyboard_key_button_template, "Z"));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_L1,
+ context.getString(R.string.keyboard_key_button_template, "L1"));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_R1,
+ context.getString(R.string.keyboard_key_button_template, "R1"));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_L2,
+ context.getString(R.string.keyboard_key_button_template, "L2"));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_R2,
+ context.getString(R.string.keyboard_key_button_template, "R2"));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_START,
+ context.getString(R.string.keyboard_key_button_template, "Start"));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_SELECT,
+ context.getString(R.string.keyboard_key_button_template, "Select"));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_MODE,
+ context.getString(R.string.keyboard_key_button_template, "Mode"));
+ mSpecialCharacterNames.put(
+ KeyEvent.KEYCODE_FORWARD_DEL, context.getString(R.string.keyboard_key_forward_del));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_ESCAPE, "Esc");
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_SYSRQ, "SysRq");
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_BREAK, "Break");
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_SCROLL_LOCK, "Scroll Lock");
+ mSpecialCharacterNames.put(
+ KeyEvent.KEYCODE_MOVE_HOME, context.getString(R.string.keyboard_key_move_home));
+ mSpecialCharacterNames.put(
+ KeyEvent.KEYCODE_MOVE_END, context.getString(R.string.keyboard_key_move_end));
+ mSpecialCharacterNames.put(
+ KeyEvent.KEYCODE_INSERT, context.getString(R.string.keyboard_key_insert));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_F1, "F1");
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_F2, "F2");
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_F3, "F3");
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_F4, "F4");
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_F5, "F5");
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_F6, "F6");
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_F7, "F7");
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_F8, "F8");
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_F9, "F9");
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_F10, "F10");
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_F11, "F11");
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_F12, "F12");
+ mSpecialCharacterNames.put(
+ KeyEvent.KEYCODE_NUM_LOCK, context.getString(R.string.keyboard_key_num_lock));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_0,
+ context.getString(R.string.keyboard_key_numpad_template, "0"));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_1,
+ context.getString(R.string.keyboard_key_numpad_template, "1"));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_2,
+ context.getString(R.string.keyboard_key_numpad_template, "2"));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_3,
+ context.getString(R.string.keyboard_key_numpad_template, "3"));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_4,
+ context.getString(R.string.keyboard_key_numpad_template, "4"));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_5,
+ context.getString(R.string.keyboard_key_numpad_template, "5"));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_6,
+ context.getString(R.string.keyboard_key_numpad_template, "6"));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_7,
+ context.getString(R.string.keyboard_key_numpad_template, "7"));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_8,
+ context.getString(R.string.keyboard_key_numpad_template, "8"));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_9,
+ context.getString(R.string.keyboard_key_numpad_template, "9"));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_DIVIDE,
+ context.getString(R.string.keyboard_key_numpad_template, "/"));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_MULTIPLY,
+ context.getString(R.string.keyboard_key_numpad_template, "*"));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_SUBTRACT,
+ context.getString(R.string.keyboard_key_numpad_template, "-"));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_ADD,
+ context.getString(R.string.keyboard_key_numpad_template, "+"));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_DOT,
+ context.getString(R.string.keyboard_key_numpad_template, "."));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_COMMA,
+ context.getString(R.string.keyboard_key_numpad_template, ","));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_ENTER,
+ context.getString(R.string.keyboard_key_numpad_template,
+ context.getString(R.string.keyboard_key_enter)));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_EQUALS,
+ context.getString(R.string.keyboard_key_numpad_template, "="));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_LEFT_PAREN,
+ context.getString(R.string.keyboard_key_numpad_template, "("));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_RIGHT_PAREN,
+ context.getString(R.string.keyboard_key_numpad_template, ")"));
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_ZENKAKU_HANKAKU, "半角/全角");
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_EISU, "英数");
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_MUHENKAN, "無変換");
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_HENKAN, "変換");
+ mSpecialCharacterNames.put(KeyEvent.KEYCODE_KATAKANA_HIRAGANA, "かな");
+
+ mModifierNames.put(KeyEvent.META_META_ON, "Meta");
+ mModifierNames.put(KeyEvent.META_CTRL_ON, "Ctrl");
+ mModifierNames.put(KeyEvent.META_ALT_ON, "Alt");
+ mModifierNames.put(KeyEvent.META_SHIFT_ON, "Shift");
+ mModifierNames.put(KeyEvent.META_SYM_ON, "Sym");
+ mModifierNames.put(KeyEvent.META_FUNCTION_ON, "Fn");
+
+ mSpecialCharacterDrawables.put(
+ KeyEvent.KEYCODE_DEL, context.getDrawable(R.drawable.ic_ksh_key_backspace));
+ mSpecialCharacterDrawables.put(
+ KeyEvent.KEYCODE_ENTER, context.getDrawable(R.drawable.ic_ksh_key_enter));
+ mSpecialCharacterDrawables.put(
+ KeyEvent.KEYCODE_DPAD_UP, context.getDrawable(R.drawable.ic_ksh_key_up));
+ mSpecialCharacterDrawables.put(
+ KeyEvent.KEYCODE_DPAD_RIGHT, context.getDrawable(R.drawable.ic_ksh_key_right));
+ mSpecialCharacterDrawables.put(
+ KeyEvent.KEYCODE_DPAD_DOWN, context.getDrawable(R.drawable.ic_ksh_key_down));
+ mSpecialCharacterDrawables.put(
+ KeyEvent.KEYCODE_DPAD_LEFT, context.getDrawable(R.drawable.ic_ksh_key_left));
+
+ mModifierDrawables.put(
+ KeyEvent.META_META_ON, context.getDrawable(R.drawable.ic_ksh_key_meta));
}
public void toggleKeyboardShortcuts(int deviceId) {
@@ -245,7 +265,57 @@
systemGroup.addItem(new KeyboardShortcutInfo(
mContext.getString(R.string.keyboard_shortcut_group_system_recents),
KeyEvent.KEYCODE_TAB, KeyEvent.META_ALT_ON));
+ systemGroup.addItem(new KeyboardShortcutInfo(
+ mContext.getString(
+ R.string.keyboard_shortcut_group_system_notifications),
+ KeyEvent.KEYCODE_N, KeyEvent.META_META_ON));
+ systemGroup.addItem(new KeyboardShortcutInfo(
+ mContext.getString(
+ R.string.keyboard_shortcut_group_system_shortcuts_helper),
+ KeyEvent.KEYCODE_SLASH, KeyEvent.META_META_ON));
+ systemGroup.addItem(new KeyboardShortcutInfo(
+ mContext.getString(
+ R.string.keyboard_shortcut_group_system_switch_input),
+ KeyEvent.KEYCODE_SPACE, KeyEvent.META_META_ON));
result.add(systemGroup);
+
+ KeyboardShortcutGroup applicationGroup = new KeyboardShortcutGroup(
+ mContext.getString(R.string.keyboard_shortcut_group_applications),
+ true);
+ applicationGroup.addItem(new KeyboardShortcutInfo(
+ mContext.getString(
+ R.string.keyboard_shortcut_group_applications_assist),
+ KeyEvent.KEYCODE_UNKNOWN, KeyEvent.META_META_ON));
+ applicationGroup.addItem(new KeyboardShortcutInfo(
+ mContext.getString(
+ R.string.keyboard_shortcut_group_applications_browser),
+ KeyEvent.KEYCODE_B, KeyEvent.META_META_ON));
+ applicationGroup.addItem(new KeyboardShortcutInfo(
+ mContext.getString(
+ R.string.keyboard_shortcut_group_applications_contacts),
+ KeyEvent.KEYCODE_C, KeyEvent.META_META_ON));
+ applicationGroup.addItem(new KeyboardShortcutInfo(
+ mContext.getString(
+ R.string.keyboard_shortcut_group_applications_email),
+ KeyEvent.KEYCODE_E, KeyEvent.META_META_ON));
+ applicationGroup.addItem(new KeyboardShortcutInfo(
+ mContext.getString(
+ R.string.keyboard_shortcut_group_applications_im),
+ KeyEvent.KEYCODE_T, KeyEvent.META_META_ON));
+ applicationGroup.addItem(new KeyboardShortcutInfo(
+ mContext.getString(
+ R.string.keyboard_shortcut_group_applications_music),
+ KeyEvent.KEYCODE_P, KeyEvent.META_META_ON));
+ applicationGroup.addItem(new KeyboardShortcutInfo(
+ mContext.getString(
+ R.string.keyboard_shortcut_group_applications_youtube),
+ KeyEvent.KEYCODE_Y, KeyEvent.META_META_ON));
+ applicationGroup.addItem(new KeyboardShortcutInfo(
+ mContext.getString(
+ R.string.keyboard_shortcut_group_applications_calendar),
+ KeyEvent.KEYCODE_L, KeyEvent.META_META_ON));
+ result.add(applicationGroup);
+
showKeyboardShortcutsDialog(result);
}
}, deviceId);
@@ -293,6 +363,8 @@
List<KeyboardShortcutGroup> keyboardShortcutGroups) {
LayoutInflater inflater = LayoutInflater.from(mContext);
final int keyboardShortcutGroupsSize = keyboardShortcutGroups.size();
+ // Needed to be able to scale the image items to the same height as the text items.
+ final int shortcutTextItemHeight = getShortcutTextItemHeight(inflater);
for (int i = 0; i < keyboardShortcutGroupsSize; i++) {
KeyboardShortcutGroup group = keyboardShortcutGroups.get(i);
TextView categoryTitle = (TextView) inflater.inflate(
@@ -314,7 +386,7 @@
Log.w(TAG, "Keyboard Shortcut contains key not on device, skipping.");
continue;
}
- List<String> shortcutKeys = getHumanReadableShortcutKeys(info);
+ List<StringOrDrawable> shortcutKeys = getHumanReadableShortcutKeys(info);
if (shortcutKeys == null) {
// Ignore shortcuts we can't display keys for.
Log.w(TAG, "Keyboard Shortcut contains unsupported keys, skipping.");
@@ -330,11 +402,26 @@
.findViewById(R.id.keyboard_shortcuts_item_container);
final int shortcutKeysSize = shortcutKeys.size();
for (int k = 0; k < shortcutKeysSize; k++) {
- String shortcutKey = shortcutKeys.get(k);
- TextView shortcutKeyView = (TextView) inflater.inflate(
- R.layout.keyboard_shortcuts_key_view, shortcutItemsContainer, false);
- shortcutKeyView.setText(shortcutKey);
- shortcutItemsContainer.addView(shortcutKeyView);
+ StringOrDrawable shortcutRepresentation = shortcutKeys.get(k);
+ if (shortcutRepresentation.drawable != null) {
+ ImageView shortcutKeyIconView = (ImageView) inflater.inflate(
+ R.layout.keyboard_shortcuts_key_icon_view, shortcutItemsContainer,
+ false);
+ Bitmap bitmap = Bitmap.createBitmap(shortcutTextItemHeight,
+ shortcutTextItemHeight, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ shortcutRepresentation.drawable.setBounds(0, 0, canvas.getWidth(),
+ canvas.getHeight());
+ shortcutRepresentation.drawable.draw(canvas);
+ shortcutKeyIconView.setImageBitmap(bitmap);
+ shortcutItemsContainer.addView(shortcutKeyIconView);
+ } else if (shortcutRepresentation.string != null) {
+ TextView shortcutKeyTextView = (TextView) inflater.inflate(
+ R.layout.keyboard_shortcuts_key_view, shortcutItemsContainer,
+ false);
+ shortcutKeyTextView.setText(shortcutRepresentation.string);
+ shortcutItemsContainer.addView(shortcutKeyTextView);
+ }
}
shortcutContainer.addView(shortcutView);
}
@@ -348,17 +435,34 @@
}
}
- private List<String> getHumanReadableShortcutKeys(KeyboardShortcutInfo info) {
- List<String> shortcutKeys = getHumanReadableModifiers(info);
+ private int getShortcutTextItemHeight(LayoutInflater inflater) {
+ TextView shortcutKeyTextView = (TextView) inflater.inflate(
+ R.layout.keyboard_shortcuts_key_view, null, false);
+ shortcutKeyTextView.measure(
+ View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+ return shortcutKeyTextView.getMeasuredHeight()
+ - shortcutKeyTextView.getPaddingTop()
+ - shortcutKeyTextView.getPaddingBottom();
+ }
+
+ private List<StringOrDrawable> getHumanReadableShortcutKeys(KeyboardShortcutInfo info) {
+ List<StringOrDrawable> shortcutKeys = getHumanReadableModifiers(info);
if (shortcutKeys == null) {
return null;
}
- String displayLabelString;
- if (info.getKeycode() == KeyEvent.KEYCODE_UNKNOWN) {
+ String displayLabelString = null;
+ Drawable displayLabelDrawable = null;
+ if (info.getBaseCharacter() > Character.MIN_VALUE) {
displayLabelString = String.valueOf(info.getBaseCharacter());
- } else if (SPECIAL_CHARACTER_NAMES.get(info.getKeycode()) != null) {
- displayLabelString = SPECIAL_CHARACTER_NAMES.get(info.getKeycode());
+ } else if (mSpecialCharacterDrawables.get(info.getKeycode()) != null) {
+ displayLabelDrawable = mSpecialCharacterDrawables.get(info.getKeycode());
+ } else if (mSpecialCharacterNames.get(info.getKeycode()) != null) {
+ displayLabelString = mSpecialCharacterNames.get(info.getKeycode());
} else {
+ // Special case for shortcuts with no base key or keycode.
+ if (info.getKeycode() == KeyEvent.KEYCODE_UNKNOWN) {
+ return shortcutKeys;
+ }
// TODO: Have a generic map for when we don't have the device's.
char displayLabel = mKeyCharacterMap == null
? 0 : mKeyCharacterMap.getDisplayLabel(info.getKeycode());
@@ -368,20 +472,31 @@
return null;
}
}
- shortcutKeys.add(displayLabelString.toUpperCase());
+
+ if (displayLabelDrawable != null) {
+ shortcutKeys.add(new StringOrDrawable(displayLabelDrawable));
+ } else if (displayLabelString != null) {
+ shortcutKeys.add(new StringOrDrawable(displayLabelString.toUpperCase()));
+ }
return shortcutKeys;
}
- private List<String> getHumanReadableModifiers(KeyboardShortcutInfo info) {
- final List<String> shortcutKeys = new ArrayList<>();
+ private List<StringOrDrawable> getHumanReadableModifiers(KeyboardShortcutInfo info) {
+ final List<StringOrDrawable> shortcutKeys = new ArrayList<>();
int modifiers = info.getModifiers();
if (modifiers == 0) {
return shortcutKeys;
}
- for(int i = 0; i < MODIFIER_NAMES.size(); ++i) {
- final int supportedModifier = MODIFIER_NAMES.keyAt(i);
+ for(int i = 0; i < mModifierNames.size(); ++i) {
+ final int supportedModifier = mModifierNames.keyAt(i);
if ((modifiers & supportedModifier) != 0) {
- shortcutKeys.add(MODIFIER_NAMES.get(supportedModifier).toUpperCase());
+ if (mModifierDrawables.get(supportedModifier) != null) {
+ shortcutKeys.add(new StringOrDrawable(
+ mModifierDrawables.get(supportedModifier)));
+ } else {
+ shortcutKeys.add(new StringOrDrawable(
+ mModifierNames.get(supportedModifier).toUpperCase()));
+ }
modifiers &= ~supportedModifier;
}
}
@@ -391,4 +506,17 @@
}
return shortcutKeys;
}
+
+ private static final class StringOrDrawable {
+ public String string;
+ public Drawable drawable;
+
+ public StringOrDrawable(String string) {
+ this.string = string;
+ }
+
+ public StringOrDrawable(Drawable drawable) {
+ this.drawable = drawable;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index f4fb0b9..5b00523 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -604,7 +604,7 @@
}
int expandedVisualType = getVisualTypeForHeight(height);
int collapsedVisualType = getVisualTypeForHeight(
- mContainingNotification.getMinExpandHeight());
+ mContainingNotification.getCollapsedHeight());
return mTransformationStartVisibleType == collapsedVisualType
? expandedVisualType
: collapsedVisualType;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index 45a24a0..3c464d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -16,28 +16,35 @@
package com.android.systemui.statusbar;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.app.INotificationManager;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
+import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.AttributeSet;
import android.view.View;
+import android.view.ViewAnimationUtils;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RadioButton;
+import android.widget.RadioGroup;
import android.widget.SeekBar;
import android.widget.TextView;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.MetricsProto.MetricsEvent;
import com.android.settingslib.Utils;
+import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.statusbar.stack.StackStateAnimator;
import com.android.systemui.tuner.TunerService;
/**
@@ -46,6 +53,8 @@
public class NotificationGuts extends LinearLayout implements TunerService.Tunable {
public static final String SHOW_SLIDER = "show_importance_slider";
+ private static final long CLOSE_GUTS_DELAY = 8000;
+
private Drawable mBackground;
private int mClipTopAmount;
private int mActualHeight;
@@ -59,10 +68,35 @@
private RadioButton mSilent;
private RadioButton mReset;
+ private Handler mHandler;
+ private Runnable mFalsingCheck;
+ private boolean mNeedsFalsingProtection;
+ private OnGutsClosedListener mListener;
+
+ public interface OnGutsClosedListener {
+ public void onGutsClosed(NotificationGuts guts);
+ }
+
public NotificationGuts(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(false);
TunerService.get(mContext).addTunable(this, SHOW_SLIDER);
+ mHandler = new Handler();
+ mFalsingCheck = new Runnable() {
+ @Override
+ public void run() {
+ if (mNeedsFalsingProtection && mExposed) {
+ closeControls(-1 /* x */, -1 /* y */, true /* notify */);
+ }
+ }
+ };
+ }
+
+ public void resetFalsingCheck() {
+ mHandler.removeCallbacks(mFalsingCheck);
+ if (mNeedsFalsingProtection && mExposed) {
+ mHandler.postDelayed(mFalsingCheck, CLOSE_GUTS_DELAY);
+ }
}
@Override
@@ -130,30 +164,23 @@
importanceSlider.setVisibility(View.VISIBLE);
importanceButtons.setVisibility(View.GONE);
} else {
- int userImportance = NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
+ mStartingImportance = NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
try {
- userImportance =
+ mStartingImportance =
mINotificationManager.getImportance(sbn.getPackageName(), sbn.getUid());
} catch (RemoteException e) {}
- bindToggles(importanceButtons, userImportance, systemApp);
+ bindToggles(importanceButtons, mStartingImportance, systemApp);
importanceButtons.setVisibility(View.VISIBLE);
importanceSlider.setVisibility(View.GONE);
}
}
+ public boolean hasImportanceChanged() {
+ return mStartingImportance != getSelectedImportance();
+ }
+
void saveImportance(final StatusBarNotification sbn) {
- int progress;
- if (mSeekBar!= null && mSeekBar.isShown()) {
- progress = mSeekBar.getProgress();
- } else {
- if (mBlock.isChecked()) {
- progress = NotificationListenerService.Ranking.IMPORTANCE_NONE;
- } else if (mSilent.isChecked()) {
- progress = NotificationListenerService.Ranking.IMPORTANCE_LOW;
- } else {
- progress = NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
- }
- }
+ int progress = getSelectedImportance();
MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE,
progress - mStartingImportance);
try {
@@ -163,8 +190,29 @@
}
}
+ private int getSelectedImportance() {
+ if (mSeekBar!= null && mSeekBar.isShown()) {
+ return mSeekBar.getProgress();
+ } else {
+ if (mBlock.isChecked()) {
+ return NotificationListenerService.Ranking.IMPORTANCE_NONE;
+ } else if (mSilent.isChecked()) {
+ return NotificationListenerService.Ranking.IMPORTANCE_LOW;
+ } else {
+ return NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
+ }
+ }
+ }
+
private void bindToggles(final View importanceButtons, final int importance,
final boolean systemApp) {
+ ((RadioGroup) importanceButtons).setOnCheckedChangeListener(
+ new RadioGroup.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(RadioGroup group, int checkedId) {
+ resetFalsingCheck();
+ }
+ });
mBlock = (RadioButton) importanceButtons.findViewById(R.id.block_importance);
mSilent = (RadioButton) importanceButtons.findViewById(R.id.silent_importance);
mReset = (RadioButton) importanceButtons.findViewById(R.id.reset_importance);
@@ -198,6 +246,7 @@
mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ resetFalsingCheck();
if (progress < minProgress) {
seekBar.setProgress(minProgress);
progress = minProgress;
@@ -210,7 +259,7 @@
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
- // no-op
+ resetFalsingCheck();
}
@Override
@@ -256,6 +305,38 @@
mSeekBar.setProgress(mStartingImportance);
}
+ public void closeControls(int x, int y, boolean notify) {
+ if (getWindowToken() == null) {
+ if (notify && mListener != null) {
+ mListener.onGutsClosed(this);
+ }
+ return;
+ }
+ if (x == -1 || y == -1) {
+ x = (getLeft() + getRight()) / 2;
+ y = (getTop() + getHeight() / 2);
+ }
+ final double horz = Math.max(getWidth() - x, x);
+ final double vert = Math.max(getHeight() - y, y);
+ final float r = (float) Math.hypot(horz, vert);
+ final Animator a = ViewAnimationUtils.createCircularReveal(this,
+ x, y, r, 0);
+ a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ a.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
+ a.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ setVisibility(View.GONE);
+ }
+ });
+ a.start();
+ setExposed(false, mNeedsFalsingProtection);
+ if (notify && mListener != null) {
+ mListener.onGutsClosed(this);
+ }
+ }
+
public void setActualHeight(int actualHeight) {
mActualHeight = actualHeight;
invalidate();
@@ -277,8 +358,18 @@
return false;
}
- public void setExposed(boolean exposed) {
+ public void setClosedListener(OnGutsClosedListener listener) {
+ mListener = listener;
+ }
+
+ public void setExposed(boolean exposed, boolean needsFalsingProtection) {
mExposed = exposed;
+ mNeedsFalsingProtection = needsFalsingProtection;
+ if (mExposed && mNeedsFalsingProtection) {
+ resetFalsingCheck();
+ } else {
+ mHandler.removeCallbacks(mFalsingCheck);
+ }
}
public boolean areGutsExposed() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
index c70aad2..06d79a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
@@ -131,11 +131,8 @@
mComparators.add(HeaderProcessor.forTextView(mRow,
com.android.internal.R.id.app_name_text));
mComparators.add(HeaderProcessor.forTextView(mRow,
- com.android.internal.R.id.header_sub_text));
- mComparators.add(HeaderProcessor.forTextView(mRow,
- com.android.internal.R.id.header_content_info));
- mDividers.add(com.android.internal.R.id.sub_text_divider);
- mDividers.add(com.android.internal.R.id.content_info_divider);
+ com.android.internal.R.id.header_text));
+ mDividers.add(com.android.internal.R.id.header_text_divider);
mDividers.add(com.android.internal.R.id.time_divider);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java
index a3e78c1..a5ebbba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java
@@ -41,7 +41,7 @@
/**
* Called when a notification is slid back over the gear.
*/
- public void onSettingsIconRowReset(NotificationSettingsIconRow row);
+ public void onSettingsIconRowReset(ExpandableNotificationRow row);
}
private ExpandableNotificationRow mParent;
@@ -92,9 +92,9 @@
mAnimating = false;
mSnapping = false;
mDismissing = false;
- setIconLocation(true /* on left */);
+ setIconLocation(true /* on left */, true /* force */);
if (mListener != null) {
- mListener.onSettingsIconRowReset(this);
+ mListener.onSettingsIconRowReset(mParent);
}
}
@@ -104,6 +104,7 @@
public void setNotificationRowParent(ExpandableNotificationRow parent) {
mParent = parent;
+ setIconLocation(mOnLeft, true /* force */);
}
public void setAppName(String appName) {
@@ -183,7 +184,7 @@
if (isIconLocationChange(transX)) {
setGearAlpha(0f);
}
- setIconLocation(transX > 0 /* fromLeft */);
+ setIconLocation(transX > 0 /* fromLeft */, false /* force */);
mFadeAnimator = ValueAnimator.ofFloat(mGearIcon.getAlpha(), 1);
mFadeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
@@ -220,12 +221,20 @@
mFadeAnimator.start();
}
- public void setIconLocation(boolean onLeft) {
- if (onLeft == mOnLeft || mSnapping) {
- // Same side? Do nothing.
+ @Override
+ public void onRtlPropertiesChanged(int layoutDirection) {
+ setIconLocation(mOnLeft, true /* force */);
+ }
+
+ public void setIconLocation(boolean onLeft, boolean force) {
+ if ((!force && onLeft == mOnLeft) || mSnapping || mParent == null) {
+ // Do nothing
return;
}
- setTranslationX(onLeft ? 0 : (mParent.getWidth() - mHorizSpaceForGear));
+ final boolean isRtl = mParent.isLayoutRtl();
+ final float left = isRtl ? -(mParent.getWidth() - mHorizSpaceForGear) : 0;
+ final float right = isRtl ? 0 : (mParent.getWidth() - mHorizSpaceForGear);
+ setTranslationX(onLeft ? left : right);
mOnLeft = onLeft;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
index d7e47c2..5fea674 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
@@ -21,6 +21,8 @@
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.RemoteInputView;
+import android.util.ArraySet;
+
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -29,7 +31,8 @@
*/
public class RemoteInputController {
- private final ArrayList<WeakReference<NotificationData.Entry>> mRemoteInputs = new ArrayList<>();
+ private final ArrayList<WeakReference<NotificationData.Entry>> mOpen = new ArrayList<>();
+ private final ArraySet<String> mSpinning = new ArraySet<>();
private final ArrayList<Callback> mCallbacks = new ArrayList<>(3);
private final HeadsUpManager mHeadsUpManager;
@@ -44,7 +47,7 @@
boolean found = pruneWeakThenRemoveAndContains(
entry /* contains */, null /* remove */);
if (!found) {
- mRemoteInputs.add(new WeakReference<>(entry));
+ mOpen.add(new WeakReference<>(entry));
}
apply(entry);
@@ -58,6 +61,18 @@
apply(entry);
}
+ public void addSpinning(String key) {
+ mSpinning.add(key);
+ }
+
+ public void removeSpinning(String key) {
+ mSpinning.remove(key);
+ }
+
+ public boolean isSpinning(String key) {
+ return mSpinning.contains(key);
+ }
+
private void apply(NotificationData.Entry entry) {
mHeadsUpManager.setRemoteInputActive(entry, isRemoteInputActive(entry));
boolean remoteInputActive = isRemoteInputActive();
@@ -79,7 +94,7 @@
*/
public boolean isRemoteInputActive() {
pruneWeakThenRemoveAndContains(null /* contains */, null /* remove */);
- return !mRemoteInputs.isEmpty();
+ return !mOpen.isEmpty();
}
/**
@@ -91,10 +106,10 @@
private boolean pruneWeakThenRemoveAndContains(
NotificationData.Entry contains, NotificationData.Entry remove) {
boolean found = false;
- for (int i = mRemoteInputs.size() - 1; i >= 0; i--) {
- NotificationData.Entry item = mRemoteInputs.get(i).get();
+ for (int i = mOpen.size() - 1; i >= 0; i--) {
+ NotificationData.Entry item = mOpen.get(i).get();
if (item == null || item == remove) {
- mRemoteInputs.remove(i);
+ mOpen.remove(i);
} else if (item == contains) {
found = true;
}
@@ -108,7 +123,16 @@
mCallbacks.add(callback);
}
+ public void remoteInputSent(NotificationData.Entry entry) {
+ int N = mCallbacks.size();
+ for (int i = 0; i < N; i++) {
+ mCallbacks.get(i).onRemoteInputSent(entry);
+ }
+ }
+
public interface Callback {
- void onRemoteInputActive(boolean active);
+ default void onRemoteInputActive(boolean active) {}
+
+ default void onRemoteInputSent(NotificationData.Entry entry) {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
index bb43899..d9bf539 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
@@ -106,7 +106,7 @@
String category = getPackageCategory(packageName);
if (mFacetCategoryMap.containsKey(category)) {
- int index = mFacetCategoryMap.get(packageName);
+ int index = mFacetCategoryMap.get(category);
mFacetHasMultipleAppsCache.put(index, facetHasMultiplePackages(index));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 726aed3..4add3cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -24,6 +24,7 @@
import android.graphics.PixelFormat;
import android.os.Handler;
import android.os.Looper;
+import android.os.RemoteException;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewStub;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandableIndicator.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandableIndicator.java
index 04095e7..3fdc35c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandableIndicator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandableIndicator.java
@@ -23,6 +23,7 @@
public class ExpandableIndicator extends ImageView {
private boolean mExpanded;
+ private boolean mIsDefaultDirection = true;
public ExpandableIndicator(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -31,16 +32,14 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- final int res = mExpanded ? R.drawable.ic_volume_collapse_animation
- : R.drawable.ic_volume_expand_animation;
+ final int res = getDrawableResourceId(mExpanded);
setImageResource(res);
}
public void setExpanded(boolean expanded) {
if (expanded == mExpanded) return;
mExpanded = expanded;
- final int res = mExpanded ? R.drawable.ic_volume_expand_animation
- : R.drawable.ic_volume_collapse_animation;
+ final int res = getDrawableResourceId(!mExpanded);
// workaround to reset drawable
final AnimatedVectorDrawable avd = (AnimatedVectorDrawable) getContext()
.getDrawable(res).getConstantState().newDrawable();
@@ -48,4 +47,19 @@
avd.forceAnimationOnUI();
avd.start();
}
+
+ /** Whether the icons are using the default direction or the opposite */
+ public void setDefaultDirection(boolean isDefaultDirection) {
+ mIsDefaultDirection = isDefaultDirection;
+ }
+
+ private int getDrawableResourceId(boolean expanded) {
+ if (mIsDefaultDirection) {
+ return expanded ? R.drawable.ic_volume_collapse_animation
+ : R.drawable.ic_volume_expand_animation;
+ } else {
+ return expanded ? R.drawable.ic_volume_expand_animation
+ : R.drawable.ic_volume_collapse_animation;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
index 65e7973..3812429 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
@@ -85,7 +85,7 @@
// wallpaper.
final int lockWallpaperUserId =
mSelectedUser != null ? mSelectedUser.getIdentifier() : mCurrentUserId;
- ParcelFileDescriptor fd = mService.getWallpaper(null, WallpaperManager.FLAG_SET_LOCK,
+ ParcelFileDescriptor fd = mService.getWallpaper(null, WallpaperManager.FLAG_LOCK,
new Bundle(), lockWallpaperUserId);
if (fd != null) {
try {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileController.java
index 63ee0c0..41eed56 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileController.java
@@ -113,7 +113,8 @@
filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
- filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED);
+ filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
+ filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, null);
} else {
mContext.unregisterReceiver(mReceiver);
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 56a7dbe..6859348 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -76,6 +76,8 @@
private Drawable mHomeDefaultIcon, mHomeCarModeIcon;
private Drawable mRecentIcon;
private Drawable mDockedIcon;
+ private Drawable mImeIcon;
+ private Drawable mMenuIcon;
private NavigationBarGestureHelper mGestureHelper;
private DeadZone mDeadZone;
@@ -270,7 +272,8 @@
}
private void updateIcons(Context ctx, Configuration oldConfig, Configuration newConfig) {
- if (oldConfig.orientation != newConfig.orientation) {
+ if (oldConfig.orientation != newConfig.orientation
+ || oldConfig.densityDpi != newConfig.densityDpi) {
mDockedIcon = ctx.getDrawable(R.drawable.ic_sysbar_docked);
}
if (oldConfig.densityDpi != newConfig.densityDpi) {
@@ -280,8 +283,10 @@
mBackAltLandIcon = mBackAltIcon;
mHomeDefaultIcon = ctx.getDrawable(R.drawable.ic_sysbar_home);
-
mRecentIcon = ctx.getDrawable(R.drawable.ic_sysbar_recent);
+ mMenuIcon = ctx.getDrawable(R.drawable.ic_sysbar_menu);
+ mImeIcon = ctx.getDrawable(R.drawable.ic_ime_switcher_default);
+
updateCarModeIcons(ctx);
}
}
@@ -348,9 +353,11 @@
final boolean showImeButton = ((hints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0);
getImeSwitchButton().setVisibility(showImeButton ? View.VISIBLE : View.INVISIBLE);
+ getImeSwitchButton().setImageDrawable(mImeIcon);
// Update menu button in case the IME state has changed.
setMenuVisibility(mShowMenu, true);
+ getMenuButton().setImageDrawable(mMenuIcon);
setDisabledFlags(mDisabledFlags, true);
}
@@ -595,14 +602,12 @@
super.onConfigurationChanged(newConfig);
boolean uiCarModeChanged = updateCarMode(newConfig);
updateTaskSwitchHelper();
- if (uiCarModeChanged) {
- // uiMode changed either from carmode or to carmode.
- // replace the nav bar button icons based on which mode
- // we are switching to.
- setNavigationIconHints(mNavigationIconHints, true);
- }
updateIcons(getContext(), mConfiguration, newConfig);
updateRecentsIcon();
+ if (uiCarModeChanged || mConfiguration.densityDpi != newConfig.densityDpi) {
+ // If car mode or density changes, we need to reset the icons.
+ setNavigationIconHints(mNavigationIconHints, true);
+ }
mConfiguration.updateFrom(newConfig);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 560dbac..45e94f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -136,7 +136,7 @@
private boolean mStackScrollerOverscrolling;
private boolean mQsExpansionFromOverscroll;
private float mLastOverscroll;
- private boolean mQsExpansionEnabled = true;
+ protected boolean mQsExpansionEnabled = true;
private ValueAnimator mQsExpansionAnimator;
private FlingAnimationUtils mFlingAnimationUtils;
private int mStatusBarMinHeight;
@@ -202,6 +202,7 @@
notifyBarPanelExpansionChanged();
}
};
+ private NotificationGroupManager mGroupManager;
public NotificationPanelView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -224,7 +225,8 @@
public void onInflated(View v) {
mQsContainer = (QSContainer) v.findViewById(R.id.quick_settings_container);
mQsContainer.setPanelView(NotificationPanelView.this);
- mQsContainer.getHeader().setOnClickListener(NotificationPanelView.this);
+ mQsContainer.getHeader().findViewById(R.id.expand_indicator)
+ .setOnClickListener(NotificationPanelView.this);
}
});
mClockView = (TextView) findViewById(R.id.clock_view);
@@ -415,6 +417,11 @@
if (!(child instanceof ExpandableNotificationRow)) {
continue;
}
+ boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
+ ((ExpandableNotificationRow) child).getStatusBarNotification());
+ if (suppressedSummary) {
+ continue;
+ }
availableSpace -= child.getMinHeight() + notificationPadding;
if (availableSpace >= 0 && count < maximum) {
count++;
@@ -783,8 +790,9 @@
}
private boolean isInQsArea(float x, float y) {
- return (x >= mQsContainer.getX() && x <= mQsContainer.getX() + mQsContainer.getWidth()) &&
- (y <= mNotificationStackScroller.getBottomMostNotificationBottom()
+ return (x >= mQsDensityContainer.getX()
+ && x <= mQsDensityContainer.getX() + mQsDensityContainer.getWidth())
+ && (y <= mNotificationStackScroller.getBottomMostNotificationBottom()
|| y <= mQsContainer.getY() + mQsContainer.getHeight());
}
@@ -1202,7 +1210,9 @@
}
private String getKeyguardOrLockScreenString() {
- if (mStatusBarState == StatusBarState.KEYGUARD) {
+ if (mQsContainer.isCustomizing()) {
+ return getContext().getString(R.string.accessibility_desc_quick_settings_edit);
+ } else if (mStatusBarState == StatusBarState.KEYGUARD) {
return getContext().getString(R.string.accessibility_desc_lock_screen);
} else {
return getContext().getString(R.string.accessibility_desc_notification_shade);
@@ -1329,7 +1339,8 @@
return false;
}
View header = mKeyguardShowing ? mKeyguardStatusBar : mQsContainer.getHeader();
- boolean onHeader = x >= header.getX() && x <= header.getX() + header.getWidth()
+ boolean onHeader = x >= mQsDensityContainer.getX()
+ && x <= mQsDensityContainer.getX() + mQsDensityContainer.getWidth()
&& y >= header.getTop() && y <= header.getBottom();
if (mQsExpanded) {
return onHeader || (yDiff < 0 && isInQsArea(x, y));
@@ -1750,7 +1761,7 @@
@Override
public void onClick(View v) {
- if (v == mQsContainer.getHeader()) {
+ if (v.getId() == R.id.expand_indicator) {
onQsExpansionStarted();
if (mQsExpanded) {
flingSettings(0 /* vel */, false /* expand */, null, true /* isClick */);
@@ -2298,4 +2309,8 @@
List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);
return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName());
}
+
+ public void setGroupManager(NotificationGroupManager groupManager) {
+ mGroupManager = groupManager;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
index 960515b..35fb2a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
@@ -35,6 +35,7 @@
public class NotificationsQuickSettingsContainer extends FrameLayout
implements ViewStub.OnInflateListener, DensityContainer.InflateListener {
+
private DensityContainer mQsContainer;
private View mUserSwitcher;
private View mStackScroller;
@@ -43,6 +44,9 @@
private boolean mQsExpanded;
private boolean mCustomizerAnimating;
+ private int mBottomPadding;
+ private int mStackScrollerMargin;
+
public NotificationsQuickSettingsContainer(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -53,6 +57,7 @@
mQsContainer = (DensityContainer) findViewById(R.id.qs_density_container);
mQsContainer.addInflateListener(this);
mStackScroller = findViewById(R.id.notification_stack_scroller);
+ mStackScrollerMargin = ((LayoutParams) mStackScroller.getLayoutParams()).bottomMargin;
mKeyguardStatusBar = findViewById(R.id.keyguard_header);
ViewStub userSwitcher = (ViewStub) findViewById(R.id.keyguard_user_switcher);
userSwitcher.setOnInflateListener(this);
@@ -75,7 +80,8 @@
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- setPadding(0, 0, 0, insets.getStableInsetBottom());
+ mBottomPadding = insets.getStableInsetBottom();
+ setPadding(0, 0, 0, mBottomPadding);
return insets;
}
@@ -141,4 +147,22 @@
invalidate();
}
}
+
+ public void setCustomizerShowing(boolean isShowing) {
+ if (isShowing) {
+ // Clear out bottom paddings/margins so the qs customization can be full height.
+ setPadding(0, 0, 0, 0);
+ setBottomMargin(mStackScroller, 0);
+ } else {
+ setPadding(0, 0, 0, mBottomPadding);
+ setBottomMargin(mStackScroller, mStackScrollerMargin);
+ }
+
+ }
+
+ private void setBottomMargin(View v, int bottomMargin) {
+ LayoutParams params = (LayoutParams) v.getLayoutParams();
+ params.bottomMargin = bottomMargin;
+ v.setLayoutParams(params);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 86031e1..bf58592 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -21,7 +21,9 @@
import android.animation.AnimatorListenerAdapter;
import android.annotation.NonNull;
import android.app.ActivityManager;
+import android.app.ActivityManager.StackId;
import android.app.ActivityManagerNative;
+import android.app.ActivityOptions;
import android.app.IActivityManager;
import android.app.Notification;
import android.app.PendingIntent;
@@ -120,6 +122,7 @@
import com.android.systemui.qs.QSPanel;
import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.AppTransitionFinishedEvent;
import com.android.systemui.recents.events.activity.UndockingTaskEvent;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.stackdivider.WindowManagerProxy;
@@ -283,7 +286,7 @@
HotspotControllerImpl mHotspotController;
RotationLockControllerImpl mRotationLockController;
UserInfoController mUserInfoController;
- ZenModeController mZenModeController;
+ protected ZenModeController mZenModeController;
CastControllerImpl mCastController;
VolumeComponent mVolumeComponent;
KeyguardUserSwitcher mKeyguardUserSwitcher;
@@ -708,6 +711,7 @@
mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(
R.id.notification_panel);
mNotificationPanel.setStatusBar(this);
+ mNotificationPanel.setGroupManager(mGroupManager);
mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);
mStatusBarView.setBar(this);
@@ -751,7 +755,6 @@
mStackScroller = (NotificationStackScrollLayout) mStatusBarWindow.findViewById(
R.id.notification_stack_scroller);
mStackScroller.setLongPressListener(getNotificationLongClicker());
- mStackScroller.setGearDisplayedListener(getGearDisplayedListener());
mStackScroller.setPhoneStatusBar(this);
mStackScroller.setGroupManager(mGroupManager);
mStackScroller.setHeadsUpManager(mHeadsUpManager);
@@ -874,7 +877,7 @@
DensityContainer container = (DensityContainer) mStatusBarWindow.findViewById(
R.id.qs_density_container);
if (container != null) {
- final QSTileHost qsh = new QSTileHost(mContext, this,
+ final QSTileHost qsh = SystemUIFactory.getInstance().createQSTileHost(mContext, this,
mBluetoothController, mLocationController, mRotationLockController,
mNetworkController, mZenModeController, mHotspotController,
mCastController, mFlashlightController,
@@ -1108,6 +1111,18 @@
mStatusBarKeyguardViewManager);
mFingerprintUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
mRemoteInputController.addCallback(mStatusBarKeyguardViewManager);
+
+ if (FORCE_REMOTE_INPUT_HISTORY) {
+ mRemoteInputController.addCallback(new RemoteInputController.Callback() {
+ @Override
+ public void onRemoteInputSent(Entry entry) {
+ if (mKeysKeptForRemoteInput.contains(entry.key)) {
+ removeNotification(entry.key, null);
+ }
+ }
+ });
+ }
+
mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
mLightStatusBarController.setFingerprintUnlockController(mFingerprintUnlockController);
}
@@ -1376,6 +1391,42 @@
clearCurrentMediaNotification();
updateMediaMetaData(true, true);
}
+ if (FORCE_REMOTE_INPUT_HISTORY && mRemoteInputController.isSpinning(key)) {
+ Entry entry = mNotificationData.get(key);
+ StatusBarNotification sbn = entry.notification;
+
+ Notification.Builder b = Notification.Builder
+ .recoverBuilder(mContext, sbn.getNotification().clone());
+ CharSequence[] oldHistory = sbn.getNotification().extras
+ .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY);
+ CharSequence[] newHistory;
+ if (oldHistory == null) {
+ newHistory = new CharSequence[1];
+ } else {
+ newHistory = new CharSequence[oldHistory.length + 1];
+ for (int i = 0; i < oldHistory.length; i++) {
+ newHistory[i + 1] = oldHistory[i];
+ }
+ }
+ newHistory[0] = String.valueOf(entry.remoteInputText);
+ b.setRemoteInputHistory(newHistory);
+
+ Notification newNotification = b.build();
+
+ // Undo any compatibility view inflation
+ newNotification.contentView = sbn.getNotification().contentView;
+ newNotification.bigContentView = sbn.getNotification().bigContentView;
+ newNotification.headsUpContentView = sbn.getNotification().headsUpContentView;
+
+ StatusBarNotification newSbn = new StatusBarNotification(sbn.getPackageName(),
+ sbn.getOpPkg(),
+ sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
+ 0, newNotification, sbn.getUser(), sbn.getPostTime());
+
+ updateNotification(newSbn, null);
+ mKeysKeptForRemoteInput.add(entry.key);
+ return;
+ }
if (deferRemoval) {
mLatestRankingMap = ranking;
mHeadsUpEntriesToRemoveOnSwitch.add(mHeadsUpManager.getEntry(key));
@@ -3041,8 +3092,8 @@
null, mContext.getBasePackageName(),
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, null,
- UserHandle.CURRENT.getIdentifier());
+ null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null,
+ getActivityOptions(), UserHandle.CURRENT.getIdentifier());
} catch (RemoteException e) {
Log.w(TAG, "Unable to start activity", e);
}
@@ -3252,10 +3303,14 @@
protected void loadDimens() {
final Resources res = mContext.getResources();
+ int oldBarHeight = mNaturalBarHeight;
mNaturalBarHeight = res.getDimensionPixelSize(
com.android.internal.R.dimen.status_bar_height);
-
- mMaxAllowedKeyguardNotifications = res.getInteger(R.integer.keyguard_max_notification_count);
+ if (mStatusBarWindowManager != null && mNaturalBarHeight != oldBarHeight) {
+ mStatusBarWindowManager.setBarHeight(mNaturalBarHeight);
+ }
+ mMaxAllowedKeyguardNotifications = res.getInteger(
+ R.integer.keyguard_max_notification_count);
if (DEBUG) Log.v(TAG, "updateResources");
}
@@ -4142,6 +4197,12 @@
}
@Override
+ public void onLockedNotificationImportanceChange(OnDismissAction dismissAction) {
+ mLeaveOpenOnKeyguardHide = true;
+ dismissKeyguardThenExecute(dismissAction, true /* afterKeyguardGone */);
+ }
+
+ @Override
protected void onLockedRemoteInput(ExpandableNotificationRow row, View clicked) {
mLeaveOpenOnKeyguardHide = true;
showBouncer();
@@ -4309,6 +4370,7 @@
@Override
public void appTransitionCancelled() {
mIconController.appTransitionCancelled();
+ EventBus.getDefault().send(new AppTransitionFinishedEvent());
}
@Override
@@ -4325,6 +4387,11 @@
}
@Override
+ public void appTransitionFinished() {
+ EventBus.getDefault().send(new AppTransitionFinishedEvent());
+ }
+
+ @Override
public void onCameraLaunchGestureDetected(int source) {
mLastCameraLaunchSource = source;
if (mStartedGoingToSleep) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index c883cc9..0ac2e7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -137,7 +137,8 @@
filter.addAction(AudioManager.ACTION_HEADSET_PLUG);
filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED);
- filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED);
+ filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
+ filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
// listen for user / profile change.
@@ -507,7 +508,8 @@
updateSimState(intent);
} else if (action.equals(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED)) {
updateTTY(intent);
- } else if (action.equals(Intent.ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED)) {
+ } else if (action.equals(Intent.ACTION_MANAGED_PROFILE_AVAILABLE) ||
+ action.equals(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)) {
updateQuietState();
updateManagedProfile();
} else if (action.equals(AudioManager.ACTION_HEADSET_PLUG)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index 5dcd393..82496ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -81,7 +81,7 @@
import java.util.Map;
/** Platform implementation of the quick settings tile host **/
-public final class QSTileHost implements QSTile.Host, Tunable {
+public class QSTileHost implements QSTile.Host, Tunable {
private static final String TAG = "QSTileHost";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -450,7 +450,7 @@
}
}
- public static List<String> loadTileSpecs(Context context, String tileList) {
+ protected List<String> loadTileSpecs(Context context, String tileList) {
final Resources res = context.getResources();
final String defaultTileList = res.getString(R.string.quick_settings_tiles_default);
if (tileList == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
index 8f329c4..f3aba4f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
@@ -68,7 +68,7 @@
private ViewGroup mDateTimeAlarmGroup;
private TextView mEmergencyOnly;
- private ExpandableIndicator mExpandIndicator;
+ protected ExpandableIndicator mExpandIndicator;
private boolean mListening;
private AlarmManager.AlarmClockInfo mNextAlarm;
@@ -124,7 +124,6 @@
// RenderThread is doing more harm than good when touching the header (to expand quick
// settings), so disable it for this view
- ((RippleDrawable) getBackground()).setForceSoftware(true);
((RippleDrawable) mSettingsButton.getBackground()).setForceSoftware(true);
updateResources();
@@ -136,6 +135,12 @@
updateResources();
}
+ @Override
+ public void onRtlPropertiesChanged(int layoutDirection) {
+ super.onRtlPropertiesChanged(layoutDirection);
+ updateResources();
+ }
+
private void updateResources() {
FontSizeUtils.updateFontSize(mAlarmStatus, R.dimen.qs_date_collapsed_size);
FontSizeUtils.updateFontSize(mEmergencyOnly, R.dimen.qs_emergency_calls_only_text_size);
@@ -175,6 +180,20 @@
.addFloat(mMultiUserSwitch, "alpha", 0, 1)
.setStartDelay(QSAnimator.EXPANDED_TILE_DELAY)
.build();
+
+ final boolean isRtl = isLayoutRtl();
+ if (isRtl && mDateTimeGroup.getWidth() == 0) {
+ mDateTimeGroup.addOnLayoutChangeListener(new OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ mDateTimeGroup.setPivotX(getWidth());
+ mDateTimeGroup.removeOnLayoutChangeListener(this);
+ }
+ });
+ } else {
+ mDateTimeGroup.setPivotX(isRtl ? mDateTimeGroup.getWidth() : 0);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index b271380..888e19c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -323,6 +323,11 @@
apply(mCurrentState);
}
+ public void setBarHeight(int barHeight) {
+ mBarHeight = barHeight;
+ apply(mCurrentState);
+ }
+
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("StatusBarWindowManager state:");
pw.println(mCurrentState);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 95f26d4c..ebfa018 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -226,6 +226,10 @@
return false;
}
}
+ if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ mStackScrollLayout.closeControlsIfOutsideTouch(ev);
+ }
+
return super.dispatchTouchEvent(ev);
}
@@ -654,7 +658,7 @@
}
@Override
- public void onMultiWindowChanged() {
+ public void onMultiWindowModeChanged() {
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index 0442ac3..2783ec5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -37,22 +37,14 @@
super(context, theme);
mContext = context;
- getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL);
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
- | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+ applyFlags(this);
WindowManager.LayoutParams attrs = getWindow().getAttributes();
attrs.setTitle(getClass().getSimpleName());
getWindow().setAttributes(attrs);
}
public void setShowForAllUsers(boolean show) {
- if (show) {
- getWindow().getAttributes().privateFlags |=
- WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
- } else {
- getWindow().getAttributes().privateFlags &=
- ~WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
- }
+ setShowForAllUsers(this, show);
}
public void setMessage(int resId) {
@@ -66,4 +58,20 @@
public void setNegativeButton(int resId, OnClickListener onClick) {
setButton(BUTTON_NEGATIVE, mContext.getString(resId), onClick);
}
+
+ public static void setShowForAllUsers(AlertDialog dialog, boolean show) {
+ if (show) {
+ dialog.getWindow().getAttributes().privateFlags |=
+ WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ } else {
+ dialog.getWindow().getAttributes().privateFlags &=
+ ~WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ }
+ }
+
+ public static void applyFlags(AlertDialog dialog) {
+ dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL);
+ dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
+ | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 1f4ef4a..557f166 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -121,8 +121,11 @@
mEditText.setEnabled(false);
mSendButton.setVisibility(INVISIBLE);
mProgressBar.setVisibility(VISIBLE);
+ mEntry.remoteInputText = mEditText.getText();
+ mController.addSpinning(mEntry.key);
mController.removeRemoteInput(mEntry);
mEditText.mShowImeOnInputConnection = false;
+ mController.remoteInputSent(mEntry);
try {
mPendingIntent.send(mContext, 0, fillInIntent);
@@ -177,6 +180,7 @@
return;
}
mController.removeRemoteInput(mEntry);
+ mController.removeSpinning(mEntry.key);
}
public void setPendingIntent(PendingIntent pendingIntent) {
@@ -213,6 +217,7 @@
mEditText.setEnabled(true);
mSendButton.setVisibility(VISIBLE);
mProgressBar.setVisibility(INVISIBLE);
+ mController.removeSpinning(mEntry.key);
updateSendButton();
onDefocus();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index ab44b6a..ea0bdf2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -23,6 +23,7 @@
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -52,6 +53,7 @@
import com.android.systemui.BitmapHelper;
import com.android.systemui.GuestResumeSessionReceiver;
import com.android.systemui.R;
+import com.android.systemui.SystemUISecondaryUserService;
import com.android.systemui.qs.QSTile;
import com.android.systemui.qs.tiles.UserDetailView;
import com.android.systemui.statusbar.phone.ActivityStarter;
@@ -101,6 +103,8 @@
private boolean mSimpleUserSwitcher;
private boolean mAddUsersWhenLocked;
private boolean mPauseRefreshUsers;
+ private int mSecondaryUser = UserHandle.USER_NULL;
+ private Intent mSecondaryUserServiceIntent;
private SparseBooleanArray mForcePictureLoadForUserId = new SparseBooleanArray(2);
public UserSwitcherController(Context context, KeyguardMonitor keyguardMonitor,
@@ -121,6 +125,8 @@
mContext.registerReceiverAsUser(mReceiver, UserHandle.SYSTEM, filter,
null /* permission */, null /* scheduler */);
+ mSecondaryUserServiceIntent = new Intent(context, SystemUISecondaryUserService.class);
+
filter = new IntentFilter();
filter.addAction(ACTION_REMOVE_GUEST);
filter.addAction(ACTION_LOGOUT_USER);
@@ -477,6 +483,20 @@
}
notifyAdapters();
+ // Disconnect from the old secondary user's service
+ if (mSecondaryUser != UserHandle.USER_NULL) {
+ context.stopServiceAsUser(mSecondaryUserServiceIntent,
+ UserHandle.of(mSecondaryUser));
+ mSecondaryUser = UserHandle.USER_NULL;
+ }
+ // Connect to the new secondary user's service (purely to ensure that a persistent
+ // SystemUI application is created for that user)
+ if (userInfo != null && !userInfo.isPrimary()) {
+ context.startServiceAsUser(mSecondaryUserServiceIntent,
+ UserHandle.of(userInfo.id));
+ mSecondaryUser = userInfo.id;
+ }
+
if (UserManager.isSplitSystemUser() && userInfo != null && !userInfo.isGuest()
&& userInfo.id != UserHandle.USER_SYSTEM) {
showLogoutNotification(currentId);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index dc567fc..ee483e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -592,7 +592,7 @@
public float getGroupExpandFraction() {
int visibleChildrenExpandedHeight = getVisibleChildrenExpandHeight();
- int minExpandHeight = getMinExpandHeight();
+ int minExpandHeight = getCollapsedHeight();
float factor = (mActualHeight - minExpandHeight)
/ (float) (visibleChildrenExpandedHeight - minExpandHeight);
return Math.max(0.0f, Math.min(1.0f, factor));
@@ -618,11 +618,14 @@
}
public int getMinHeight() {
- return getIntrinsicHeight(NUMBER_OF_CHILDREN_WHEN_COLLAPSED);
+ return getMinHeight(NUMBER_OF_CHILDREN_WHEN_COLLAPSED);
}
- public int getMinExpandHeight() {
- int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* forceCollapsed */);
+ public int getCollapsedHeight() {
+ return getMinHeight(getMaxAllowedVisibleChildren(true /* forceCollapsed */));
+ }
+
+ private int getMinHeight(int maxAllowedVisibleChildren) {
int minExpandHeight = mNotificationHeaderHeight;
int visibleChildren = 0;
boolean firstChild = true;
@@ -637,7 +640,7 @@
firstChild = false;
}
ExpandableNotificationRow child = mChildren.get(i);
- minExpandHeight += child.getMinHeight();
+ minExpandHeight += child.getSingleLineView().getHeight();
visibleChildren++;
}
minExpandHeight += mCollapsedBottompadding;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 632ce21..fa37e22 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -60,6 +60,7 @@
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.ExpandableView;
+import com.android.systemui.statusbar.NotificationGuts;
import com.android.systemui.statusbar.NotificationOverflowContainer;
import com.android.systemui.statusbar.NotificationSettingsIconRow;
import com.android.systemui.statusbar.NotificationSettingsIconRow.SettingsIconRowListener;
@@ -220,7 +221,6 @@
*/
private int mMaxScrollAfterExpand;
private SwipeHelper.LongPressListener mLongPressListener;
- private GearDisplayedListener mGearDisplayedListener;
private NotificationSettingsIconRow mCurrIconRow;
private View mTranslatingParentView;
@@ -374,8 +374,12 @@
}
@Override
- public void onSettingsIconRowReset(NotificationSettingsIconRow row) {
- mSwipeHelper.setSnappedToGear(false);
+ public void onSettingsIconRowReset(ExpandableNotificationRow row) {
+ if (mTranslatingParentView != null && row == mTranslatingParentView) {
+ mSwipeHelper.setSnappedToGear(false);
+ mGearExposedView = null;
+ mTranslatingParentView = null;
+ }
}
@Override
@@ -669,10 +673,6 @@
mLongPressListener = listener;
}
- public void setGearDisplayedListener(GearDisplayedListener listener) {
- mGearDisplayedListener = listener;
- }
-
public void setQsContainer(ViewGroup qsContainer) {
mQsContainer = qsContainer;
}
@@ -737,17 +737,9 @@
// We start the swipe and snap back in the same frame, we don't want any animation
mDragAnimPendingChildren.remove(animView);
}
-
- if (mCurrIconRow != null) {
- if (targetLeft == 0) {
- mCurrIconRow.resetState();
- mCurrIconRow = null;
- if (mGearExposedView != null && mGearExposedView == mTranslatingParentView) {
- mGearExposedView = null;
- }
- } else {
- mSwipeHelper.setSnappedToGear(true);
- }
+ if (mCurrIconRow != null && targetLeft == 0) {
+ mCurrIconRow.resetState();
+ mCurrIconRow = null;
}
}
@@ -926,7 +918,7 @@
int positionInLinearLayout = getPositionInLinearLayout(v);
int targetScroll = positionInLinearLayout + expandableView.getActualHeight() +
- mBottomInset - getHeight() + getTopPadding();
+ getImeInset() - getHeight() + getTopPadding();
if (mOwnScrollY < targetScroll) {
mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScroll - mOwnScrollY);
mDontReportNextOverScroll = true;
@@ -936,8 +928,7 @@
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- mBottomInset = Math.max(0, insets.getSystemWindowInsetBottom()
- - (getRootView().getHeight() - getHeight()));
+ mBottomInset = insets.getSystemWindowInsetBottom();
int range = getScrollRange();
if (mOwnScrollY > range) {
@@ -995,7 +986,8 @@
}
public void dismissViewAnimated(View child, Runnable endRunnable, int delay, long duration) {
- mSwipeHelper.dismissChild(child, 0, endRunnable, delay, true, duration);
+ mSwipeHelper.dismissChild(child, 0, endRunnable, delay, true, duration,
+ true /* isDismissAll */);
}
public void snapViewIfNeeded(View child) {
@@ -1506,23 +1498,17 @@
}
private int getScrollRange() {
- int scrollRange = 0;
- ExpandableView firstChild = (ExpandableView) getFirstChildNotGone();
- if (firstChild != null) {
- int contentHeight = getContentHeight();
- scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight + mBottomStackPeekSize
- + mBottomStackSlowDownHeight);
- if (scrollRange > 0) {
- int firstChildMaxExpandHeight = getMaxExpandHeight(firstChild);
- // We want to at least be able collapse the first item and not ending in a weird
- // end state.
- scrollRange = Math.max(scrollRange, firstChildMaxExpandHeight
- - firstChild.getMinHeight());
- }
- }
- int imeOverlap = Math.max(0,
- getContentHeight() - (getHeight() - mBottomInset));
- return scrollRange + imeOverlap;
+ int contentHeight = getContentHeight();
+ int scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight + mBottomStackPeekSize
+ + mBottomStackSlowDownHeight);
+ int imeInset = getImeInset();
+ scrollRange += Math.min(imeInset, Math.max(0,
+ getContentHeight() - (getHeight() - imeInset)));
+ return scrollRange;
+ }
+
+ private int getImeInset() {
+ return Math.max(0, mBottomInset - (getRootView().getHeight() - getHeight()));
}
/**
@@ -1947,7 +1933,7 @@
public int getPeekHeight() {
final ExpandableView firstChild = getFirstChildNotGone();
- final int firstChildMinHeight = firstChild != null ? (int) firstChild.getMinHeight()
+ final int firstChildMinHeight = firstChild != null ? firstChild.getCollapsedHeight()
: mCollapsedSize;
return mIntrinsicPadding + firstChildMinHeight + mBottomStackPeekSize
+ mBottomStackSlowDownHeight;
@@ -2063,6 +2049,14 @@
}
}
+ @Override
+ public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+ super.requestDisallowInterceptTouchEvent(disallowIntercept);
+ if (disallowIntercept) {
+ mSwipeHelper.removeLongPressCallback();
+ }
+ }
+
private void onViewRemovedInternal(View child) {
if (mChangePositionInProgress) {
// This is only a position change, don't do anything special
@@ -3201,9 +3195,6 @@
disableClipOptimization();
}
handleDismissAllClipping();
- if (mCurrIconRow != null && mCurrIconRow.isVisible()) {
- mCurrIconRow.getNotificationParent().animateTranslateNotification(0 /* left target */);
- }
}
private void handleDismissAllClipping() {
@@ -3462,16 +3453,11 @@
public void flingTopOverscroll(float velocity, boolean open);
}
- /**
- * A listener that is notified when the gear is shown behind a notification.
- */
- public interface GearDisplayedListener {
- void onGearDisplayed(ExpandableNotificationRow row);
- }
-
private class NotificationSwipeHelper extends SwipeHelper {
- private static final long GEAR_SHOW_DELAY = 60;
+ private static final long SHOW_GEAR_DELAY = 60;
+ private static final long COVER_GEAR_DELAY = 4000;
private CheckForDrag mCheckForDrag;
+ private Runnable mFalsingCheck;
private Handler mHandler;
private boolean mGearSnappedTo;
private boolean mGearSnappedOnLeft;
@@ -3479,6 +3465,12 @@
public NotificationSwipeHelper(int swipeDirection, Callback callback, Context context) {
super(swipeDirection, callback, context);
mHandler = new Handler();
+ mFalsingCheck = new Runnable() {
+ @Override
+ public void run() {
+ resetExposedGearView(true /* animate */, true /* force */);
+ }
+ };
}
@Override
@@ -3493,9 +3485,10 @@
}
mCheckForDrag = null;
mCurrIconRow = null;
+ mHandler.removeCallbacks(mFalsingCheck);
// Slide back any notifications that might be showing a gear
- resetExposedGearView();
+ resetExposedGearView(true /* animate */, false /* force */);
if (currView instanceof ExpandableNotificationRow) {
// Set the listener for the current row's gear
@@ -3506,6 +3499,8 @@
@Override
public void onMoveUpdate(View view, float translation, float delta) {
+ mHandler.removeCallbacks(mFalsingCheck);
+
if (mCurrIconRow != null) {
mCurrIconRow.setSnapping(false); // If we're moving, we're not snapping.
@@ -3524,7 +3519,8 @@
} else {
// Check scheduled, reset alpha and update location; check will fade it in
mCurrIconRow.setGearAlpha(0f);
- mCurrIconRow.setIconLocation(translation > 0 /* onLeft */);
+ mCurrIconRow.setIconLocation(translation > 0 /* onLeft */,
+ false /* force */);
}
}
}
@@ -3539,10 +3535,10 @@
}
@Override
- public void dismissChild(final View view, float velocity) {
- super.dismissChild(view, velocity);
- cancelCheckForDrag();
- setSnappedToGear(false);
+ public void dismissChild(final View view, float velocity,
+ boolean useAccelerateInterpolator) {
+ super.dismissChild(view, velocity, useAccelerateInterpolator);
+ handleGearCoveredOrDismissed();
}
@Override
@@ -3550,11 +3546,17 @@
super.snapChild(animView, targetLeft, velocity);
onDragCancelled(animView);
if (targetLeft == 0) {
- cancelCheckForDrag();
- setSnappedToGear(false);
+ handleGearCoveredOrDismissed();
}
}
+ private void handleGearCoveredOrDismissed() {
+ cancelCheckForDrag();
+ setSnappedToGear(false);
+ if (mGearExposedView != null && mGearExposedView == mTranslatingParentView) {
+ mGearExposedView = null;
+ }
+ }
@Override
public boolean handleUpEvent(MotionEvent ev, View animView, float velocity,
@@ -3576,7 +3578,8 @@
snapChild(animView, 0 /* leftTarget */, velocity);
} else if (isDismissGesture(ev)) {
// Gesture is a dismiss that's not towards the gear
- dismissChild(animView, swipedFastEnough() ? velocity : 0f);
+ dismissChild(animView, velocity,
+ !swipedFastEnough() /* useAccelerateInterpolator */);
} else {
// Didn't move enough to dismiss or cover, snap to the gear
snapToGear(animView, velocity);
@@ -3601,7 +3604,8 @@
private void dismissOrSnapBack(View animView, float velocity, MotionEvent ev) {
if (isDismissGesture(ev)) {
- dismissChild(animView, swipedFastEnough() ? velocity : 0f);
+ dismissChild(animView, velocity,
+ !swipedFastEnough() /* useAccelerateInterpolator */);
} else {
snapChild(animView, 0 /* leftTarget */, velocity);
}
@@ -3612,19 +3616,29 @@
final float target = mCurrIconRow.isIconOnLeft() ? snapBackThreshold
: -snapBackThreshold;
mGearExposedView = mTranslatingParentView;
- if (mGearDisplayedListener != null
- && (animView instanceof ExpandableNotificationRow)) {
- mGearDisplayedListener.onGearDisplayed((ExpandableNotificationRow) animView);
+ if (animView instanceof ExpandableNotificationRow) {
+ MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR,
+ ((ExpandableNotificationRow) animView).getStatusBarNotification()
+ .getPackageName());
}
if (mCurrIconRow != null) {
mCurrIconRow.setSnapping(true);
setSnappedToGear(true);
}
onDragCancelled(animView);
+
+ // If we're on the lockscreen we want to false this.
+ if (mPhoneStatusBar.getBarState() == StatusBarState.KEYGUARD) {
+ mHandler.removeCallbacks(mFalsingCheck);
+ mHandler.postDelayed(mFalsingCheck, COVER_GEAR_DELAY);
+ }
super.snapChild(animView, target, velocity);
}
private boolean swipedEnoughToShowGear(View animView) {
+ if (mTranslatingParentView == null) {
+ return false;
+ }
final float snapBackThreshold = getSpaceForGear(animView);
final float translation = getTranslation(animView);
final boolean fromLeft = translation > 0;
@@ -3640,10 +3654,7 @@
@Override
public Animator getViewTranslationAnimator(View v, float target,
AnimatorUpdateListener listener) {
- if (mDismissAllInProgress) {
- // When dismissing all, we translate the entire view instead.
- return super.getViewTranslationAnimator(v, target, listener);
- } else if (v instanceof ExpandableNotificationRow) {
+ if (v instanceof ExpandableNotificationRow) {
return ((ExpandableNotificationRow) v).getTranslateViewAnimator(target, listener);
} else {
return super.getViewTranslationAnimator(v, target, listener);
@@ -3652,21 +3663,42 @@
@Override
public void setTranslation(View v, float translate) {
- if (mDismissAllInProgress) {
- // When dismissing all, we translate the entire view instead.
- super.setTranslation(v, translate);
- } else {
- ((ExpandableView) v).setTranslation(translate);
- }
+ ((ExpandableView) v).setTranslation(translate);
}
@Override
public float getTranslation(View v) {
- if (mDismissAllInProgress) {
- // When dismissing all, we translate the entire view instead.
- return super.getTranslation(v);
- } else {
- return ((ExpandableView) v).getTranslation();
+ return ((ExpandableView) v).getTranslation();
+ }
+
+ public void closeControlsIfOutsideTouch(MotionEvent ev) {
+ NotificationGuts guts = mPhoneStatusBar.getExposedGuts();
+ View view = null;
+ int height = 0;
+ if (guts != null) {
+ // Checking guts
+ view = guts;
+ height = guts.getActualHeight();
+ } else if (mCurrIconRow != null && mCurrIconRow.isVisible()
+ && mTranslatingParentView != null) {
+ // Checking gear
+ view = mTranslatingParentView;
+ height = ((ExpandableView) mTranslatingParentView).getActualHeight();
+ }
+ if (view != null) {
+ final int rx = (int) ev.getRawX();
+ final int ry = (int) ev.getRawY();
+
+ getLocationOnScreen(mTempInt2);
+ int[] location = new int[2];
+ view.getLocationOnScreen(location);
+ final int x = location[0] - mTempInt2[0];
+ final int y = location[1] - mTempInt2[1];
+ Rect rect = new Rect(x, y, x + view.getWidth(), y + height);
+ if (!rect.contains((int) rx, (int) ry)) {
+ // Touch was outside visible guts / gear notification, close what's visible
+ mPhoneStatusBar.dismissPopups(-1, -1, true /* resetGear */, true /* animate */);
+ }
}
}
@@ -3703,7 +3735,7 @@
private void checkForDrag() {
if (mCheckForDrag == null || !mHandler.hasCallbacks(mCheckForDrag)) {
mCheckForDrag = new CheckForDrag();
- mHandler.postDelayed(mCheckForDrag, GEAR_SHOW_DELAY);
+ mHandler.postDelayed(mCheckForDrag, SHOW_GEAR_DELAY);
}
}
@@ -3717,6 +3749,9 @@
private final class CheckForDrag implements Runnable {
@Override
public void run() {
+ if (mTranslatingParentView == null) {
+ return;
+ }
final float translation = getTranslation(mTranslatingParentView);
final float absTransX = Math.abs(translation);
final float bounceBackToGearWidth = getSpaceForGear(mTranslatingParentView);
@@ -3732,20 +3767,24 @@
}
}
- private void resetExposedGearView() {
- if (mGearExposedView == null || mGearExposedView == mTranslatingParentView) {
+ public void resetExposedGearView(boolean animate, boolean force) {
+ if (mGearExposedView == null
+ || (!force && mGearExposedView == mTranslatingParentView)) {
// If no gear is showing or it's showing for this view we do nothing.
return;
}
-
final View prevGearExposedView = mGearExposedView;
+ if (animate) {
+ Animator anim = getViewTranslationAnimator(prevGearExposedView,
+ 0 /* leftTarget */, null /* updateListener */);
+ if (anim != null) {
+ anim.start();
+ }
+ } else if (mGearExposedView instanceof ExpandableNotificationRow) {
+ ((ExpandableNotificationRow) mGearExposedView).resetTranslation();
+ }
mGearExposedView = null;
mGearSnappedTo = false;
- Animator anim = getViewTranslationAnimator(prevGearExposedView,
- 0 /* leftTarget */, null /* updateListener */);
- if (anim != null) {
- anim.start();
- }
}
}
@@ -3761,6 +3800,14 @@
}
}
+ public void resetExposedGearView(boolean animate, boolean force) {
+ mSwipeHelper.resetExposedGearView(animate, force);
+ }
+
+ public void closeControlsIfOutsideTouch(MotionEvent ev) {
+ mSwipeHelper.closeControlsIfOutsideTouch(ev);
+ }
+
static class AnimationEvent {
static AnimationFilter[] FILTERS = new AnimationFilter[] {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index 4c94fe9..c7333c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -368,7 +368,7 @@
childViewState.location = StackViewState.LOCATION_UNKNOWN;
paddingAfterChild = getPaddingAfterChild(algorithmState, child);
int childHeight = getMaxAllowedChildHeight(child);
- int minHeight = child.getMinHeight();
+ int collapsedHeight = child.getCollapsedHeight();
childViewState.yTranslation = currentYPosition;
if (i == 0) {
updateFirstChildHeight(child, childViewState, childHeight, ambientState);
@@ -384,7 +384,7 @@
// According to the regular scroll view we are fully translated out of the
// bottom of the screen so we are fully in the bottom stack
updateStateForChildFullyInBottomStack(algorithmState,
- bottomStackStart, childViewState, minHeight, ambientState, child);
+ bottomStackStart, childViewState, collapsedHeight, ambientState, child);
} else {
// According to the regular scroll view we are currently translating out of /
// into the bottom of the screen
@@ -475,17 +475,17 @@
float newTranslation = Math.max(ambientState.getTopPadding()
+ ambientState.getStackTranslation(), childState.yTranslation);
childState.height = (int) Math.max(childState.height - (newTranslation
- - childState.yTranslation), row.getMinHeight());
+ - childState.yTranslation), row.getCollapsedHeight());
childState.yTranslation = newTranslation;
}
private void clampHunToMaxTranslation(AmbientState ambientState, ExpandableNotificationRow row,
StackViewState childState) {
float newTranslation;
- float bottomPosition = ambientState.getMaxHeadsUpTranslation() - row.getMinHeight();
+ float bottomPosition = ambientState.getMaxHeadsUpTranslation() - row.getCollapsedHeight();
newTranslation = Math.min(childState.yTranslation, bottomPosition);
childState.height = (int) Math.max(childState.height
- - (childState.yTranslation - newTranslation), row.getMinHeight());
+ - (childState.yTranslation - newTranslation), row.getCollapsedHeight());
childState.yTranslation = newTranslation;
}
@@ -534,10 +534,10 @@
float offset = mBottomStackIndentationFunctor.getValue(algorithmState.partialInBottom);
algorithmState.itemsInBottomStack += algorithmState.partialInBottom;
int newHeight = childHeight;
- if (childHeight > child.getMinHeight()) {
+ if (childHeight > child.getCollapsedHeight()) {
newHeight = (int) Math.max(Math.min(transitioningPositionStart + offset -
getPaddingAfterChild(algorithmState, child) - currentYPosition, childHeight),
- child.getMinHeight());
+ child.getCollapsedHeight());
childViewState.height = newHeight;
}
childViewState.yTranslation = transitioningPositionStart + offset - newHeight
@@ -547,7 +547,7 @@
private void updateStateForChildFullyInBottomStack(StackScrollAlgorithmState algorithmState,
float transitioningPositionStart, StackViewState childViewState,
- int minHeight, AmbientState ambientState, ExpandableView child) {
+ int collapsedHeight, AmbientState ambientState, ExpandableView child) {
float currentYPosition;
algorithmState.itemsInBottomStack += 1.0f;
if (algorithmState.itemsInBottomStack < MAX_ITEMS_IN_BOTTOM_STACK) {
@@ -568,16 +568,14 @@
childViewState.location = StackViewState.LOCATION_BOTTOM_STACK_HIDDEN;
currentYPosition = ambientState.getInnerHeight();
}
- childViewState.height = minHeight;
- childViewState.yTranslation = currentYPosition - minHeight;
+ childViewState.height = collapsedHeight;
+ childViewState.yTranslation = currentYPosition - collapsedHeight;
}
/**
* Update the height of the first child i.e clamp it to the bottom stack
*
- *
-
* @param child the child to update
* @param childViewState the viewstate of the child
* @param childHeight the height of the child
@@ -591,7 +589,7 @@
mBottomStackSlowDownLength + ambientState.getScrollY();
// Collapse and expand the first child while the shade is being expanded
childViewState.height = (int) Math.max(Math.min(bottomPeekStart, (float) childHeight),
- child.getMinHeight());
+ child.getCollapsedHeight());
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 2524e1a..f9bb5e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -171,6 +171,10 @@
}
@Override
+ public void appTransitionFinished() {
+ }
+
+ @Override
public void onCameraLaunchGestureDetected(int source) {
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/NightModeFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/NightModeFragment.java
index 8c945f9..ae2856c 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/NightModeFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/NightModeFragment.java
@@ -43,7 +43,6 @@
public static final String EXTRA_SHOW_NIGHT_MODE = "show_night_mode";
private static final CharSequence KEY_AUTO = "auto";
- private static final CharSequence KEY_DARK_THEME = "dark_theme";
private static final CharSequence KEY_ADJUST_TINT = "adjust_tint";
private static final CharSequence KEY_ADJUST_BRIGHTNESS = "adjust_brightness";
@@ -51,7 +50,6 @@
private NightModeController mNightModeController;
private SwitchPreference mAutoSwitch;
- private SwitchPreference mDarkTheme;
private SwitchPreference mAdjustTint;
private SwitchPreference mAdjustBrightness;
private UiModeManager mUiModeManager;
@@ -79,8 +77,6 @@
addPreferencesFromResource(R.xml.night_mode);
mAutoSwitch = (SwitchPreference) findPreference(KEY_AUTO);
mAutoSwitch.setOnPreferenceChangeListener(this);
- mDarkTheme = (SwitchPreference) findPreference(KEY_DARK_THEME);
- mDarkTheme.setOnPreferenceChangeListener(this);
mAdjustTint = (SwitchPreference) findPreference(KEY_ADJUST_TINT);
mAdjustTint.setOnPreferenceChangeListener(this);
mAdjustBrightness = (SwitchPreference) findPreference(KEY_ADJUST_BRIGHTNESS);
@@ -111,7 +107,6 @@
mNightModeController.addListener(this);
TunerService.get(getContext()).addTunable(this, Secure.BRIGHTNESS_USE_TWILIGHT,
NightModeController.NIGHT_MODE_ADJUST_TINT);
- mDarkTheme.setChecked(mUiModeManager.getNightMode() == UiModeManager.MODE_NIGHT_AUTO);
calculateDisabled();
}
@@ -129,12 +124,6 @@
if (mAutoSwitch == preference) {
MetricsLogger.action(getContext(), MetricsEvent.ACTION_TUNER_NIGHT_MODE_AUTO, value);
mNightModeController.setAuto(value);
- } else if (mDarkTheme == preference) {
- MetricsLogger.action(getContext(),
- MetricsEvent.ACTION_TUNER_NIGHT_MODE_ADJUST_DARK_THEME, value);
- mUiModeManager.setNightMode(value ? UiModeManager.MODE_NIGHT_AUTO
- : UiModeManager.MODE_NIGHT_NO);
- postCalculateDisabled();
} else if (mAdjustTint == preference) {
MetricsLogger.action(getContext(),
MetricsEvent.ACTION_TUNER_NIGHT_MODE_ADJUST_TINT, value);
@@ -163,19 +152,15 @@
}
private void calculateDisabled() {
- int enabledCount = (mDarkTheme.isChecked() ? 1 : 0)
- + (mAdjustTint.isChecked() ? 1 : 0)
+ int enabledCount = (mAdjustTint.isChecked() ? 1 : 0)
+ (mAdjustBrightness.isChecked() ? 1 : 0);
if (enabledCount == 1) {
- if (mDarkTheme.isChecked()) {
- mDarkTheme.setEnabled(false);
- } else if (mAdjustTint.isChecked()) {
+ if (mAdjustTint.isChecked()) {
mAdjustTint.setEnabled(false);
} else {
mAdjustBrightness.setEnabled(false);
}
} else {
- mDarkTheme.setEnabled(true);
mAdjustTint.setEnabled(true);
mAdjustBrightness.setEnabled(true);
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/NightModeTile.java b/packages/SystemUI/src/com/android/systemui/tuner/NightModeTile.java
index 26e1d46..fe44502 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/NightModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/NightModeTile.java
@@ -15,10 +15,7 @@
*/
package com.android.systemui.tuner;
-import android.app.ActivityManager;
import android.content.Intent;
-import android.provider.Settings;
-
import com.android.internal.logging.MetricsProto.MetricsEvent;
import com.android.systemui.Prefs;
import com.android.systemui.Prefs.Key;
@@ -26,8 +23,6 @@
import com.android.systemui.qs.QSTile;
import com.android.systemui.statusbar.policy.NightModeController;
-import java.util.Objects;
-
public class NightModeTile extends QSTile<QSTile.State> implements NightModeController.Listener {
@@ -81,6 +76,11 @@
}
@Override
+ public CharSequence getTileLabel() {
+ return mContext.getString(R.string.night_mode);
+ }
+
+ @Override
protected void handleUpdateState(State state, Object arg) {
// TODO: Right now this is just a dropper, needs an actual night icon.
boolean enabled = mNightModeController.isEnabled();
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java
new file mode 100644
index 0000000..3f87611
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.tv.pip;
+
+import android.content.Context;
+import android.media.session.MediaController;
+import android.media.session.PlaybackState;
+import android.view.View;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View.OnFocusChangeListener;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.LinearLayout;
+import android.util.AttributeSet;
+
+import com.android.systemui.R;
+
+import static android.media.session.PlaybackState.ACTION_PAUSE;
+import static android.media.session.PlaybackState.ACTION_PLAY;
+
+import static com.android.systemui.tv.pip.PipManager.PLAYBACK_STATE_PLAYING;
+import static com.android.systemui.tv.pip.PipManager.PLAYBACK_STATE_PAUSED;
+import static com.android.systemui.tv.pip.PipManager.PLAYBACK_STATE_UNAVAILABLE;
+
+
+/**
+ * A view containing PIP controls including fullscreen, close, and media controls.
+ */
+public class PipControlsView extends LinearLayout {
+ /**
+ * An interface to listen user action.
+ */
+ public abstract static interface Listener {
+ /**
+ * Called when an user clicks close PIP button.
+ */
+ public abstract void onClosed();
+ };
+
+ private MediaController mMediaController;
+
+ final PipManager mPipManager = PipManager.getInstance();
+ Listener mListener;
+
+ View mFullButtonView;
+ View mFullDescriptionView;
+ View mPlayPauseView;
+ ImageView mPlayPauseButtonImageView;
+ TextView mPlayPauseDescriptionTextView;
+ View mCloseButtonView;
+ View mCloseDescriptionView;
+
+ private boolean mHasFocus;
+ private OnFocusChangeListener mOnChildFocusChangeListener;
+
+ private MediaController.Callback mMediaControllerCallback = new MediaController.Callback() {
+ @Override
+ public void onPlaybackStateChanged(PlaybackState state) {
+ updatePlayPauseView();
+ }
+ };
+
+ private PipManager.MediaListener mPipMediaListener = new PipManager.MediaListener() {
+ @Override
+ public void onMediaControllerChanged() {
+ updateMediaController();
+ }
+ };
+
+ public PipControlsView(Context context) {
+ this(context, null, 0, 0);
+ }
+
+ public PipControlsView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0, 0);
+ }
+
+ public PipControlsView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public PipControlsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ LayoutInflater inflater = (LayoutInflater) getContext()
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ inflater.inflate(R.layout.tv_pip_controls, this);
+
+ setOrientation(LinearLayout.HORIZONTAL);
+ setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL);
+ }
+
+ @Override
+ public void onFinishInflate() {
+ super.onFinishInflate();
+
+ mFullButtonView = findViewById(R.id.full_button);
+ mFullDescriptionView = findViewById(R.id.full_desc);
+ mFullButtonView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mPipManager.movePipToFullscreen();
+ }
+ });
+ mFullButtonView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ mFullDescriptionView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
+ onChildViewFocusChanged();
+ }
+ });
+
+ mPlayPauseView = findViewById(R.id.play_pause);
+ mPlayPauseButtonImageView = (ImageView) findViewById(R.id.play_pause_button);
+ mPlayPauseDescriptionTextView = (TextView) findViewById(R.id.play_pause_desc);
+ mPlayPauseButtonImageView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mMediaController == null || mMediaController.getPlaybackState() == null) {
+ return;
+ }
+ long actions = mMediaController.getPlaybackState().getActions();
+ int state = mMediaController.getPlaybackState().getState();
+ if (mPipManager.getPlaybackState() == PLAYBACK_STATE_PAUSED) {
+ mMediaController.getTransportControls().play();
+ } else if (mPipManager.getPlaybackState() == PLAYBACK_STATE_PLAYING) {
+ mMediaController.getTransportControls().pause();
+ }
+ // View will be updated later in {@link mMediaControllerCallback}
+ }
+ });
+ mPlayPauseButtonImageView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ mPlayPauseDescriptionTextView.setVisibility(
+ hasFocus ? View.VISIBLE : View.INVISIBLE);
+ onChildViewFocusChanged();
+ }
+ });
+
+ mCloseButtonView = findViewById(R.id.close_button);
+ mCloseDescriptionView = findViewById(R.id.close_desc);
+ mCloseButtonView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mPipManager.closePip();
+ if (mListener != null) {
+ mListener.onClosed();
+ }
+ }
+ });
+ mCloseButtonView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ mCloseDescriptionView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
+ onChildViewFocusChanged();
+ }
+ });
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ updateMediaController();
+ mPipManager.addMediaListener(mPipMediaListener);
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mPipManager.removeMediaListener(mPipMediaListener);
+ if (mMediaController != null) {
+ mMediaController.unregisterCallback(mMediaControllerCallback);
+ }
+ }
+
+ private void updateMediaController() {
+ MediaController newController = mPipManager.getMediaController();
+ if (mMediaController == newController) {
+ return;
+ }
+ if (mMediaController != null) {
+ mMediaController.unregisterCallback(mMediaControllerCallback);
+ }
+ mMediaController = newController;
+ if (mMediaController != null) {
+ mMediaController.registerCallback(mMediaControllerCallback);
+ }
+ updatePlayPauseView();
+ }
+
+ private void updatePlayPauseView() {
+ int state = mPipManager.getPlaybackState();
+ if (state == PLAYBACK_STATE_UNAVAILABLE) {
+ mPlayPauseView.setVisibility(View.GONE);
+ } else {
+ mPlayPauseView.setVisibility(View.VISIBLE);
+ if (state == PLAYBACK_STATE_PLAYING) {
+ mPlayPauseButtonImageView.setImageResource(R.drawable.tv_pip_pause_button);
+ mPlayPauseDescriptionTextView.setText(R.string.pip_pause);
+ } else {
+ mPlayPauseButtonImageView.setImageResource(R.drawable.tv_pip_play_button);
+ mPlayPauseDescriptionTextView.setText(R.string.pip_play);
+ }
+ }
+ }
+
+ /**
+ * Sets a listener to be invoked when {@link android.view.View.hasFocus()} is changed.
+ */
+ public void setOnChildFocusChangeListener(OnFocusChangeListener listener) {
+ mOnChildFocusChangeListener = listener;
+ }
+
+ private void onChildViewFocusChanged() {
+ // At this moment, hasFocus() returns true although there's no focused child.
+ boolean hasFocus = (mFullButtonView != null && mFullButtonView.isFocused())
+ || (mPlayPauseButtonImageView != null && mPlayPauseButtonImageView.isFocused())
+ || (mCloseButtonView != null && mCloseButtonView.isFocused());
+ if (mHasFocus != hasFocus) {
+ mHasFocus = hasFocus;
+ if (mOnChildFocusChangeListener != null) {
+ mOnChildFocusChangeListener.onFocusChange(getFocusedChild(), mHasFocus);
+ }
+ }
+ }
+
+ /**
+ * Sets the {@link Listener} to listen user actions.
+ */
+ public void setListener(Listener listener) {
+ mListener = listener;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
index acb1a7f..fe54090 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
@@ -30,6 +30,7 @@
import android.graphics.Rect;
import android.media.session.MediaController;
import android.media.session.MediaSessionManager;
+import android.media.session.PlaybackState;
import android.os.Debug;
import android.os.Handler;
import android.os.RemoteException;
@@ -37,6 +38,7 @@
import android.util.Log;
import com.android.systemui.Prefs;
+import com.android.systemui.R;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;
@@ -60,9 +62,27 @@
private static final int MAX_RUNNING_TASKS_COUNT = 10;
+ /**
+ * State when there's no PIP.
+ */
public static final int STATE_NO_PIP = 0;
+ /**
+ * State when PIP is shown with an overlay message on top of it.
+ * This is used as default PIP state.
+ */
public static final int STATE_PIP_OVERLAY = 1;
+ /**
+ * State when PIP menu dialog is shown.
+ */
public static final int STATE_PIP_MENU = 2;
+ /**
+ * State when PIP is shown in Recents.
+ */
+ public static final int STATE_PIP_RECENTS = 3;
+ /**
+ * State when PIP is shown in Recents and it's focused to allow an user to control.
+ */
+ public static final int STATE_PIP_RECENTS_FOCUSED = 4;
private static final int TASK_ID_NO_PIP = -1;
private static final int INVALID_RESOURCE_TYPE = -1;
@@ -70,32 +90,43 @@
public static final int SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH = 0x1;
public static final int SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_OVERLAY_ACTIVITY_FINISH = 0x2;
+ /**
+ * PIPed activity is playing a media and it can be paused.
+ */
+ static final int PLAYBACK_STATE_PLAYING = 0;
+ /**
+ * PIPed activity has a paused media and it can be played.
+ */
+ static final int PLAYBACK_STATE_PAUSED = 1;
+ /**
+ * Users are unable to control PIPed activity's media playback.
+ */
+ static final int PLAYBACK_STATE_UNAVAILABLE = 2;
+
private static final int CLOSE_PIP_WHEN_MEDIA_SESSION_GONE_TIMEOUT_MS = 3000;
private int mSuspendPipResizingReason;
- private static final float SCALE_FACTOR = 1.1f;
-
private Context mContext;
+ private PipRecentsOverlayManager mPipRecentsOverlayManager;
private IActivityManager mActivityManager;
private MediaSessionManager mMediaSessionManager;
private int mState = STATE_NO_PIP;
private final Handler mHandler = new Handler();
private List<Listener> mListeners = new ArrayList<>();
+ private List<MediaListener> mMediaListeners = new ArrayList<>();
private Rect mCurrentPipBounds;
private Rect mPipBounds;
private Rect mMenuModePipBounds;
private Rect mRecentsPipBounds;
private Rect mRecentsFocusedPipBounds;
+ private int mRecentsFocusChangedAnimationDurationMs;
private boolean mInitialized;
private int mPipTaskId = TASK_ID_NO_PIP;
private ComponentName mPipComponentName;
private MediaController mPipMediaController;
private boolean mOnboardingShown;
- private boolean mIsRecentsShown;
- private boolean mIsPipFocusedInRecent;
-
private final Runnable mResizePinnedStackRunnable = new Runnable() {
@Override
public void run() {
@@ -148,15 +179,13 @@
mPipBounds = Rect.unflattenFromString(res.getString(
com.android.internal.R.string.config_defaultPictureInPictureBounds));
mMenuModePipBounds = Rect.unflattenFromString(res.getString(
- com.android.internal.R.string.config_centeredPictureInPictureBounds));
+ R.string.pip_menu_bounds));
mRecentsPipBounds = Rect.unflattenFromString(res.getString(
- com.android.internal.R.string.config_pictureInPictureBoundsInRecents));
- float scaleBy = (SCALE_FACTOR - 1.0f) / 2;
- mRecentsFocusedPipBounds = new Rect(
- (int) (mRecentsPipBounds.left - scaleBy * mRecentsPipBounds.width()),
- (int) (mRecentsPipBounds.top - scaleBy * mRecentsPipBounds.height()),
- (int) (mRecentsPipBounds.right + scaleBy * mRecentsPipBounds.width()),
- (int) (mRecentsPipBounds.bottom + scaleBy * mRecentsPipBounds.height()));
+ R.string.pip_recents_bounds));
+ mRecentsFocusedPipBounds = Rect.unflattenFromString(res.getString(
+ R.string.pip_recents_focused_bounds));
+ mRecentsFocusChangedAnimationDurationMs = res.getInteger(
+ R.integer.recents_tv_pip_focus_anim_duration);
mActivityManager = ActivityManagerNative.getDefault();
SystemServicesProxy.getInstance(context).registerTaskStackListener(mTaskStackListener);
@@ -166,6 +195,7 @@
mOnboardingShown = Prefs.getBoolean(
mContext, TV_PICTURE_IN_PICTURE_ONBOARDING_SHOWN, false);
+ mPipRecentsOverlayManager = new PipRecentsOverlayManager(context);
mMediaSessionManager =
(MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
}
@@ -219,7 +249,7 @@
/**
* Moves the PIPed activity to the fullscreen and closes PIP system UI.
*/
- public void movePipToFullscreen() {
+ void movePipToFullscreen() {
mState = STATE_NO_PIP;
mPipTaskId = TASK_ID_NO_PIP;
for (int i = mListeners.size() - 1; i >= 0; --i) {
@@ -235,7 +265,6 @@
*/
private void showPipOverlay() {
if (DEBUG) Log.d(TAG, "showPipOverlay()");
- mState = STATE_PIP_OVERLAY;
PipOverlayActivity.showPipOverlay(mContext);
}
@@ -267,8 +296,10 @@
* Resize the Pip to the appropriate size for the input state.
* @param state In Pip state also used to determine the new size for the Pip.
*/
- public void resizePinnedStack(int state) {
+ void resizePinnedStack(int state) {
if (DEBUG) Log.d(TAG, "resizePinnedStack() state=" + state);
+ boolean wasRecentsShown =
+ (mState == STATE_PIP_RECENTS || mState == STATE_PIP_RECENTS_FOCUSED);
mState = state;
for (int i = mListeners.size() - 1; i >= 0; --i) {
mListeners.get(i).onPipResizeAboutToStart();
@@ -287,89 +318,56 @@
mCurrentPipBounds = mMenuModePipBounds;
break;
case STATE_PIP_OVERLAY:
- if (mIsRecentsShown) {
- if (mIsPipFocusedInRecent) {
- mCurrentPipBounds = mRecentsFocusedPipBounds;
- } else {
- mCurrentPipBounds = mRecentsPipBounds;
- }
- } else {
- mCurrentPipBounds = mPipBounds;
- }
+ mCurrentPipBounds = mPipBounds;
+ break;
+ case STATE_PIP_RECENTS:
+ mCurrentPipBounds = mRecentsPipBounds;
+ break;
+ case STATE_PIP_RECENTS_FOCUSED:
+ mCurrentPipBounds = mRecentsFocusedPipBounds;
break;
default:
mCurrentPipBounds = mPipBounds;
break;
}
try {
- mActivityManager.resizeStack(PINNED_STACK_ID, mCurrentPipBounds, true, true, true, -1);
+ int animationDurationMs = -1;
+ if (wasRecentsShown
+ && (mState == STATE_PIP_RECENTS || mState == STATE_PIP_RECENTS_FOCUSED)) {
+ animationDurationMs = mRecentsFocusChangedAnimationDurationMs;
+ }
+ mActivityManager.resizeStack(PINNED_STACK_ID, mCurrentPipBounds,
+ true, true, true, animationDurationMs);
} catch (RemoteException e) {
Log.e(TAG, "resizeStack failed", e);
}
}
/**
- * Returns the current PIP bound for activities to sync their UI with PIP.
+ * Returns the default PIP bound.
*/
public Rect getPipBounds() {
- return mCurrentPipBounds;
+ return mPipBounds;
}
/**
- * Called when Recents is started.
- * PIPed activity will be resized accordingly and overlay will show available buttons.
+ * Returns the focused PIP bound while Recents is shown.
+ * This is used to place PIP controls in Recents.
*/
- public void onRecentsStarted() {
- mIsRecentsShown = true;
- mIsPipFocusedInRecent = false;
- if (mState == STATE_NO_PIP) {
- return;
- }
- resizePinnedStack(STATE_PIP_OVERLAY);
- }
-
- /**
- * Called when Recents is stopped.
- * PIPed activity will be resized accordingly and overlay will hide available buttons.
- */
- public void onRecentsStopped() {
- mIsRecentsShown = false;
- mIsPipFocusedInRecent = false;
- if (mState == STATE_NO_PIP) {
- return;
- }
- resizePinnedStack(STATE_PIP_OVERLAY);
- }
-
- /**
- * Returns {@code true} if recents is shown.
- */
- boolean isRecentsShown() {
- return mIsRecentsShown;
- }
-
- /**
- * Called when the PIP view in {@link com.android.systemui.recents.tv.RecentsTvActivity}
- * is focused.
- * This only resizes pinned stack so it looks like it's in Recents.
- * This should be called only by {@link com.android.systemui.recents.tv.RecentsTvActivity}.
- */
- public void onPipViewFocusChangedInRecents(boolean hasFocus) {
- mIsPipFocusedInRecent = hasFocus;
- if (mState != STATE_PIP_OVERLAY) {
- Log.w(TAG, "There is no pinned stack to handle focus change.");
- return;
- }
- resizePinnedStack(STATE_PIP_OVERLAY);
+ public Rect getRecentsFocusedPipBounds() {
+ return mRecentsFocusedPipBounds;
}
/**
* Shows PIP menu UI by launching {@link PipMenuActivity}. It also locates the pinned
- * stack to the centered PIP bound {@link com.android.internal.R.string
- * .config_centeredPictureInPictureBounds}.
+ * stack to the centered PIP bound {@link R.config_centeredPictureInPictureBounds}.
*/
private void showPipMenu() {
if (DEBUG) Log.d(TAG, "showPipMenu()");
+ if (mPipRecentsOverlayManager.isRecentsShown()) {
+ if (DEBUG) Log.d(TAG, "Ignore showing PIP menu");
+ return;
+ }
mState = STATE_PIP_MENU;
for (int i = mListeners.size() - 1; i >= 0; --i) {
mListeners.get(i).onShowPipMenu();
@@ -379,14 +377,34 @@
mContext.startActivity(intent);
}
+ /**
+ * Adds a {@link Listener} to PipManager.
+ */
public void addListener(Listener listener) {
mListeners.add(listener);
}
+ /**
+ * Removes a {@link Listener} from PipManager.
+ */
public void removeListener(Listener listener) {
mListeners.remove(listener);
}
+ /**
+ * Adds a {@link MediaListener} to PipManager.
+ */
+ public void addMediaListener(MediaListener listener) {
+ mMediaListeners.add(listener);
+ }
+
+ /**
+ * Removes a {@link MediaListener} from PipManager.
+ */
+ public void removeMediaListener(MediaListener listener) {
+ mMediaListeners.remove(listener);
+ }
+
private void launchPipOnboardingActivityIfNeeded() {
if (DEBUG_FORCE_ONBOARDING || !mOnboardingShown) {
mOnboardingShown = true;
@@ -459,8 +477,8 @@
}
if (mPipMediaController != mediaController) {
mPipMediaController = mediaController;
- for (int i = mListeners.size() - 1; i >= 0; i--) {
- mListeners.get(i).onMediaControllerChanged();
+ for (int i = mMediaListeners.size() - 1; i >= 0; i--) {
+ mMediaListeners.get(i).onMediaControllerChanged();
}
if (mPipMediaController == null) {
mHandler.postDelayed(mClosePipRunnable,
@@ -478,7 +496,33 @@
return mPipMediaController;
}
- TaskStackListener mTaskStackListener = new TaskStackListener() {
+ /**
+ * Returns the PIPed activity's playback state.
+ * This returns one of {@link PLAYBACK_STATE_PLAYING}, {@link PLAYBACK_STATE_PAUSED},
+ * or {@link PLAYBACK_STATE_UNAVAILABLE}.
+ */
+ int getPlaybackState() {
+ if (mPipMediaController == null || mPipMediaController.getPlaybackState() == null) {
+ return PLAYBACK_STATE_UNAVAILABLE;
+ }
+ int state = mPipMediaController.getPlaybackState().getState();
+ boolean isPlaying = (state == PlaybackState.STATE_BUFFERING
+ || state == PlaybackState.STATE_CONNECTING
+ || state == PlaybackState.STATE_PLAYING
+ || state == PlaybackState.STATE_FAST_FORWARDING
+ || state == PlaybackState.STATE_REWINDING
+ || state == PlaybackState.STATE_SKIPPING_TO_PREVIOUS
+ || state == PlaybackState.STATE_SKIPPING_TO_NEXT);
+ long actions = mPipMediaController.getPlaybackState().getActions();
+ if (!isPlaying && ((actions & PlaybackState.ACTION_PLAY) != 0)) {
+ return PLAYBACK_STATE_PAUSED;
+ } else if (isPlaying && ((actions & PlaybackState.ACTION_PAUSE) != 0)) {
+ return PLAYBACK_STATE_PLAYING;
+ }
+ return PLAYBACK_STATE_UNAVAILABLE;
+ }
+
+ private TaskStackListener mTaskStackListener = new TaskStackListener() {
@Override
public void onTaskStackChanged() {
if (mState != STATE_NO_PIP) {
@@ -530,10 +574,10 @@
mMediaSessionManager.addOnActiveSessionsChangedListener(
mActiveMediaSessionListener, null);
updateMediaController(mMediaSessionManager.getActiveSessions(null));
- if (mIsRecentsShown) {
+ if (mPipRecentsOverlayManager.isRecentsShown()) {
// If an activity becomes PIPed again after the fullscreen, the Recents is shown
// behind so we need to resize the pinned stack and show the correct overlay.
- resizePinnedStack(STATE_PIP_OVERLAY);
+ resizePinnedStack(STATE_PIP_RECENTS);
}
for (int i = mListeners.size() - 1; i >= 0; i--) {
mListeners.get(i).onPipEntered();
@@ -552,7 +596,18 @@
if (DEBUG) Log.d(TAG, "onPinnedStackAnimationEnded()");
switch (mState) {
case STATE_PIP_OVERLAY:
- showPipOverlay();
+ if (!mPipRecentsOverlayManager.isRecentsShown()) {
+ showPipOverlay();
+ break;
+ } else {
+ // This happens only if an activity is PIPed after the Recents is shown.
+ // See {@link PipRecentsOverlayManager.requestFocus} for more details.
+ resizePinnedStack(mState);
+ break;
+ }
+ case STATE_PIP_RECENTS:
+ case STATE_PIP_RECENTS_FOCUSED:
+ mPipRecentsOverlayManager.addPipRecentsOverlayView();
break;
case STATE_PIP_MENU:
showPipMenu();
@@ -569,17 +624,23 @@
* Invoked when an activity is pinned and PIP manager is set corresponding information.
* Classes must use this instead of {@link android.app.ITaskStackListener.onActivityPinned}
* because there's no guarantee for the PIP manager be return relavent information
- * correctly. (e.g. {@link isPipShown}, {@link getPipBounds})
+ * correctly. (e.g. {@link isPipShown}).
*/
void onPipEntered();
/** Invoked when a PIPed activity is closed. */
void onPipActivityClosed();
/** Invoked when the PIP menu gets shown. */
void onShowPipMenu();
- /** Invoked when the PIPed activity is returned back to the fullscreen. */
+ /** Invoked when the PIPed activity is about to return back to the fullscreen. */
void onMoveToFullscreen();
/** Invoked when we are above to start resizing the Pip. */
void onPipResizeAboutToStart();
+ }
+
+ /**
+ * A listener interface to receive change in PIP's media controller
+ */
+ public interface MediaListener {
/** Invoked when the MediaController on PIPed activity is changed. */
void onMediaControllerChanged();
}
@@ -593,4 +654,11 @@
}
return sPipManager;
}
+
+ /**
+ * Gets an instance of {@link PipRecentsOverlayManager}.
+ */
+ public PipRecentsOverlayManager getPipRecentsOverlayManager() {
+ return mPipRecentsOverlayManager;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
index b8b837a..854e09d 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
@@ -17,22 +17,9 @@
package com.android.systemui.tv.pip;
import android.app.Activity;
-import android.media.session.MediaController;
-import android.media.session.PlaybackState;
import android.os.Bundle;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
import com.android.systemui.R;
-import com.android.systemui.SystemUI;
-import com.android.systemui.SystemUIApplication;
-import com.android.systemui.recents.Recents;
-
-import static android.content.pm.PackageManager.FEATURE_LEANBACK;
-import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
-import static android.media.session.PlaybackState.ACTION_PAUSE;
-import static android.media.session.PlaybackState.ACTION_PLAY;
/**
* Activity to show the PIP menu to control PIP.
@@ -41,135 +28,23 @@
private static final String TAG = "PipMenuActivity";
private final PipManager mPipManager = PipManager.getInstance();
- private MediaController mMediaController;
- private View mFullButtonView;
- private View mFullDescriptionView;
- private View mPlayPauseView;
- private ImageView mPlayPauseButtonImageView;
- private TextView mPlayPauseDescriptionTextView;
- private View mCloseButtonView;
- private View mCloseDescriptionView;
- private boolean mPipMovedToFullscreen;
-
- private MediaController.Callback mMediaControllerCallback = new MediaController.Callback() {
- @Override
- public void onPlaybackStateChanged(PlaybackState state) {
- updatePlayPauseView(state);
- }
- };
+ private PipControlsView mPipControlsView;
+ private boolean mRestorePipSizeWhenClose;
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.tv_pip_menu);
mPipManager.addListener(this);
- mFullButtonView = findViewById(R.id.full_button);
- mFullDescriptionView = findViewById(R.id.full_desc);
- mFullButtonView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- mPipManager.movePipToFullscreen();
- mPipMovedToFullscreen = true;
- finish();
- }
- });
- mFullButtonView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- mFullDescriptionView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
- }
- });
- mPlayPauseView = findViewById(R.id.play_pause);
- mPlayPauseButtonImageView = (ImageView) findViewById(R.id.play_pause_button);
- mPlayPauseDescriptionTextView = (TextView) findViewById(R.id.play_pause_desc);
- mPlayPauseButtonImageView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mMediaController == null || mMediaController.getPlaybackState() == null) {
- return;
- }
- long actions = mMediaController.getPlaybackState().getActions();
- int state = mMediaController.getPlaybackState().getState();
- if (((actions & ACTION_PLAY) != 0) && !isPlaying(state)) {
- mMediaController.getTransportControls().play();
- } else if ((actions & ACTION_PAUSE) != 0 && isPlaying(state)) {
- mMediaController.getTransportControls().pause();
- }
- // View will be updated later in {@link mMediaControllerCallback}
- }
- });
- mPlayPauseButtonImageView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- mPlayPauseDescriptionTextView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
- }
- });
-
- mCloseButtonView = findViewById(R.id.close_button);
- mCloseDescriptionView = findViewById(R.id.close_desc);
- mCloseButtonView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- mPipManager.closePip();
- finish();
- }
- });
- mCloseButtonView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- mCloseDescriptionView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
- }
- });
- updateMediaController();
- }
-
- private void updateMediaController() {
- MediaController newController = mPipManager.getMediaController();
- if (mMediaController == newController) {
- return;
- }
- if (mMediaController != null) {
- mMediaController.unregisterCallback(mMediaControllerCallback);
- }
- mMediaController = newController;
- if (mMediaController != null) {
- mMediaController.registerCallback(mMediaControllerCallback);
- updatePlayPauseView(mMediaController.getPlaybackState());
- } else {
- updatePlayPauseView(null);
- }
- }
-
- private void updatePlayPauseView(PlaybackState playbackState) {
- if (playbackState != null
- && (playbackState.getActions() & (ACTION_PLAY | ACTION_PAUSE)) != 0) {
- mPlayPauseView.setVisibility(View.VISIBLE);
- if (isPlaying(playbackState.getState())) {
- mPlayPauseButtonImageView.setImageResource(R.drawable.tv_pip_pause_button);
- mPlayPauseDescriptionTextView.setText(R.string.pip_pause);
- } else {
- mPlayPauseButtonImageView.setImageResource(R.drawable.tv_pip_play_button);
- mPlayPauseDescriptionTextView.setText(R.string.pip_play);
- }
- } else {
- mPlayPauseView.setVisibility(View.GONE);
- }
- }
-
- private boolean isPlaying(int state) {
- return state == PlaybackState.STATE_BUFFERING
- || state == PlaybackState.STATE_CONNECTING
- || state == PlaybackState.STATE_PLAYING
- || state == PlaybackState.STATE_FAST_FORWARDING
- || state == PlaybackState.STATE_REWINDING
- || state == PlaybackState.STATE_SKIPPING_TO_PREVIOUS
- || state == PlaybackState.STATE_SKIPPING_TO_NEXT;
+ mPipControlsView = (PipControlsView) findViewById(R.id.pip_controls);
+ mRestorePipSizeWhenClose = true;
}
private void restorePipAndFinish() {
- if (!mPipMovedToFullscreen) {
+ if (mRestorePipSizeWhenClose) {
+ // When PIP menu activity is closed, restore to the default position.
mPipManager.resizePinnedStack(PipManager.STATE_PIP_OVERLAY);
}
finish();
@@ -184,9 +59,6 @@
@Override
protected void onDestroy() {
super.onDestroy();
- if (mMediaController != null) {
- mMediaController.unregisterCallback(mMediaControllerCallback);
- }
mPipManager.removeListener(this);
mPipManager.resumePipResizing(
PipManager.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH);
@@ -210,32 +82,16 @@
@Override
public void onMoveToFullscreen() {
+ // Moving PIP to fullscreen is implemented by resizing PINNED_STACK with null bounds.
+ // This conflicts with restoring PIP position, so disable it.
+ mRestorePipSizeWhenClose = false;
finish();
}
@Override
- public void onMediaControllerChanged() {
- updateMediaController();
- }
-
- @Override
public void onPipResizeAboutToStart() {
finish();
mPipManager.suspendPipResizing(
PipManager.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH);
}
-
- @Override
- public void finish() {
- super.finish();
- if (mPipManager.isRecentsShown() && !mPipMovedToFullscreen) {
- SystemUI[] services = ((SystemUIApplication) getApplication()).getServices();
- for (int i = services.length - 1; i >= 0; i--) {
- if (services[i] instanceof Recents) {
- ((Recents) services[i]).showRecents(false, null);
- break;
- }
- }
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java
index 79daf3d..86ceff4 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java
@@ -86,7 +86,4 @@
@Override
public void onPipResizeAboutToStart() { }
-
- @Override
- public void onMediaControllerChanged() { }
}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
index 1de321d..e205ff5 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
@@ -23,6 +23,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
+import android.widget.ImageView;
import com.android.systemui.R;
@@ -35,28 +36,29 @@
private static final long SHOW_GUIDE_OVERLAY_VIEW_DURATION_MS = 4000;
/**
- * The single instance of PipOverlayActivity to prevent it from restarting.
+ * A flag to ensure the single instance of PipOverlayActivity to prevent it from restarting.
* Note that {@link PipManager} moves the PIPed activity to fullscreen if the activity is
* restarted. It's because the activity may be started by the Launcher or an intent again,
* but we don't want do so for the PipOverlayActivity.
*/
- private static PipOverlayActivity sPipOverlayActivity;
+ private static boolean sActivityCreated;
private final PipManager mPipManager = PipManager.getInstance();
private final Handler mHandler = new Handler();
private View mGuideOverlayView;
private View mGuideButtonsView;
+ private ImageView mGuideButtonPlayPauseImageView;
private final Runnable mHideGuideOverlayRunnable = new Runnable() {
public void run() {
- mGuideOverlayView.setVisibility(View.INVISIBLE);
+ mGuideOverlayView.setVisibility(View.GONE);
}
};
/**
- * Launches the PIP overlay. This should be only called on the main thread.
+ * Shows PIP overlay UI only if it's not there.
*/
- public static void showPipOverlay(Context context) {
- if (sPipOverlayActivity == null) {
+ static void showPipOverlay(Context context) {
+ if (!sActivityCreated) {
Intent intent = new Intent(context, PipOverlayActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
final ActivityOptions options = ActivityOptions.makeBasic();
@@ -68,25 +70,15 @@
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
+ sActivityCreated = true;
setContentView(R.layout.tv_pip_overlay);
mGuideOverlayView = findViewById(R.id.guide_overlay);
- mGuideButtonsView = findViewById(R.id.guide_buttons);
mPipManager.addListener(this);
-
- sPipOverlayActivity = this;
}
@Override
protected void onResume() {
super.onResume();
- // TODO: Implement animation for this
- if (!mPipManager.isRecentsShown()) {
- mGuideOverlayView.setVisibility(View.VISIBLE);
- mGuideButtonsView.setVisibility(View.INVISIBLE);
- } else {
- mGuideOverlayView.setVisibility(View.INVISIBLE);
- mGuideButtonsView.setVisibility(View.VISIBLE);
- }
mHandler.removeCallbacks(mHideGuideOverlayRunnable);
mHandler.postDelayed(mHideGuideOverlayRunnable, SHOW_GUIDE_OVERLAY_VIEW_DURATION_MS);
}
@@ -101,7 +93,7 @@
@Override
protected void onDestroy() {
super.onDestroy();
- sPipOverlayActivity = null;
+ sActivityCreated = false;
mHandler.removeCallbacksAndMessages(null);
mPipManager.removeListener(this);
mPipManager.resumePipResizing(
@@ -132,13 +124,4 @@
mPipManager.suspendPipResizing(
PipManager.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_OVERLAY_ACTIVITY_FINISH);
}
-
- @Override
- public void onMediaControllerChanged() { }
-
- @Override
- public void finish() {
- sPipOverlayActivity = null;
- super.finish();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsControlsView.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsControlsView.java
new file mode 100644
index 0000000..8b8c105
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsControlsView.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.tv.pip;
+
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
+import android.animation.AnimatorSet;
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.View.OnFocusChangeListener;
+
+import com.android.systemui.R;
+
+import static com.android.systemui.tv.pip.PipManager.PLAYBACK_STATE_PLAYING;
+import static com.android.systemui.tv.pip.PipManager.PLAYBACK_STATE_PAUSED;
+import static com.android.systemui.tv.pip.PipManager.PLAYBACK_STATE_UNAVAILABLE;
+
+/**
+ * An extended version of {@link PipControlsView} that supports animation in Recents.
+ */
+public class PipRecentsControlsView extends PipControlsView {
+ /**
+ * An interface to listen user action.
+ */
+ public interface Listener extends PipControlsView.Listener {
+ /**
+ * Called when an user presses BACK key and up.
+ */
+ abstract void onBackPressed();
+ }
+
+ private AnimatorSet mFocusGainAnimatorSet;
+ private AnimatorSet mFocusLoseAnimatorSet;
+
+ public PipRecentsControlsView(Context context) {
+ this(context, null, 0, 0);
+ }
+
+ public PipRecentsControlsView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0, 0);
+ }
+
+ public PipRecentsControlsView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public PipRecentsControlsView(
+ Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ public void onFinishInflate() {
+ super.onFinishInflate();
+
+ int buttonsFocusGainAnim = R.anim.tv_pip_controls_buttons_in_recents_focus_gain_animation;
+ int textFocusGainAnim = R.anim.tv_pip_controls_text_in_recents_focus_gain_animation;
+ mFocusGainAnimatorSet = new AnimatorSet();
+ mFocusGainAnimatorSet.playTogether(
+ loadAnimator(this, R.anim.tv_pip_controls_in_recents_focus_gain_animation),
+ loadAnimator(mFullButtonView,buttonsFocusGainAnim),
+ loadAnimator(mPlayPauseButtonImageView, buttonsFocusGainAnim),
+ loadAnimator(mCloseButtonView, buttonsFocusGainAnim),
+ loadAnimator(mFullDescriptionView, textFocusGainAnim),
+ loadAnimator(mPlayPauseDescriptionTextView, textFocusGainAnim),
+ loadAnimator(mCloseDescriptionView, textFocusGainAnim));
+
+ int buttonsFocusLoseAnim = R.anim.tv_pip_controls_buttons_in_recents_focus_lose_animation;
+ int textFocusLoseAnim = R.anim.tv_pip_controls_text_in_recents_focus_lose_animation;
+ mFocusLoseAnimatorSet = new AnimatorSet();
+ mFocusLoseAnimatorSet.playTogether(
+ loadAnimator(this, R.anim.tv_pip_controls_in_recents_focus_lose_animation),
+ loadAnimator(mFullButtonView, buttonsFocusLoseAnim),
+ loadAnimator(mPlayPauseButtonImageView, buttonsFocusLoseAnim),
+ loadAnimator(mCloseButtonView, buttonsFocusLoseAnim),
+ loadAnimator(mFullDescriptionView, textFocusLoseAnim),
+ loadAnimator(mPlayPauseDescriptionTextView, textFocusLoseAnim),
+ loadAnimator(mCloseDescriptionView, textFocusLoseAnim));
+
+ Rect pipBounds = mPipManager.getRecentsFocusedPipBounds();
+ int pipControlsMarginTop = getContext().getResources().getDimensionPixelSize(
+ R.dimen.recents_tv_pip_controls_margin_top);
+ setPadding(0, pipBounds.bottom + pipControlsMarginTop, 0, 0);
+ }
+
+ private Animator loadAnimator(View view, int animatorResId) {
+ Animator animator = AnimatorInflater.loadAnimator(getContext(), animatorResId);
+ animator.setTarget(view);
+ return animator;
+ }
+
+ /**
+ * Starts focus gaining animation.
+ */
+ public void startFocusGainAnimation() {
+ if (mFocusLoseAnimatorSet.isStarted()) {
+ mFocusLoseAnimatorSet.cancel();
+ }
+ mFocusGainAnimatorSet.start();
+ }
+
+ /**
+ * Starts focus losing animation.
+ */
+ public void startFocusLoseAnimation() {
+ if (mFocusGainAnimatorSet.isStarted()) {
+ mFocusGainAnimatorSet.cancel();
+ }
+ mFocusLoseAnimatorSet.start();
+ }
+
+ /**
+ * Resets the view to the initial state. (i.e. end of the focus gain)
+ */
+ public void reset() {
+ if (mFocusGainAnimatorSet.isStarted()) {
+ mFocusGainAnimatorSet.cancel();
+ }
+ if (mFocusLoseAnimatorSet.isStarted()) {
+ mFocusLoseAnimatorSet.cancel();
+ }
+
+ // Reset to initial state (i.e. end of focused)
+ requestFocus();
+ setTranslationY(0);
+ setScaleXY(mFullButtonView, 1);
+ setScaleXY(mPlayPauseButtonImageView, 1);
+ setScaleXY(mCloseButtonView, 1);
+ mFullDescriptionView.setAlpha(1);
+ mPlayPauseDescriptionTextView.setAlpha(1);
+ mCloseDescriptionView.setAlpha(1);
+ }
+
+ private void setScaleXY(View view, float scale) {
+ view.setScaleX(scale);
+ view.setScaleY(scale);
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ if (!event.isCanceled()
+ && event.getKeyCode() == KeyEvent.KEYCODE_BACK
+ && event.getAction() == KeyEvent.ACTION_UP) {
+ if (mListener != null) {
+ ((PipRecentsControlsView.Listener) mListener).onBackPressed();
+ }
+ return true;
+ }
+ return super.dispatchKeyEvent(event);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java
new file mode 100644
index 0000000..47cd8e5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.tv.pip;
+
+import android.animation.AnimatorInflater;
+import android.animation.AnimatorSet;
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager.LayoutParams;
+import android.view.WindowManager;
+
+import com.android.systemui.R;
+
+import static com.android.systemui.tv.pip.PipManager.STATE_PIP_OVERLAY;
+import static com.android.systemui.tv.pip.PipManager.STATE_PIP_RECENTS;
+import static com.android.systemui.tv.pip.PipManager.STATE_PIP_RECENTS_FOCUSED;
+
+public class PipRecentsOverlayManager {
+ private static final String TAG = "PipRecentsOverlayManager";
+
+ public interface Callback {
+ void onClosed();
+ void onBackPressed();
+ void onRecentsFocused();
+ }
+
+ private final PipManager mPipManager = PipManager.getInstance();
+ private final WindowManager mWindowManager;
+ private final View mOverlayView;
+ private final PipRecentsControlsView mPipControlsView;
+ private final View mRecentsView;
+
+ private final LayoutParams mPipRecentsControlsViewLayoutParams;
+ private final LayoutParams mPipRecentsControlsViewFocusedLayoutParams;
+
+ private boolean mIsPipRecentsOverlayShown;
+ private boolean mIsRecentsShown;
+ private boolean mIsPipFocusedInRecent;
+ private Callback mCallback;
+ private PipRecentsControlsView.Listener mPipControlsViewListener =
+ new PipRecentsControlsView.Listener() {
+ @Override
+ public void onClosed() {
+ if (mCallback != null) {
+ mCallback.onClosed();
+ }
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (mCallback != null) {
+ mCallback.onBackPressed();
+ }
+ }
+ };
+
+ PipRecentsOverlayManager(Context context) {
+ mWindowManager = (WindowManager) context.getSystemService(WindowManager.class);
+
+ LayoutInflater inflater = (LayoutInflater) context
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mOverlayView = inflater.inflate(R.layout.tv_pip_recents_overlay, null);
+ mPipControlsView = (PipRecentsControlsView) mOverlayView.findViewById(R.id.pip_controls);
+ mRecentsView = mOverlayView.findViewById(R.id.recents);
+ mRecentsView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ if (hasFocus) {
+ clearFocus();
+ }
+ }
+ });
+
+ mPipRecentsControlsViewLayoutParams = new WindowManager.LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
+ LayoutParams.TYPE_SYSTEM_DIALOG,
+ LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_NOT_TOUCHABLE,
+ PixelFormat.TRANSLUCENT);
+ mPipRecentsControlsViewFocusedLayoutParams = new WindowManager.LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
+ LayoutParams.TYPE_SYSTEM_DIALOG,
+ 0,
+ PixelFormat.TRANSLUCENT);
+ }
+
+ /**
+ * Add Recents overlay view.
+ * This is expected to be called after the PIP animation is over.
+ */
+ void addPipRecentsOverlayView() {
+ if (mIsPipRecentsOverlayShown) {
+ return;
+ }
+ mIsPipRecentsOverlayShown = true;
+ mIsPipFocusedInRecent = true;
+ mPipControlsView.reset();
+ mWindowManager.addView(mOverlayView, mPipRecentsControlsViewFocusedLayoutParams);
+ }
+
+ /**
+ * Remove Recents overlay view.
+ * This should be called when Recents or PIP is closed.
+ */
+ public void removePipRecentsOverlayView() {
+ if (!mIsPipRecentsOverlayShown) {
+ return;
+ }
+ mWindowManager.removeView(mOverlayView);
+ mIsPipRecentsOverlayShown = false;
+ }
+
+ /**
+ * Request focus to the PIP Recents overlay.
+ * Called when the PIP view in {@link com.android.systemui.recents.tv.RecentsTvActivity}
+ * is focused.
+ * This should be called only by {@link com.android.systemui.recents.tv.RecentsTvActivity}.
+ * @param allowRecentsFocusable {@code true} if Recents can have focus. (i.e. Has a recent task)
+ */
+ public void requestFocus(boolean allowRecentsFocusable) {
+ if (!mIsRecentsShown || mIsPipFocusedInRecent) {
+ return;
+ }
+ mIsPipFocusedInRecent = true;
+ mPipManager.resizePinnedStack(STATE_PIP_RECENTS_FOCUSED);
+
+ mWindowManager.updateViewLayout(mOverlayView, mPipRecentsControlsViewFocusedLayoutParams);
+ mPipControlsView.requestFocus();
+ mPipControlsView.startFocusGainAnimation();
+ mRecentsView.setVisibility(allowRecentsFocusable ? View.VISIBLE : View.GONE);
+ }
+
+ /**
+ * Request focus to the PIP Recents overlay.
+ * Called when the PIP view in {@link com.android.systemui.recents.tv.RecentsTvActivity}
+ * is focused.
+ * This should be called only by {@link com.android.systemui.recents.tv.RecentsTvActivity}.
+ */
+ private void clearFocus() {
+ if (!mIsRecentsShown || !mIsPipFocusedInRecent) {
+ return;
+ }
+ mIsPipFocusedInRecent = false;
+ mPipManager.resizePinnedStack(STATE_PIP_RECENTS);
+ mWindowManager.updateViewLayout(mOverlayView, mPipRecentsControlsViewLayoutParams);
+ mPipControlsView.startFocusLoseAnimation();
+ if (mCallback != null) {
+ mCallback.onRecentsFocused();
+ }
+ }
+
+ public void setCallback(Callback listener) {
+ mCallback = listener;
+ mPipControlsView.setListener(mCallback != null ? mPipControlsViewListener : null);
+ }
+
+ /**
+ * Called when Recents is resumed.
+ * PIPed activity will be resized accordingly and overlay will show available buttons.
+ */
+ public void onRecentsResumed() {
+ if (!mPipManager.isPipShown()) {
+ return;
+ }
+ mIsRecentsShown = true;
+ mIsPipFocusedInRecent = true;
+ mPipManager.resizePinnedStack(STATE_PIP_RECENTS_FOCUSED);
+ // Overlay view will be added after the resize animation ends, if any.
+ }
+
+ /**
+ * Called when Recents is paused.
+ * PIPed activity will be resized accordingly and overlay will hide available buttons.
+ */
+ public void onRecentsPaused() {
+ mIsRecentsShown = false;
+ mIsPipFocusedInRecent = false;
+ removePipRecentsOverlayView();
+
+ if (mPipManager.isPipShown()) {
+ mPipManager.resizePinnedStack(STATE_PIP_OVERLAY);
+ }
+ }
+
+ /**
+ * Returns {@code true} if recents is shown.
+ */
+ boolean isRecentsShown() {
+ return mIsRecentsShown;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java b/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
index f432808..c6e4356 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
@@ -35,11 +35,11 @@
private static final Typeface MEDIUM = Typeface.create("sans-serif-medium", Typeface.NORMAL);
private final Context mContext;
- private final LayoutInflater mInflater;
+ protected final LayoutInflater mInflater;
private final SpTexts mSpTexts;
private Callback mCallback;
- private Object mSelectedValue;
+ protected Object mSelectedValue;
public SegmentedButtons(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -65,13 +65,21 @@
final Object tag = c.getTag();
final boolean selected = Objects.equals(mSelectedValue, tag);
c.setSelected(selected);
- c.setTypeface(selected ? MEDIUM : REGULAR);
+ setSelectedStyle(c, selected);
}
fireOnSelected(fromClick);
}
+ protected void setSelectedStyle(TextView textView, boolean selected) {
+ textView.setTypeface(selected ? MEDIUM : REGULAR);
+ }
+
+ public Button inflateButton() {
+ return (Button) mInflater.inflate(R.layout.segmented_button, this, false);
+ }
+
public void addButton(int labelResId, int contentDescriptionResId, Object value) {
- final Button b = (Button) mInflater.inflate(R.layout.segmented_button, this, false);
+ final Button b = inflateButton();
b.setTag(LABEL_RES_KEY, labelResId);
b.setText(labelResId);
b.setContentDescription(getResources().getString(contentDescriptionResId));
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index 6976c0b..e3ed92c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -86,7 +86,7 @@
= new Intent(Settings.ACTION_ZEN_MODE_PRIORITY_SETTINGS);
private final Context mContext;
- private final LayoutInflater mInflater;
+ protected final LayoutInflater mInflater;
private final H mHandler = new H();
private final ZenPrefs mPrefs;
private final TransitionHelper mTransitionHelper = new TransitionHelper();
@@ -95,12 +95,12 @@
private String mTag = TAG + "/" + Integer.toHexString(System.identityHashCode(this));
- private SegmentedButtons mZenButtons;
+ protected SegmentedButtons mZenButtons;
private View mZenIntroduction;
private TextView mZenIntroductionMessage;
private View mZenIntroductionConfirm;
private TextView mZenIntroductionCustomize;
- private LinearLayout mZenConditions;
+ protected LinearLayout mZenConditions;
private TextView mZenAlarmWarning;
private Callback mCallback;
@@ -148,10 +148,7 @@
mTransitionHelper.dump(fd, pw, args);
}
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
+ protected void createZenButtons() {
mZenButtons = (SegmentedButtons) findViewById(R.id.zen_buttons);
mZenButtons.addButton(R.string.interruption_level_none_twoline,
R.string.interruption_level_none_with_warning,
@@ -163,7 +160,12 @@
R.string.interruption_level_priority,
Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
mZenButtons.setCallback(mZenButtonsCallback);
+ }
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ createZenButtons();
mZenIntroduction = findViewById(R.id.zen_introduction);
mZenIntroductionMessage = (TextView) findViewById(R.id.zen_introduction_message);
mSpTexts.add(mZenIntroductionMessage);
@@ -302,14 +304,18 @@
}
}
+ protected void addZenConditions(int count) {
+ for (int i = 0; i < count; i++) {
+ mZenConditions.addView(mInflater.inflate(R.layout.zen_mode_condition, this, false));
+ }
+ }
+
public void init(ZenModeController controller) {
mController = controller;
mCountdownConditionSupported = mController.isCountdownConditionSupported();
final int countdownDelta = mCountdownConditionSupported ? COUNTDOWN_CONDITION_COUNT : 0;
final int minConditions = 1 /*forever*/ + countdownDelta;
- for (int i = 0; i < minConditions; i++) {
- mZenConditions.addView(mInflater.inflate(R.layout.zen_mode_condition, this, false));
- }
+ addZenConditions(minConditions);
mSessionZen = getSelectedZen(-1);
handleUpdateManualRule(mController.getManualRule());
if (DEBUG) Log.d(mTag, "init mExitCondition=" + mExitCondition);
@@ -917,7 +923,7 @@
}
}
- private final SegmentedButtons.Callback mZenButtonsCallback = new SegmentedButtons.Callback() {
+ protected final SegmentedButtons.Callback mZenButtonsCallback = new SegmentedButtons.Callback() {
@Override
public void onSelected(final Object value, boolean fromClick) {
if (value != null && mZenButtons.isShown() && isAttachedToWindow()) {
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index 5389c804..53a9976 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -23,6 +23,8 @@
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<uses-permission android:name="android.permission.MANAGE_USERS" />
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+ <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/TouchAnimatorTests.java b/packages/SystemUI/tests/src/com/android/systemui/qs/TouchAnimatorTests.java
index 1d81fd4..e9b85b9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/TouchAnimatorTests.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/TouchAnimatorTests.java
@@ -14,11 +14,13 @@
package com.android.systemui.qs;
+import android.test.suitebuilder.annotation.SmallTest;
import android.view.View;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.qs.TouchAnimator.Listener;
import org.mockito.Mockito;
+@SmallTest
public class TouchAnimatorTests extends SysuiTestCase {
private Listener mTouchListener;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTests.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTests.java
index f86c6a4..a30f507 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTests.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTests.java
@@ -32,9 +32,11 @@
import android.service.quicksettings.IQSTileService;
import android.service.quicksettings.Tile;
import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
import android.util.ArraySet;
import android.util.Log;
+@SmallTest
public class TileLifecycleManagerTests extends AndroidTestCase {
public static final String TILE_UPDATE_BROADCAST = "com.android.systemui.tests.TILE_UPDATE";
public static final String EXTRA_CALLBACK = "callback";
@@ -196,7 +198,7 @@
}
private void waitForCallback(String callback) {
- for (int i = 0; i < 25; i++) {
+ for (int i = 0; i < 50; i++) {
if (mCallbacks.contains(callback)) {
mCallbacks.remove(callback);
return;
@@ -227,7 +229,7 @@
}
});
try {
- lock.wait(5000);
+ lock.wait(10000);
} catch (InterruptedException e) {
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTests.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTests.java
index efdb50d..f24b541 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTests.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTests.java
@@ -19,10 +19,12 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.service.quicksettings.TileService;
+import android.test.suitebuilder.annotation.SmallTest;
import com.android.systemui.SysuiTestCase;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
+@SmallTest
public class TileServiceManagerTests extends SysuiTestCase {
private TileServices mTileServices;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTests.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTests.java
index 01514646b..94c98d6c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTests.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTests.java
@@ -17,6 +17,7 @@
import android.content.ComponentName;
import android.os.Looper;
+import android.test.suitebuilder.annotation.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.phone.QSTileHost;
import com.android.systemui.statusbar.policy.DataSaverController;
@@ -27,6 +28,7 @@
import java.util.ArrayList;
+@SmallTest
public class TileServicesTests extends SysuiTestCase {
private static int NUM_FAKES = TileServices.DEFAULT_MAX_BOUND * 2;
diff --git a/packages/VpnDialogs/AndroidManifest.xml b/packages/VpnDialogs/AndroidManifest.xml
index 32e1e6d..08257b6 100644
--- a/packages/VpnDialogs/AndroidManifest.xml
+++ b/packages/VpnDialogs/AndroidManifest.xml
@@ -24,7 +24,7 @@
<application android:label="VpnDialogs"
android:allowBackup="false" >
<activity android:name=".ConfirmDialog"
- android:theme="@*android:style/Theme.Material.DayNight.Dialog.Alert">
+ android:theme="@android:style/Theme.Material.Light.Dialog.Alert">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.DEFAULT"/>
@@ -32,7 +32,7 @@
</activity>
<activity android:name=".ManageDialog"
- android:theme="@*android:style/Theme.Material.DayNight.Dialog.Alert"
+ android:theme="@android:style/Theme.Material.Light.Dialog.Alert"
android:noHistory="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
diff --git a/packages/WallpaperCropper/res/values/styles.xml b/packages/WallpaperCropper/res/values/styles.xml
index 0f9e247..6a56afd 100644
--- a/packages/WallpaperCropper/res/values/styles.xml
+++ b/packages/WallpaperCropper/res/values/styles.xml
@@ -15,7 +15,7 @@
-->
<resources>
- <style name="Theme.WallpaperCropper" parent="@*android:style/Theme.Material.DayNight">
+ <style name="Theme.WallpaperCropper" parent="@android:style/Theme.Material.Light">
<item name="android:actionBarStyle">@style/WallpaperCropperActionBar</item>
<item name="android:windowFullscreen">true</item>
<item name="android:windowActionBarOverlay">true</item>
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index d6f1499..b3613df 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -2017,6 +2017,124 @@
// action pass package name of calling package.
ACTION_SCOPED_DIRECTORY_ACCESS_DENIED_AND_PERSIST_BY_PACKAGE = 356;
+ // Logged when a user dismisses all task in overview
+ OVERVIEW_DISMISS_ALL = 357;
+
+ // Quick Settings -> Edit
+ QS_EDIT = 358;
+
+ // Quick Settings -> Edit -> Overflow -> Reset
+ ACTION_QS_EDIT_RESET = 359;
+
+ // QS -> Edit - Drag a tile out of the active tiles.
+ // The _SPEC contains either the spec of the tile or
+ // the package of the 3rd party app in the PKG field.
+ ACTION_QS_EDIT_REMOVE_SPEC = 360;
+ ACTION_QS_EDIT_REMOVE = 361;
+
+ // QS -> Edit - Drag a tile into the active tiles.
+ // The _SPEC contains either the spec of the tile or
+ // the package of the 3rd party app in the PKG field.
+ ACTION_QS_EDIT_ADD_SPEC = 362;
+ ACTION_QS_EDIT_ADD = 363;
+
+ // QS -> Edit - Drag a tile within the active tiles.
+ // The _SPEC contains either the spec of the tile or
+ // the package of the 3rd party app in the PKG field.
+ ACTION_QS_EDIT_MOVE_SPEC = 364;
+ ACTION_QS_EDIT_MOVE = 365;
+
+ // Long-press on a QS tile. Tile spec in package field.
+ ACTION_QS_LONG_PRESS = 366;
+
+ // OPEN: SUW Welcome Screen -> Vision Settings
+ // CATEGORY: SETTINGS
+ // OS: N
+ SUW_ACCESSIBILITY = 367;
+
+ // OPEN: SUW Welcome Screen -> Vision Settings -> Magnification gesture
+ // ACTION: New magnification gesture configuration is chosen
+ // SUBTYPE: 0 is off, 1 is on
+ // CATEGORY: SETTINGS
+ // OS: N
+ SUW_ACCESSIBILITY_TOGGLE_SCREEN_MAGNIFICATION = 368;
+
+ // OPEN: SUW Welcome Screen -> Vision Settings -> Font size
+ // ACTION: New font size is chosen
+ // SUBTYPE: 0 is small, 1 is default, 2 is large, 3 is largest
+ // CATEGORY: SETTINGS
+ // OS: N
+ SUW_ACCESSIBILITY_FONT_SIZE = 369;
+
+ // OPEN: SUW Welcome Screen -> Vision Settings -> Display size
+ // ACTION: New display size is chosen
+ // SUBTYPE: 0 is small, 1 is default, 2 is large, 3 is larger, 4 is largest
+ // CATEGORY: SETTINGS
+ // OS: N
+ SUW_ACCESSIBILITY_DISPLAY_SIZE = 370;
+
+ // OPEN: SUW Welcome Screen -> Vision Settings -> TalkBack
+ // ACTION: New screen reader configuration is chosen
+ // SUBTYPE: 0 is off, 1 is on
+ // CATEGORY: SETTINGS
+ // OS: N
+ SUW_ACCESSIBILITY_TOGGLE_SCREEN_READER = 371;
+
+ // ------- Begin N Settings conditionals -----
+ // Conditionals are the green bars at the top of the settings dashboard
+ // All conditionals will have visible/hide events onResume/onPause
+ // but they will also be used as extra ints in the
+ // dismiss/expand/collapse/click/button events
+
+ // swipe away conditional
+ ACTION_SETTINGS_CONDITION_DISMISS = 372;
+
+ // click on collapsed conditional or clicks expand button
+ ACTION_SETTINGS_CONDITION_EXPAND = 373;
+
+ // click collapse button on expanded conditional
+ ACTION_SETTINGS_CONDITION_COLLAPSE = 374;
+
+ // click main area of expanded conditional
+ ACTION_SETTINGS_CONDITION_CLICK = 375;
+
+ // click a direct button on expanded conditional
+ ACTION_SETTINGS_CONDITION_BUTTON = 376;
+
+ // Airplane mode on
+ SETTINGS_CONDITION_AIRPLANE_MODE = 377;
+ // AKA Data saver on
+ SETTINGS_CONDITION_BACKGROUND_DATA = 378;
+ // Battery saver on
+ SETTINGS_CONDITION_BATTERY_SAVER = 379;
+ // Cellular data off
+ SETTINGS_CONDITION_CELLULAR_DATA = 380;
+ // Do not disturb on
+ SETTINGS_CONDITION_DND = 381;
+ // Hotspot on
+ SETTINGS_CONDITION_HOTSPOT = 382;
+ // Work profile off
+ SETTINGS_CONDITION_WORK_MODE = 383;
+
+ // ------- Begin N Settings suggestions -----
+ // Since suggestions come from system apps, suggestions will
+ // have generic constants and the package providing the suggestion
+ // will be put in the package field. For suggestions in the Settings
+ // package, the class name will be filled in instead (since settings
+ // provides several suggetions).
+
+ // Settings shown/hidden on main settings dashboard.
+ // These are actually visibility events, but visible/hidden doesn't
+ // take a package, so these are being logged as actions.
+ ACTION_SHOW_SETTINGS_SUGGESTION = 384;
+ ACTION_HIDE_SETTINGS_SUGGESTION = 385;
+
+ // Click on a suggestion.
+ ACTION_SETTINGS_SUGGESTION = 386;
+
+ // Suggestion -> Overflow -> Remove.
+ ACTION_SETTINGS_DISMISS_SUGGESTION = 387;
+
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
}
diff --git a/rs/java/android/renderscript/Allocation.java b/rs/java/android/renderscript/Allocation.java
index fc92966..9ec6e8d 100644
--- a/rs/java/android/renderscript/Allocation.java
+++ b/rs/java/android/renderscript/Allocation.java
@@ -142,7 +142,10 @@
}
return Element.DataType.FLOAT_64;
}
- return null;
+
+ throw new RSIllegalArgumentException("Parameter of type " + cmp.getSimpleName() +
+ "[] is not compatible with data type " + mType.mElement.mType.name() +
+ " of allocation");
}
@@ -293,8 +296,13 @@
}
/**
- * Enable/Disable AutoPadding for Vec3 elements.
- * By default: Diabled.
+ * Enable/Disable AutoPadding for Vec3 Elements.
+ *
+ * <p> Vec3 Elements, such as {@link Element#U8_3} are treated as Vec4 Elements
+ * with the fourth vector element used as padding. Enabling the AutoPadding feature
+ * will automatically add/remove the padding when you copy to/from an Allocation
+ * with a Vec3 Element.
+ * <p> By default: Disabled.
*
* @param useAutoPadding True: enable AutoPadding; False: disable AutoPadding
*
@@ -372,6 +380,7 @@
Log.e(RenderScript.LOG_TAG, "Couldn't invoke registerNativeAllocation:" + e);
throw new RSRuntimeException("Couldn't invoke registerNativeAllocation:" + e);
}
+ guard.open("destroy");
}
Allocation(long id, RenderScript rs, Type t, int usage, MipmapControl mips) {
@@ -1907,6 +1916,7 @@
if (type.getID(rs) == 0) {
throw new RSInvalidStateException("Bad Type");
}
+ // TODO: What if there is an exception after this? The native allocation would leak.
long id = rs.nAllocationCreateTyped(type.getID(rs), mips.mID, usage, 0);
if (id == 0) {
throw new RSRuntimeException("Allocation creation failed.");
diff --git a/rs/java/android/renderscript/BaseObj.java b/rs/java/android/renderscript/BaseObj.java
index 1372ab7..f95af16 100644
--- a/rs/java/android/renderscript/BaseObj.java
+++ b/rs/java/android/renderscript/BaseObj.java
@@ -16,6 +16,7 @@
package android.renderscript;
+import dalvik.system.CloseGuard;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
@@ -69,6 +70,7 @@
}
private long mID;
+ final CloseGuard guard = CloseGuard.get();
private boolean mDestroyed;
private String mName;
RenderScript mRS;
@@ -119,6 +121,7 @@
}
if (shouldDestroy) {
+ guard.close();
// must include nObjDestroy in the critical section
ReentrantReadWriteLock.ReadLock rlock = mRS.mRWLock.readLock();
rlock.lock();
@@ -133,8 +136,14 @@
}
protected void finalize() throws Throwable {
- helpDestroy();
- super.finalize();
+ try {
+ if (guard != null) {
+ guard.warnIfOpen();
+ }
+ helpDestroy();
+ } finally {
+ super.finalize();
+ }
}
/**
diff --git a/rs/java/android/renderscript/Element.java b/rs/java/android/renderscript/Element.java
index 6efb6d6..50226ac 100644
--- a/rs/java/android/renderscript/Element.java
+++ b/rs/java/android/renderscript/Element.java
@@ -808,6 +808,7 @@
mSize += mElements[ct].mSize * mArraySizes[ct];
}
updateVisibleSubElements();
+ guard.open("destroy");
}
Element(long id, RenderScript rs, DataType dt, DataKind dk, boolean norm, int size) {
@@ -827,6 +828,7 @@
mKind = dk;
mNormalized = norm;
mVectorSize = size;
+ guard.open("destroy");
}
Element(long id, RenderScript rs) {
diff --git a/rs/java/android/renderscript/FileA3D.java b/rs/java/android/renderscript/FileA3D.java
index 9d8f162..278d309 100644
--- a/rs/java/android/renderscript/FileA3D.java
+++ b/rs/java/android/renderscript/FileA3D.java
@@ -170,6 +170,7 @@
FileA3D(long id, RenderScript rs, InputStream stream) {
super(id, rs);
mInputStream = stream;
+ guard.open("destroy");
}
private void initEntries() {
diff --git a/rs/java/android/renderscript/Font.java b/rs/java/android/renderscript/Font.java
index 4318b9d..d5ca31e 100644
--- a/rs/java/android/renderscript/Font.java
+++ b/rs/java/android/renderscript/Font.java
@@ -150,6 +150,7 @@
Font(long id, RenderScript rs) {
super(id, rs);
+ guard.open("destroy");
}
/**
diff --git a/rs/java/android/renderscript/Mesh.java b/rs/java/android/renderscript/Mesh.java
index 13c8e1c..9e4f905 100644
--- a/rs/java/android/renderscript/Mesh.java
+++ b/rs/java/android/renderscript/Mesh.java
@@ -91,6 +91,7 @@
Mesh(long id, RenderScript rs) {
super(id, rs);
+ guard.open("destroy");
}
/**
diff --git a/rs/java/android/renderscript/Program.java b/rs/java/android/renderscript/Program.java
index 3eb9b75..772021c 100644
--- a/rs/java/android/renderscript/Program.java
+++ b/rs/java/android/renderscript/Program.java
@@ -76,6 +76,7 @@
Program(long id, RenderScript rs) {
super(id, rs);
+ guard.open("destroy");
}
/**
diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java
index 9beaba3..2650e5a 100644
--- a/rs/java/android/renderscript/RenderScript.java
+++ b/rs/java/android/renderscript/RenderScript.java
@@ -1031,7 +1031,6 @@
- long mDev;
long mContext;
private boolean mDestroyed = false;
@@ -1426,8 +1425,8 @@
RenderScript rs = new RenderScript(ctx);
- rs.mDev = rs.nDeviceCreate();
- rs.mContext = rs.nContextCreate(rs.mDev, flags, sdkVersion, ct.mID);
+ long device = rs.nDeviceCreate();
+ rs.mContext = rs.nContextCreate(device, flags, sdkVersion, ct.mID);
rs.mContextType = ct;
rs.mContextFlags = flags;
rs.mContextSdkVersion = sdkVersion;
@@ -1635,9 +1634,6 @@
}
nContextDestroy();
-
- nDeviceDestroy(mDev);
- mDev = 0;
}
}
diff --git a/rs/java/android/renderscript/RenderScriptGL.java b/rs/java/android/renderscript/RenderScriptGL.java
index 6178994..be1f899 100644
--- a/rs/java/android/renderscript/RenderScriptGL.java
+++ b/rs/java/android/renderscript/RenderScriptGL.java
@@ -177,9 +177,9 @@
mWidth = 0;
mHeight = 0;
- mDev = nDeviceCreate();
+ long device = nDeviceCreate();
int dpi = ctx.getResources().getDisplayMetrics().densityDpi;
- mContext = nContextCreateGL(mDev, 0, sdkVersion,
+ mContext = nContextCreateGL(device, 0, sdkVersion,
mSurfaceConfig.mColorMin, mSurfaceConfig.mColorPref,
mSurfaceConfig.mAlphaMin, mSurfaceConfig.mAlphaPref,
mSurfaceConfig.mDepthMin, mSurfaceConfig.mDepthPref,
diff --git a/rs/java/android/renderscript/Sampler.java b/rs/java/android/renderscript/Sampler.java
index a4edbb5..5c4bae9 100644
--- a/rs/java/android/renderscript/Sampler.java
+++ b/rs/java/android/renderscript/Sampler.java
@@ -51,6 +51,7 @@
Sampler(long id, RenderScript rs) {
super(id, rs);
+ guard.open("destroy");
}
/**
diff --git a/rs/java/android/renderscript/Script.java b/rs/java/android/renderscript/Script.java
index 2b06780..fc3280b 100644
--- a/rs/java/android/renderscript/Script.java
+++ b/rs/java/android/renderscript/Script.java
@@ -41,6 +41,7 @@
mScript = s;
mSlot = slot;
mSig = sig;
+ guard.open("destroy");
}
}
@@ -118,6 +119,7 @@
super(id, rs);
mScript = s;
mSlot = slot;
+ guard.open("destroy");
}
}
@@ -357,6 +359,19 @@
super(id, rs);
mInIdsBuffer = new long[1];
+
+ /* The constructors for the derived classes (including ScriptIntrinsic
+ * derived classes and ScriptC derived classes generated by Slang
+ * reflection) seem to be simple enough, so we just put the guard.open()
+ * call here, rather than in the end of the constructor for the derived
+ * class. This, of course, assumes the derived constructor would not
+ * throw any exception after calling this constructor.
+ *
+ * If new derived classes are added with more complicated constructors
+ * that throw exceptions, this call has to be (duplicated and) moved
+ * to the end of each derived class constructor.
+ */
+ guard.open("destroy");
}
/**
@@ -541,21 +556,22 @@
/**
* Class for specifying the specifics about how a kernel will be
- * launched
+ * launched.
*
* This class can specify a potential range of cells on which to
* run a kernel. If no set is called for a dimension then this
* class will have no impact on that dimension when the kernel
* is executed.
*
- * The forEach launch will operate over the intersection of the
- * dimensions.
+ * The forEach kernel launch will operate over the intersection of
+ * the dimensions.
*
* Example:
* LaunchOptions with setX(5, 15)
* Allocation with dimension X=10, Y=10
- * The resulting forEach run would execute over x = 5 to 10 and
- * y = 0 to 10.
+ * The resulting forEach run would execute over:
+ * x = 5 to 9 (inclusive) and
+ * y = 0 to 9 (inclusive).
*
*
*/
@@ -569,11 +585,11 @@
private int strategy;
/**
- * Set the X range. If the end value is set to 0 the X dimension is not
- * clipped.
+ * Set the X range. xstartArg is the lowest coordinate of the range,
+ * and xendArg-1 is the highest coordinate of the range.
*
* @param xstartArg Must be >= 0
- * @param xendArg Must be >= xstartArg
+ * @param xendArg Must be > xstartArg
*
* @return LaunchOptions
*/
@@ -587,11 +603,11 @@
}
/**
- * Set the Y range. If the end value is set to 0 the Y dimension is not
- * clipped.
+ * Set the Y range. ystartArg is the lowest coordinate of the range,
+ * and yendArg-1 is the highest coordinate of the range.
*
* @param ystartArg Must be >= 0
- * @param yendArg Must be >= ystartArg
+ * @param yendArg Must be > ystartArg
*
* @return LaunchOptions
*/
@@ -605,11 +621,11 @@
}
/**
- * Set the Z range. If the end value is set to 0 the Z dimension is not
- * clipped.
+ * Set the Z range. zstartArg is the lowest coordinate of the range,
+ * and zendArg-1 is the highest coordinate of the range.
*
* @param zstartArg Must be >= 0
- * @param zendArg Must be >= zstartArg
+ * @param zendArg Must be > zstartArg
*
* @return LaunchOptions
*/
diff --git a/rs/java/android/renderscript/ScriptGroup.java b/rs/java/android/renderscript/ScriptGroup.java
index 9357c3bb..219f16b 100644
--- a/rs/java/android/renderscript/ScriptGroup.java
+++ b/rs/java/android/renderscript/ScriptGroup.java
@@ -148,6 +148,8 @@
fieldIDs, values, sizes, depClosures, depFieldIDs);
setID(id);
+
+ guard.open("destroy");
}
Closure(RenderScript rs, Script.InvokeID invokeID,
@@ -181,6 +183,8 @@
values, sizes);
setID(id);
+
+ guard.open("destroy");
}
private void retrieveValueAndDependenceInfo(RenderScript rs,
@@ -382,6 +386,7 @@
ScriptGroup(long id, RenderScript rs) {
super(id, rs);
+ guard.open("destroy");
}
ScriptGroup(RenderScript rs, String name, List<Closure> closures,
@@ -398,6 +403,7 @@
}
long id = rs.nScriptGroup2Create(name, RenderScript.getCachePath(), closureIDs);
setID(id);
+ guard.open("destroy");
}
/**
diff --git a/rs/java/android/renderscript/ScriptIntrinsicConvolve3x3.java b/rs/java/android/renderscript/ScriptIntrinsicConvolve3x3.java
index 76da781..339e0e9 100644
--- a/rs/java/android/renderscript/ScriptIntrinsicConvolve3x3.java
+++ b/rs/java/android/renderscript/ScriptIntrinsicConvolve3x3.java
@@ -32,10 +32,9 @@
* Supported elements types are {@link Element#U8}, {@link
* Element#U8_2}, {@link Element#U8_3}, {@link Element#U8_4},
* {@link Element#F32}, {@link Element#F32_2}, {@link
- * Element#F32_3}, and {@link Element#F32_4}
+ * Element#F32_3}, and {@link Element#F32_4}.
*
- * The default coefficients are.
- *
+ * <p> The default coefficients are:
* <code>
* <p> [ 0, 0, 0 ]
* <p> [ 0, 1, 0 ]
@@ -67,7 +66,7 @@
}
/**
- * Set the input of the blur.
+ * Set the input of the 3x3 convolve.
* Must match the element type supplied during create.
*
* @param ain The input allocation.
@@ -80,7 +79,7 @@
/**
* Set the coefficients for the convolve.
*
- * The convolve layout is
+ * <p> The convolve layout is:
* <code>
* <p> [ 0, 1, 2 ]
* <p> [ 3, 4, 5 ]
diff --git a/rs/java/android/renderscript/ScriptIntrinsicConvolve5x5.java b/rs/java/android/renderscript/ScriptIntrinsicConvolve5x5.java
index 2d37600..a288cee 100644
--- a/rs/java/android/renderscript/ScriptIntrinsicConvolve5x5.java
+++ b/rs/java/android/renderscript/ScriptIntrinsicConvolve5x5.java
@@ -32,9 +32,9 @@
* Supported elements types are {@link Element#U8}, {@link
* Element#U8_2}, {@link Element#U8_3}, {@link Element#U8_4},
* {@link Element#F32}, {@link Element#F32_2}, {@link
- * Element#F32_3}, and {@link Element#F32_4}
+ * Element#F32_3}, and {@link Element#F32_4}.
*
- * The default coefficients are.
+ * <p> The default coefficients are:
* <code>
* <p> [ 0, 0, 0, 0, 0 ]
* <p> [ 0, 0, 0, 0, 0 ]
@@ -66,7 +66,7 @@
}
/**
- * Set the input of the blur.
+ * Set the input of the 5x5 convolve.
* Must match the element type supplied during create.
*
* @param ain The input allocation.
@@ -79,7 +79,7 @@
/**
* Set the coefficients for the convolve.
*
- * The convolve layout is
+ * <p> The convolve layout is:
* <code>
* <p> [ 0, 1, 2, 3, 4 ]
* <p> [ 5, 6, 7, 8, 9 ]
diff --git a/rs/java/android/renderscript/Type.java b/rs/java/android/renderscript/Type.java
index dc23785..9252898 100644
--- a/rs/java/android/renderscript/Type.java
+++ b/rs/java/android/renderscript/Type.java
@@ -227,6 +227,7 @@
Type(long id, RenderScript rs) {
super(id, rs);
+ guard.open("destroy");
}
@Override
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index e710ded..2741733 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1262,7 +1262,7 @@
Service service = componentNameToServiceMap.get(componentName);
// Ignore non-encryption-aware services until user is unlocked
- if (!isUnlocked && !installedService.isEncryptionAware()) {
+ if (!isUnlocked && !installedService.isDirectBootAware()) {
Slog.d(LOG_TAG, "Ignoring non-encryption-aware service " + componentName);
continue;
}
@@ -1774,17 +1774,27 @@
private void updateSoftKeyboardShowModeLocked(UserState userState) {
final int userId = userState.mUserId;
- if (userId == mCurrentUserId) {
- // Check whether any Accessibility Services are still enabled and, if not, remove flag
- // requesting no soft keyboard
- final boolean accessibilityRequestingNoIme = userState.mSoftKeyboardShowMode == 1;
- if (accessibilityRequestingNoIme && !userState.isHandlingAccessibilityEvents()) {
- // No active Accessibility Services can be requesting the soft keyboard to be hidden
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
- 0,
- userState.mUserId);
+ // Only check whether we need to reset the soft keyboard mode if it is not set to the
+ // default.
+ if ((userId == mCurrentUserId) && (userState.mSoftKeyboardShowMode != 0)) {
+ // Check whether the last Accessibility Service that changed the soft keyboard mode to
+ // something other than the default is still enabled and, if not, remove flag and
+ // reset to the default soft keyboard behavior.
+ boolean serviceChangingSoftKeyboardModeIsEnabled =
+ userState.mEnabledServices.contains(userState.mServiceChangingSoftKeyboardMode);
+
+ if (!serviceChangingSoftKeyboardModeIsEnabled) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+ 0,
+ userState.mUserId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
userState.mSoftKeyboardShowMode = 0;
+ userState.mServiceChangingSoftKeyboardMode = null;
}
notifySoftKeyboardShowModeChangedLocked(userState.mSoftKeyboardShowMode);
@@ -1812,6 +1822,87 @@
return mKeyEventDispatcher;
}
+ /**
+ * Enables accessibility service specified by {@param componentName} for the {@param userId}.
+ */
+ public void enableAccessibilityService(ComponentName componentName, int userId) {
+ synchronized(mLock) {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("only SYSTEM can call enableAccessibilityService.");
+ }
+
+ SettingsStringHelper settingsHelper = new SettingsStringHelper(
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, userId);
+ settingsHelper.addService(componentName);
+ settingsHelper.writeToSettings();
+
+ UserState userState = getUserStateLocked(userId);
+ if (userState.mEnabledServices.add(componentName)) {
+ onUserStateChangedLocked(userState);
+ }
+ }
+ }
+
+ /**
+ * Disables accessibility service specified by {@param componentName} for the {@param userId}.
+ */
+ public void disableAccessibilityService(ComponentName componentName, int userId) {
+ synchronized(mLock) {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("only SYSTEM can call disableAccessibility");
+ }
+
+ SettingsStringHelper settingsHelper = new SettingsStringHelper(
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, userId);
+ settingsHelper.deleteService(componentName);
+ settingsHelper.writeToSettings();
+
+ UserState userState = getUserStateLocked(userId);
+ if (userState.mEnabledServices.remove(componentName)) {
+ onUserStateChangedLocked(userState);
+ }
+ }
+ }
+
+ private class SettingsStringHelper {
+ private static final String SETTINGS_DELIMITER = ":";
+ private ContentResolver mContentResolver;
+ private final String mSettingsName;
+ private Set<String> mServices;
+ private final int mUserId;
+
+ public SettingsStringHelper(String name, int userId) {
+ mUserId = userId;
+ mSettingsName = name;
+ mContentResolver = mContext.getContentResolver();
+ String servicesString = Settings.Secure.getStringForUser(
+ mContentResolver, mSettingsName, userId);
+ mServices = new HashSet();
+ if (!TextUtils.isEmpty(servicesString)) {
+ final TextUtils.SimpleStringSplitter colonSplitter =
+ new TextUtils.SimpleStringSplitter(SETTINGS_DELIMITER.charAt(0));
+ colonSplitter.setString(servicesString);
+ while (colonSplitter.hasNext()) {
+ final String serviceName = colonSplitter.next();
+ mServices.add(serviceName);
+ }
+ }
+ }
+
+ public void addService(ComponentName component) {
+ mServices.add(component.flattenToString());
+ }
+
+ public void deleteService(ComponentName component) {
+ mServices.remove(component.flattenToString());
+ }
+
+ public void writeToSettings() {
+ Settings.Secure.putStringForUser(mContentResolver, mSettingsName,
+ TextUtils.join(SETTINGS_DELIMITER, mServices), mUserId);
+ }
+ }
+
@Override
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
mSecurityPolicy.enforceCallingPermission(Manifest.permission.DUMP, FUNCTION_DUMP);
@@ -2060,7 +2151,7 @@
MagnificationController getMagnificationController() {
synchronized (mLock) {
if (mMagnificationController == null) {
- mMagnificationController = new MagnificationController(mContext, this);
+ mMagnificationController = new MagnificationController(mContext, this, mLock);
mMagnificationController.register();
mMagnificationController.setUserId(mCurrentUserId);
}
@@ -2885,6 +2976,14 @@
final long identity = Binder.clearCallingIdentity();
try {
+ // Keep track of the last service to request a non-default show mode. The show mode
+ // should be restored to default should this service be disabled.
+ if (showMode == Settings.Secure.SHOW_MODE_AUTO) {
+ userState.mServiceChangingSoftKeyboardMode = null;
+ } else {
+ userState.mServiceChangingSoftKeyboardMode = mComponentName;
+ }
+
Settings.Secure.putIntForUser(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, showMode,
userState.mUserId);
@@ -3397,6 +3496,8 @@
reportedWindow.setLayer(window.layer);
reportedWindow.setFocused(window.focused);
reportedWindow.setBoundsInScreen(window.boundsInScreen);
+ reportedWindow.setTitle(window.title);
+ reportedWindow.setAnchorId(window.accessibilityIdOfAnchor);
final int parentId = findWindowIdLocked(window.parentToken);
if (parentId >= 0) {
@@ -4034,6 +4135,8 @@
public final Set<ComponentName> mTouchExplorationGrantedServices =
new HashSet<>();
+ public ComponentName mServiceChangingSoftKeyboardMode;
+
public int mLastSentClientState = -1;
public int mSoftKeyboardShowMode = 0;
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
index a093d92..b2196bf 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
@@ -72,7 +72,7 @@
*/
private static final float MIN_PERSISTED_SCALE = 2.0f;
- private final Object mLock = new Object();
+ private final Object mLock;
/**
* The current magnification spec. If an animation is running, this
@@ -97,12 +97,13 @@
private int mUserId;
- public MagnificationController(Context context, AccessibilityManagerService ams) {
+ public MagnificationController(Context context, AccessibilityManagerService ams, Object lock) {
mAms = ams;
mContentResolver = context.getContentResolver();
mScreenStateObserver = new ScreenStateObserver(context, this);
mWindowStateObserver = new WindowStateObserver(context, this);
mSpecAnimationBridge = new SpecAnimationBridge(context);
+ mLock = lock;
}
/**
@@ -111,12 +112,18 @@
public void register() {
mScreenStateObserver.register();
mWindowStateObserver.register();
+
+ // Obtain initial state.
+ mWindowStateObserver.getRegions(mMagnifiedRegion, mAvailableRegion);
+ mMagnifiedRegion.getBounds(mMagnifiedBounds);
}
/**
* Unregisters magnification-related observers.
*/
public void unregister() {
+ mSpecAnimationBridge.cancel();
+
mScreenStateObserver.unregister();
mWindowStateObserver.unregister();
}
@@ -149,8 +156,10 @@
final float offsetY = sentSpec.offsetY;
// Compute the new center and update spec as needed.
- final float centerX = (mMagnifiedBounds.width() / 2.0f - offsetX) / scale;
- final float centerY = (mMagnifiedBounds.height() / 2.0f - offsetY) / scale;
+ final float centerX = (mMagnifiedBounds.width() / 2.0f
+ + mMagnifiedBounds.left - offsetX) / scale;
+ final float centerY = (mMagnifiedBounds.height() / 2.0f
+ + mMagnifiedBounds.top - offsetY) / scale;
if (updateSpec) {
setScaleAndCenter(scale, centerX, centerY, false);
} else {
@@ -246,7 +255,8 @@
*/
public float getCenterX() {
synchronized (mLock) {
- return (mMagnifiedBounds.width() / 2.0f - getOffsetX()) / getScale();
+ return (mMagnifiedBounds.width() / 2.0f
+ + mMagnifiedBounds.left - getOffsetX()) / getScale();
}
}
@@ -268,7 +278,8 @@
*/
public float getCenterY() {
synchronized (mLock) {
- return (mMagnifiedBounds.height() / 2.0f - getOffsetY()) / getScale();
+ return (mMagnifiedBounds.height() / 2.0f
+ + mMagnifiedBounds.top - getOffsetY()) / getScale();
}
}
@@ -471,18 +482,25 @@
* otherwise
*/
private boolean updateMagnificationSpecLocked(float scale, float centerX, float centerY) {
+ // Handle defaults.
+ if (Float.isNaN(centerX)) {
+ centerX = getCenterX();
+ }
+ if (Float.isNaN(centerY)) {
+ centerY = getCenterY();
+ }
+ if (Float.isNaN(scale)) {
+ scale = getScale();
+ }
+
+ // Ensure requested center is within the available region.
if (!availableRegionContains(centerX, centerY)) {
return false;
}
- boolean changed = false;
-
+ // Compute changes.
final MagnificationSpec currSpec = mCurrentMagnificationSpec;
-
- // Handle scale.
- if (Float.isNaN(scale)) {
- scale = getScale();
- }
+ boolean changed = false;
final float normScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
if (Float.compare(currSpec.scale, normScale) != 0) {
@@ -490,24 +508,16 @@
changed = true;
}
- // Handle X offset.
- if (Float.isNaN(centerX)) {
- centerX = getCenterX();
- }
-
- final float nonNormOffsetX = mMagnifiedBounds.width() / 2.0f - centerX * scale;
+ final float nonNormOffsetX = mMagnifiedBounds.width() / 2.0f
+ + mMagnifiedBounds.left - centerX * scale;
final float offsetX = MathUtils.constrain(nonNormOffsetX, getMinOffsetXLocked(), 0);
if (Float.compare(currSpec.offsetX, offsetX) != 0) {
currSpec.offsetX = offsetX;
changed = true;
}
- // Handle Y offset.
- if (Float.isNaN(centerY)) {
- centerY = getCenterY();
- }
-
- final float nonNormOffsetY = mMagnifiedBounds.height() / 2.0f - centerY * scale;
+ final float nonNormOffsetY = mMagnifiedBounds.height() / 2.0f
+ + mMagnifiedBounds.top - centerY * scale;
final float offsetY = MathUtils.constrain(nonNormOffsetY, getMinOffsetYLocked(), 0);
if (Float.compare(currSpec.offsetY, offsetY) != 0) {
currSpec.offsetY = offsetY;
@@ -661,6 +671,12 @@
mTransformationAnimator.setInterpolator(new DecelerateInterpolator(2.5f));
}
+ public void cancel() {
+ if (mTransformationAnimator != null && mTransformationAnimator.isRunning()) {
+ mTransformationAnimator.cancel();
+ }
+ }
+
public void updateSentSpec(MagnificationSpec spec, boolean animate) {
if (Thread.currentThread().getId() == mMainThreadId) {
// Already on the main thread, don't bother proxying.
@@ -811,9 +827,6 @@
private static final int MESSAGE_ON_USER_CONTEXT_CHANGED = 3;
private static final int MESSAGE_ON_ROTATION_CHANGED = 4;
- private final Rect mTempRect = new Rect();
- private final Rect mTempRect1 = new Rect();
-
private final MagnificationController mController;
private final WindowManagerInternal mWindowManager;
private final Handler mHandler;
@@ -884,6 +897,10 @@
mController.resetIfNeeded(true);
}
+ public void getRegions(@NonNull Region outMagnified, @NonNull Region outAvailable) {
+ mWindowManager.getMagnificationRegions(outMagnified, outAvailable);
+ }
+
private class CallbackHandler extends Handler {
public CallbackHandler(Context context) {
super(context.getMainLooper());
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 215be4a..f93fb1b 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -164,7 +164,8 @@
onUserStopped(userId);
} else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
reloadWidgetsMaskedStateForGroup(userId);
- } else if (Intent.ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED.equals(action)) {
+ } else if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action)
+ || Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) {
synchronized (mLock) {
reloadWidgetsMaskedState(userId);
}
@@ -288,7 +289,8 @@
userFilter, null, null);
IntentFilter offModeFilter = new IntentFilter();
- offModeFilter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED);
+ offModeFilter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
+ offModeFilter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
offModeFilter, null, null);
@@ -486,8 +488,14 @@
boolean changed = provider.setMaskedByLockedProfileLocked(lockedProfile);
changed |= provider.setMaskedByQuietProfileLocked(quietProfile);
try {
- boolean suspended = mPackageManager.isPackageSuspendedForUser(
- provider.info.provider.getPackageName(), provider.getUserId());
+ boolean suspended;
+ try {
+ suspended = mPackageManager.isPackageSuspendedForUser(
+ provider.info.provider.getPackageName(), provider.getUserId());
+ } catch (IllegalArgumentException ex) {
+ // Package not found.
+ suspended = false;
+ }
changed |= provider.setMaskedBySuspendedPackageLocked(suspended);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to query application info", e);
@@ -588,27 +596,28 @@
}
final boolean showBadge;
final Intent onClickIntent;
- if (provider.maskedBySuspendedPackage) {
- final long identity = Binder.clearCallingIdentity();
- try {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (provider.maskedBySuspendedPackage) {
UserInfo userInfo = mUserManager.getUserInfo(providerUserId);
showBadge = userInfo.isManagedProfile();
onClickIntent = mDevicePolicyManagerInternal.createPackageSuspendedDialogIntent(
providerPackage, providerUserId);
- } finally {
- Binder.restoreCallingIdentity(identity);
+ } else if (provider.maskedByQuietProfile) {
+ showBadge = true;
+ onClickIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(
+ providerUserId);
+ } else /* provider.maskedByLockedProfile */ {
+ showBadge = true;
+ onClickIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, null,
+ providerUserId);
+ if (onClickIntent != null) {
+ onClickIntent.setFlags(FLAG_ACTIVITY_NEW_TASK
+ | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ }
}
- } else if (provider.maskedByQuietProfile) {
- showBadge = true;
- onClickIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(
- providerUserId);
- } else /* provider.maskedByLockedProfile */ {
- showBadge = true;
- onClickIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, null,
- providerUserId);
- if (onClickIntent != null) {
- onClickIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
for (int j = 0; j < widgetCount; j++) {
@@ -736,8 +745,8 @@
}
@Override
- public int[] startListening(IAppWidgetHost callbacks, String callingPackage,
- int hostId, List<RemoteViews> updatedViews) {
+ public ParceledListSlice<RemoteViews> startListening(IAppWidgetHost callbacks,
+ String callingPackage, int hostId, int[] appWidgetIds, int[] updatedIds) {
final int userId = UserHandle.getCallingUserId();
if (DEBUG) {
@@ -754,21 +763,21 @@
// sure the caller can only access hosts it owns.
HostId id = new HostId(Binder.getCallingUid(), hostId, callingPackage);
Host host = lookupOrAddHostLocked(id);
-
host.callbacks = callbacks;
- updatedViews.clear();
-
- ArrayList<Widget> instances = host.widgets;
- int N = instances.size();
- int[] updatedIds = new int[N];
+ int N = appWidgetIds.length;
+ ArrayList<RemoteViews> outViews = new ArrayList<>(N);
+ RemoteViews rv;
+ int added = 0;
for (int i = 0; i < N; i++) {
- Widget widget = instances.get(i);
- updatedIds[i] = widget.appWidgetId;
- updatedViews.add(cloneIfLocalBinder(widget.getEffectiveViewsLocked()));
+ rv = host.getPendingViewsForId(appWidgetIds[i]);
+ if (rv != null) {
+ updatedIds[added] = appWidgetIds[i];
+ outViews.add(rv);
+ added++;
+ }
}
-
- return updatedIds;
+ return new ParceledListSlice<>(outViews);
}
}
@@ -1876,6 +1885,10 @@
}
private void scheduleNotifyUpdateAppWidgetLocked(Widget widget, RemoteViews updateViews) {
+ long requestTime = SystemClock.uptimeMillis();
+ if (widget != null) {
+ widget.lastUpdateTime = requestTime;
+ }
if (widget == null || widget.provider == null || widget.provider.zombie
|| widget.host.callbacks == null || widget.host.zombie) {
return;
@@ -1885,6 +1898,7 @@
args.arg1 = widget.host;
args.arg2 = widget.host.callbacks;
args.arg3 = updateViews;
+ args.arg4 = requestTime;
args.argi1 = widget.appWidgetId;
mCallbackHandler.obtainMessage(
@@ -1893,9 +1907,10 @@
}
private void handleNotifyUpdateAppWidget(Host host, IAppWidgetHost callbacks,
- int appWidgetId, RemoteViews views) {
+ int appWidgetId, RemoteViews views, long requestTime) {
try {
callbacks.updateAppWidget(appWidgetId, views);
+ host.lastWidgetUpdateTime = requestTime;
} catch (RemoteException re) {
synchronized (mLock) {
Slog.e(TAG, "Widget host dead: " + host.id, re);
@@ -3392,10 +3407,11 @@
Host host = (Host) args.arg1;
IAppWidgetHost callbacks = (IAppWidgetHost) args.arg2;
RemoteViews views = (RemoteViews) args.arg3;
+ long requestTime = (Long) args.arg4;
final int appWidgetId = args.argi1;
args.recycle();
- handleNotifyUpdateAppWidget(host, callbacks, appWidgetId, views);
+ handleNotifyUpdateAppWidget(host, callbacks, appWidgetId, views, requestTime);
} break;
case MSG_NOTIFY_PROVIDER_CHANGED: {
@@ -3763,6 +3779,7 @@
boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
int tag = TAG_UNDEFINED; // for use while saving state (the index)
+ long lastWidgetUpdateTime; // last time we were successfully able to send an update.
public int getUserId() {
return UserHandle.getUserId(id.uid);
@@ -3784,6 +3801,23 @@
return false;
}
+ /**
+ * Returns the RemoveViews for the provided widget id if an update is pending
+ * for that widget.
+ */
+ public RemoteViews getPendingViewsForId(int appWidgetId) {
+ long updateTime = lastWidgetUpdateTime;
+ int N = widgets.size();
+ for (int i = 0; i < N; i++) {
+ Widget widget = widgets.get(i);
+ if (widget.appWidgetId == appWidgetId
+ && widget.lastUpdateTime > updateTime) {
+ return cloneIfLocalBinder(widget.getEffectiveViewsLocked());
+ }
+ }
+ return null;
+ }
+
@Override
public String toString() {
return "Host{" + id + (zombie ? " Z" : "") + '}';
@@ -3854,6 +3888,7 @@
RemoteViews maskedViews;
Bundle options;
Host host;
+ long lastUpdateTime;
@Override
public String toString() {
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index b737ae2..6288b56 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -1818,7 +1818,7 @@
File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME);
if (initSentinel.exists()) {
synchronized (mQueueLock) {
- mPendingInits.add(transportName);
+ mPendingInits.add(name);
// TODO: pick a better starting time than now + 1 minute
long delay = 1000 * 60; // one minute, in milliseconds
@@ -2316,6 +2316,25 @@
}
}
+ // What name is this transport registered under...?
+ private String getTransportName(IBackupTransport transport) {
+ if (MORE_DEBUG) {
+ Slog.v(TAG, "Searching for transport name of " + transport);
+ }
+ synchronized (mTransports) {
+ final int N = mTransports.size();
+ for (int i = 0; i < N; i++) {
+ if (mTransports.valueAt(i).equals(transport)) {
+ if (MORE_DEBUG) {
+ Slog.v(TAG, " Name found: " + mTransports.keyAt(i));
+ }
+ return mTransports.keyAt(i);
+ }
+ }
+ }
+ return null;
+ }
+
// fire off a backup agent, blocking until it attaches or times out
IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) {
IBackupAgent agent = null;
@@ -2921,7 +2940,15 @@
if (MORE_DEBUG) Slog.d(TAG, "Server requires init; rerunning");
addBackupTrace("init required; rerunning");
try {
- mPendingInits.add(mTransport.transportDirName());
+ final String name = getTransportName(mTransport);
+ if (name != null) {
+ mPendingInits.add(name);
+ } else {
+ if (DEBUG) {
+ Slog.w(TAG, "Couldn't find name of transport " + mTransport
+ + " for init");
+ }
+ }
} catch (Exception e) {
Slog.w(TAG, "Failed to query transport name heading for init", e);
// swallow it and proceed; we don't rely on this
@@ -7590,77 +7617,6 @@
// ----- Restore handling -----
- // new style: we only store the SHA-1 hashes of each sig, not the full block
- static boolean signaturesMatch(ArrayList<byte[]> storedSigHashes, PackageInfo target) {
- if (target == null) {
- return false;
- }
-
- // If the target resides on the system partition, we allow it to restore
- // data from the like-named package in a restore set even if the signatures
- // do not match. (Unlike general applications, those flashed to the system
- // partition will be signed with the device's platform certificate, so on
- // different phones the same system app will have different signatures.)
- if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
- if (MORE_DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check");
- return true;
- }
-
- // Allow unsigned apps, but not signed on one device and unsigned on the other
- // !!! TODO: is this the right policy?
- Signature[] deviceSigs = target.signatures;
- if (MORE_DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigHashes
- + " device=" + deviceSigs);
- if ((storedSigHashes == null || storedSigHashes.size() == 0)
- && (deviceSigs == null || deviceSigs.length == 0)) {
- return true;
- }
- if (storedSigHashes == null || deviceSigs == null) {
- return false;
- }
-
- // !!! TODO: this demands that every stored signature match one
- // that is present on device, and does not demand the converse.
- // Is this this right policy?
- final int nStored = storedSigHashes.size();
- final int nDevice = deviceSigs.length;
-
- // hash each on-device signature
- ArrayList<byte[]> deviceHashes = new ArrayList<byte[]>(nDevice);
- for (int i = 0; i < nDevice; i++) {
- deviceHashes.add(hashSignature(deviceSigs[i]));
- }
-
- // now ensure that each stored sig (hash) matches an on-device sig (hash)
- for (int n = 0; n < nStored; n++) {
- boolean match = false;
- final byte[] storedHash = storedSigHashes.get(n);
- for (int i = 0; i < nDevice; i++) {
- if (Arrays.equals(storedHash, deviceHashes.get(i))) {
- match = true;
- break;
- }
- }
- // match is false when no on-device sig matched one of the stored ones
- if (!match) {
- return false;
- }
- }
-
- return true;
- }
-
- static byte[] hashSignature(Signature sig) {
- try {
- MessageDigest digest = MessageDigest.getInstance("SHA-256");
- digest.update(sig.toByteArray());
- return digest.digest();
- } catch (NoSuchAlgorithmException e) {
- Slog.w(TAG, "No SHA-256 algorithm found!");
- }
- return null;
- }
-
// Old style: directly match the stored vs on device signature blocks
static boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) {
if (target == null) {
@@ -8173,7 +8129,7 @@
}
Metadata metaInfo = mPmAgent.getRestoredMetadata(packageName);
- if (!signaturesMatch(metaInfo.sigHashes, mCurrentPackage)) {
+ if (!BackupUtils.signaturesMatch(metaInfo.sigHashes, mCurrentPackage)) {
Slog.w(TAG, "Signature mismatch restoring " + packageName);
EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
"Signature mismatch");
diff --git a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
index f197c1e..09f240f 100644
--- a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
+++ b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
@@ -205,7 +205,7 @@
PackageManager.GET_SIGNATURES);
homeInstaller = mPackageManager.getInstallerPackageName(home.getPackageName());
homeVersion = homeInfo.versionCode;
- homeSigHashes = hashSignatureArray(homeInfo.signatures);
+ homeSigHashes = BackupUtils.hashSignatureArray(homeInfo.signatures);
} catch (NameNotFoundException e) {
Slog.w(TAG, "Can't access preferred home info");
// proceed as though there were no preferred home set
@@ -222,7 +222,7 @@
final boolean needHomeBackup = (homeVersion != mStoredHomeVersion)
|| !Objects.equals(home, mStoredHomeComponent)
|| (home != null
- && !BackupManagerService.signaturesMatch(mStoredHomeSigHashes, homeInfo));
+ && !BackupUtils.signaturesMatch(mStoredHomeSigHashes, homeInfo));
if (needHomeBackup) {
if (DEBUG) {
Slog.i(TAG, "Home preference changed; backing up new state " + home);
@@ -309,7 +309,7 @@
outputBuffer.reset();
outputBufferStream.writeInt(info.versionCode);
writeSignatureHashArray(outputBufferStream,
- hashSignatureArray(info.signatures));
+ BackupUtils.hashSignatureArray(info.signatures));
if (DEBUG) {
Slog.v(TAG, "+ writing metadata for " + packName
@@ -432,18 +432,6 @@
mRestoredSignatures = sigMap;
}
- private static ArrayList<byte[]> hashSignatureArray(Signature[] sigs) {
- if (sigs == null) {
- return null;
- }
-
- ArrayList<byte[]> hashes = new ArrayList<byte[]>(sigs.length);
- for (Signature s : sigs) {
- hashes.add(BackupManagerService.hashSignature(s));
- }
- return hashes;
- }
-
private static void writeSignatureHashArray(DataOutputStream out, ArrayList<byte[]> hashes)
throws IOException {
// the number of entries in the array
@@ -492,13 +480,8 @@
}
if (nonHashFound) {
- ArrayList<byte[]> hashes =
- new ArrayList<byte[]>(sigs.size());
- for (int i = 0; i < sigs.size(); i++) {
- Signature s = new Signature(sigs.get(i));
- hashes.add(BackupManagerService.hashSignature(s));
- }
- sigs = hashes;
+ // Replace with the hashes.
+ sigs = BackupUtils.hashSignatureArray(sigs);
}
return sigs;
diff --git a/services/core/java/com/android/server/AnyMotionDetector.java b/services/core/java/com/android/server/AnyMotionDetector.java
index a0b5c15..e98b4aa 100644
--- a/services/core/java/com/android/server/AnyMotionDetector.java
+++ b/services/core/java/com/android/server/AnyMotionDetector.java
@@ -108,63 +108,71 @@
public AnyMotionDetector(PowerManager pm, Handler handler, SensorManager sm,
DeviceIdleCallback callback, float thresholdAngle) {
if (DEBUG) Slog.d(TAG, "AnyMotionDetector instantiated.");
- mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
- mWakeLock.setReferenceCounted(false);
- mHandler = handler;
- mSensorManager = sm;
- mAccelSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
- mMeasurementInProgress = false;
- mState = STATE_INACTIVE;
- mCallback = callback;
- mThresholdAngle = thresholdAngle;
- mRunningStats = new RunningSignalStats();
- mNumSufficientSamples = (int) Math.ceil(
- ((double)ORIENTATION_MEASUREMENT_DURATION_MILLIS / SAMPLING_INTERVAL_MILLIS));
- if (DEBUG) Slog.d(TAG, "mNumSufficientSamples = " + mNumSufficientSamples);
+ synchronized (mLock) {
+ mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+ mWakeLock.setReferenceCounted(false);
+ mHandler = handler;
+ mSensorManager = sm;
+ mAccelSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+ mMeasurementInProgress = false;
+ mState = STATE_INACTIVE;
+ mCallback = callback;
+ mThresholdAngle = thresholdAngle;
+ mRunningStats = new RunningSignalStats();
+ mNumSufficientSamples = (int) Math.ceil(
+ ((double)ORIENTATION_MEASUREMENT_DURATION_MILLIS / SAMPLING_INTERVAL_MILLIS));
+ if (DEBUG) Slog.d(TAG, "mNumSufficientSamples = " + mNumSufficientSamples);
+ }
}
/*
* Acquire accel data until we determine AnyMotion status.
*/
public void checkForAnyMotion() {
- if (DEBUG) Slog.d(TAG, "checkForAnyMotion(). mState = " + mState);
+ if (DEBUG) {
+ Slog.d(TAG, "checkForAnyMotion(). mState = " + mState);
+ }
if (mState != STATE_ACTIVE) {
- mState = STATE_ACTIVE;
- if (DEBUG) Slog.d(TAG, "Moved from STATE_INACTIVE to STATE_ACTIVE.");
- mCurrentGravityVector = null;
- mPreviousGravityVector = null;
- startOrientationMeasurement();
+ synchronized (mLock) {
+ mState = STATE_ACTIVE;
+ if (DEBUG) {
+ Slog.d(TAG, "Moved from STATE_INACTIVE to STATE_ACTIVE.");
+ }
+ mCurrentGravityVector = null;
+ mPreviousGravityVector = null;
+ mWakeLock.acquire();
+ startOrientationMeasurementLocked();
+ }
}
}
public void stop() {
if (mState == STATE_ACTIVE) {
- mState = STATE_INACTIVE;
- if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE.");
- if (mMeasurementInProgress) {
- mMeasurementInProgress = false;
- mSensorManager.unregisterListener(mListener);
+ synchronized (mLock) {
+ mState = STATE_INACTIVE;
+ if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE.");
+ if (mMeasurementInProgress) {
+ mMeasurementInProgress = false;
+ mSensorManager.unregisterListener(mListener);
+ }
+ mHandler.removeCallbacks(mMeasurementTimeout);
+ mHandler.removeCallbacks(mSensorRestart);
+ mCurrentGravityVector = null;
+ mPreviousGravityVector = null;
+ mWakeLock.release();
}
- mHandler.removeCallbacks(mMeasurementTimeout);
- mHandler.removeCallbacks(mSensorRestart);
- mWakeLock.release();
- mCurrentGravityVector = null;
- mPreviousGravityVector = null;
}
}
- private void startOrientationMeasurement() {
- if (DEBUG) Slog.d(TAG, "startOrientationMeasurement: mMeasurementInProgress=" +
+ private void startOrientationMeasurementLocked() {
+ if (DEBUG) Slog.d(TAG, "startOrientationMeasurementLocked: mMeasurementInProgress=" +
mMeasurementInProgress + ", (mAccelSensor != null)=" + (mAccelSensor != null));
-
if (!mMeasurementInProgress && mAccelSensor != null) {
if (mSensorManager.registerListener(mListener, mAccelSensor,
SAMPLING_INTERVAL_MILLIS * 1000)) {
- mWakeLock.acquire();
mMeasurementInProgress = true;
mRunningStats.reset();
}
-
Message msg = Message.obtain(mHandler, mMeasurementTimeout);
msg.setAsynchronous(true);
mHandler.sendMessageDelayed(msg, ACCELEROMETER_DATA_TIMEOUT_MILLIS);
@@ -178,7 +186,6 @@
if (mMeasurementInProgress) {
mSensorManager.unregisterListener(mListener);
mHandler.removeCallbacks(mMeasurementTimeout);
- mWakeLock.release();
long detectionEndTime = SystemClock.elapsedRealtime();
mMeasurementInProgress = false;
mPreviousGravityVector = mCurrentGravityVector;
@@ -196,8 +203,10 @@
status = getStationaryStatus();
if (DEBUG) Slog.d(TAG, "getStationaryStatus() returned " + status);
if (status != RESULT_UNKNOWN) {
- if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE. status = " +
- status);
+ mWakeLock.release();
+ if (DEBUG) {
+ Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE. status = " + status);
+ }
mState = STATE_INACTIVE;
} else {
/*
@@ -275,7 +284,7 @@
@Override
public void run() {
synchronized (mLock) {
- startOrientationMeasurement();
+ startOrientationMeasurementLocked();
}
}
};
@@ -442,4 +451,4 @@
return msg;
}
}
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index e7db2a8..0a2153e 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -25,7 +25,6 @@
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
@@ -447,8 +446,12 @@
int[] ops) {
mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
Binder.getCallingPid(), Binder.getCallingUid(), null);
+ String resolvedPackageName = resolvePackageName(uid, packageName);
+ if (resolvedPackageName == null) {
+ return Collections.emptyList();
+ }
synchronized (this) {
- Ops pkgOps = getOpsLocked(uid, packageName, false);
+ Ops pkgOps = getOpsRawLocked(uid, resolvedPackageName, false);
if (pkgOps == null) {
return null;
}
@@ -466,7 +469,7 @@
private void pruneOp(Op op, int uid, String packageName) {
if (op.time == 0 && op.rejectTime == 0) {
- Ops ops = getOpsLocked(uid, packageName, false);
+ Ops ops = getOpsRawLocked(uid, packageName, false);
if (ops != null) {
ops.remove(op.op);
if (ops.size() <= 0) {
@@ -880,8 +883,12 @@
public int checkOperation(int code, int uid, String packageName) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
+ String resolvedPackageName = resolvePackageName(uid, packageName);
+ if (resolvedPackageName == null) {
+ return AppOpsManager.MODE_IGNORED;
+ }
synchronized (this) {
- if (isOpRestricted(uid, code, packageName)) {
+ if (isOpRestricted(uid, code, resolvedPackageName)) {
return AppOpsManager.MODE_IGNORED;
}
code = AppOpsManager.opToSwitch(code);
@@ -892,7 +899,7 @@
return uidMode;
}
}
- Op op = getOpLocked(code, uid, packageName, false);
+ Op op = getOpLocked(code, uid, resolvedPackageName, false);
if (op == null) {
return AppOpsManager.opToDefaultMode(code);
}
@@ -902,7 +909,15 @@
@Override
public int checkAudioOperation(int code, int usage, int uid, String packageName) {
- if (isPackageSuspendedForUser(packageName, uid)) {
+ boolean suspended;
+ try {
+ suspended = isPackageSuspendedForUser(packageName, uid);
+ } catch (IllegalArgumentException ex) {
+ // Package not found.
+ suspended = false;
+ }
+
+ if (suspended) {
Log.i(TAG, "Audio disabled for suspended package=" + packageName + " for uid=" + uid);
return AppOpsManager.MODE_IGNORED;
}
@@ -964,10 +979,12 @@
usageRestrictions.put(usage, r);
}
}
+ notifyWatchersOfChange(code);
}
@Override
public int checkPackage(int uid, String packageName) {
+ Preconditions.checkNotNull(packageName);
synchronized (this) {
if (getOpsRawLocked(uid, packageName, true) != null) {
return AppOpsManager.MODE_ALLOWED;
@@ -981,26 +998,39 @@
public int noteProxyOperation(int code, String proxyPackageName,
int proxiedUid, String proxiedPackageName) {
verifyIncomingOp(code);
- final int proxyMode = noteOperationUnchecked(code, Binder.getCallingUid(),
- proxyPackageName, -1, null);
+ final int proxyUid = Binder.getCallingUid();
+ String resolveProxyPackageName = resolvePackageName(proxyUid, proxyPackageName);
+ if (resolveProxyPackageName == null) {
+ return AppOpsManager.MODE_IGNORED;
+ }
+ final int proxyMode = noteOperationUnchecked(code, proxyUid,
+ resolveProxyPackageName, -1, null);
if (proxyMode != AppOpsManager.MODE_ALLOWED || Binder.getCallingUid() == proxiedUid) {
return proxyMode;
}
- return noteOperationUnchecked(code, proxiedUid, proxiedPackageName,
- Binder.getCallingUid(), proxyPackageName);
+ String resolveProxiedPackageName = resolvePackageName(proxiedUid, proxiedPackageName);
+ if (resolveProxiedPackageName == null) {
+ return AppOpsManager.MODE_IGNORED;
+ }
+ return noteOperationUnchecked(code, proxiedUid, resolveProxiedPackageName,
+ proxyMode, resolveProxyPackageName);
}
@Override
public int noteOperation(int code, int uid, String packageName) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
- return noteOperationUnchecked(code, uid, packageName, 0, null);
+ String resolvedPackageName = resolvePackageName(uid, packageName);
+ if (resolvedPackageName == null) {
+ return AppOpsManager.MODE_IGNORED;
+ }
+ return noteOperationUnchecked(code, uid, resolvedPackageName, 0, null);
}
private int noteOperationUnchecked(int code, int uid, String packageName,
int proxyUid, String proxyPackageName) {
synchronized (this) {
- Ops ops = getOpsLocked(uid, packageName, true);
+ Ops ops = getOpsRawLocked(uid, packageName, true);
if (ops == null) {
if (DEBUG) Log.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
+ " package " + packageName);
@@ -1017,7 +1047,9 @@
op.duration = 0;
final int switchCode = AppOpsManager.opToSwitch(code);
UidState uidState = ops.uidState;
- if (uidState.opModes != null) {
+ // If there is a non-default per UID policy (we set UID op mode only if
+ // non-default) it takes over, otherwise use the per package policy.
+ if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) {
final int uidMode = uidState.opModes.get(switchCode);
if (uidMode != AppOpsManager.MODE_ALLOWED) {
if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code "
@@ -1026,13 +1058,15 @@
op.rejectTime = System.currentTimeMillis();
return uidMode;
}
- }
- final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
- if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
- if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code "
- + switchCode + " (" + code + ") uid " + uid + " package " + packageName);
- op.rejectTime = System.currentTimeMillis();
- return switchOp.mode;
+ } else {
+ final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
+ if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
+ if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code "
+ + switchCode + " (" + code + ") uid " + uid + " package "
+ + packageName);
+ op.rejectTime = System.currentTimeMillis();
+ return switchOp.mode;
+ }
}
if (DEBUG) Log.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
+ " package " + packageName);
@@ -1048,16 +1082,20 @@
public int startOperation(IBinder token, int code, int uid, String packageName) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
+ String resolvedPackageName = resolvePackageName(uid, packageName);
+ if (resolvedPackageName == null) {
+ return AppOpsManager.MODE_IGNORED;
+ }
ClientState client = (ClientState)token;
synchronized (this) {
- Ops ops = getOpsLocked(uid, packageName, true);
+ Ops ops = getOpsRawLocked(uid, resolvedPackageName, true);
if (ops == null) {
if (DEBUG) Log.d(TAG, "startOperation: no op for code " + code + " uid " + uid
- + " package " + packageName);
+ + " package " + resolvedPackageName);
return AppOpsManager.MODE_ERRORED;
}
Op op = getOpLocked(ops, code, true);
- if (isOpRestricted(uid, code, packageName)) {
+ if (isOpRestricted(uid, code, resolvedPackageName)) {
return AppOpsManager.MODE_IGNORED;
}
final int switchCode = AppOpsManager.opToSwitch(code);
@@ -1067,7 +1105,7 @@
if (uidMode != AppOpsManager.MODE_ALLOWED) {
if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
- + packageName);
+ + resolvedPackageName);
op.rejectTime = System.currentTimeMillis();
return uidMode;
}
@@ -1075,12 +1113,13 @@
final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
if (DEBUG) Log.d(TAG, "startOperation: reject #" + op.mode + " for code "
- + switchCode + " (" + code + ") uid " + uid + " package " + packageName);
+ + switchCode + " (" + code + ") uid " + uid + " package "
+ + resolvedPackageName);
op.rejectTime = System.currentTimeMillis();
return switchOp.mode;
}
if (DEBUG) Log.d(TAG, "startOperation: allowing code " + code + " uid " + uid
- + " package " + packageName);
+ + " package " + resolvedPackageName);
if (op.nesting == 0) {
op.time = System.currentTimeMillis();
op.rejectTime = 0;
@@ -1098,9 +1137,16 @@
public void finishOperation(IBinder token, int code, int uid, String packageName) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
- ClientState client = (ClientState)token;
+ String resolvedPackageName = resolvePackageName(uid, packageName);
+ if (resolvedPackageName == null) {
+ return;
+ }
+ if (!(token instanceof ClientState)) {
+ return;
+ }
+ ClientState client = (ClientState) token;
synchronized (this) {
- Op op = getOpLocked(code, uid, packageName, true);
+ Op op = getOpLocked(code, uid, resolvedPackageName, true);
if (op == null) {
return;
}
@@ -1116,6 +1162,9 @@
@Override
public int permissionToOpCode(String permission) {
+ if (permission == null) {
+ return AppOpsManager.OP_NONE;
+ }
return AppOpsManager.permissionToOpCode(permission);
}
@@ -1165,15 +1214,6 @@
return uidState;
}
- private Ops getOpsLocked(int uid, String packageName, boolean edit) {
- if (uid == 0) {
- packageName = "root";
- } else if (uid == Process.SHELL_UID) {
- packageName = "com.android.shell";
- }
- return getOpsRawLocked(uid, packageName, edit);
- }
-
private Ops getOpsRawLocked(int uid, String packageName, boolean edit) {
UidState uidState = getUidStateLocked(uid, edit);
if (uidState == null) {
@@ -1259,7 +1299,7 @@
}
private Op getOpLocked(int code, int uid, String packageName, boolean edit) {
- Ops ops = getOpsLocked(uid, packageName, edit);
+ Ops ops = getOpsRawLocked(uid, packageName, edit);
if (ops == null) {
return null;
}
@@ -1317,7 +1357,7 @@
if (AppOpsManager.opAllowSystemBypassRestriction(code)) {
// If we are the system, bypass user restrictions for certain codes
synchronized (this) {
- Ops ops = getOpsLocked(uid, packageName, true);
+ Ops ops = getOpsRawLocked(uid, packageName, true);
if ((ops != null) && ops.isPrivileged) {
return false;
}
@@ -1582,7 +1622,7 @@
out.startTag(null, "uid");
out.attribute(null, "n", Integer.toString(pkg.getUid()));
synchronized (this) {
- Ops ops = getOpsLocked(pkg.getUid(), pkg.getPackageName(), false);
+ Ops ops = getOpsRawLocked(pkg.getUid(), pkg.getPackageName(), false);
// Should always be present as the list of PackageOps is generated
// from Ops.
if (ops != null) {
@@ -1645,7 +1685,9 @@
int userId = UserHandle.USER_SYSTEM;
String packageName;
String opStr;
+ String modeStr;
int op;
+ int mode;
int packageUid;
Shell(IAppOpsService iface, AppOpsService internal) {
@@ -1681,6 +1723,59 @@
}
}
+ int strModeToMode(String modeStr, PrintWriter err) {
+ switch (modeStr) {
+ case "allow":
+ return AppOpsManager.MODE_ALLOWED;
+ case "deny":
+ return AppOpsManager.MODE_ERRORED;
+ case "ignore":
+ return AppOpsManager.MODE_IGNORED;
+ case "default":
+ return AppOpsManager.MODE_DEFAULT;
+ }
+ try {
+ return Integer.parseInt(modeStr);
+ } catch (NumberFormatException e) {
+ }
+ err.println("Error: Mode " + modeStr + " is not valid");
+ return -1;
+ }
+
+ int parseUserOpMode(int defMode, PrintWriter err) throws RemoteException {
+ userId = UserHandle.USER_CURRENT;
+ opStr = null;
+ modeStr = null;
+ for (String argument; (argument = getNextArg()) != null;) {
+ if ("--user".equals(argument)) {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ } else {
+ if (opStr == null) {
+ opStr = argument;
+ } else if (modeStr == null) {
+ modeStr = argument;
+ break;
+ }
+ }
+ }
+ if (opStr == null) {
+ err.println("Error: Operation not specified.");
+ return -1;
+ }
+ op = strOpToOp(opStr, err);
+ if (op < 0) {
+ return -1;
+ }
+ if (modeStr != null) {
+ if ((mode=strModeToMode(modeStr, err)) < 0) {
+ return -1;
+ }
+ } else {
+ mode = defMode;
+ }
+ return 0;
+ }
+
int parseUserPackageOp(boolean reqOp, PrintWriter err) throws RemoteException {
userId = UserHandle.USER_CURRENT;
packageName = null;
@@ -1742,6 +1837,8 @@
pw.println(" Set the mode for a particular application and operation.");
pw.println(" get [--user <USER_ID>] <PACKAGE> [<OP>]");
pw.println(" Return the mode for a particular application and optional operation.");
+ pw.println(" query-op [--user <USER_ID>] <OP> [<MODE>]");
+ pw.println(" Print all packages that currently have the given op in the given mode.");
pw.println(" reset [--user <USER_ID>] [<PACKAGE>]");
pw.println(" Reset the given application or all applications to default modes.");
pw.println(" write-settings");
@@ -1775,23 +1872,9 @@
return -1;
}
- final int mode;
- switch (modeStr) {
- case "allow":
- mode = AppOpsManager.MODE_ALLOWED;
- break;
- case "deny":
- mode = AppOpsManager.MODE_ERRORED;
- break;
- case "ignore":
- mode = AppOpsManager.MODE_IGNORED;
- break;
- case "default":
- mode = AppOpsManager.MODE_DEFAULT;
- break;
- default:
- err.println("Error: Mode " + modeStr + " is not valid,");
- return -1;
+ final int mode = shell.strModeToMode(modeStr, err);
+ if (mode < 0) {
+ return -1;
}
shell.mInterface.setMode(shell.op, shell.packageUid, shell.packageName, mode);
@@ -1856,6 +1939,34 @@
}
return 0;
}
+ case "query-op": {
+ int res = shell.parseUserOpMode(AppOpsManager.MODE_IGNORED, err);
+ if (res < 0) {
+ return res;
+ }
+ List<AppOpsManager.PackageOps> ops = shell.mInterface.getPackagesForOps(
+ new int[] {shell.op});
+ if (ops == null || ops.size() <= 0) {
+ pw.println("No operations.");
+ return 0;
+ }
+ for (int i=0; i<ops.size(); i++) {
+ final AppOpsManager.PackageOps pkg = ops.get(i);
+ boolean hasMatch = false;
+ final List<AppOpsManager.OpEntry> entries = ops.get(i).getOps();
+ for (int j=0; j<entries.size(); j++) {
+ AppOpsManager.OpEntry ent = entries.get(j);
+ if (ent.getOp() == shell.op && ent.getMode() == shell.mode) {
+ hasMatch = true;
+ break;
+ }
+ }
+ if (hasMatch) {
+ pw.println(pkg.getPackageName());
+ }
+ }
+ return 0;
+ }
case "reset": {
String packageName = null;
int userId = UserHandle.USER_CURRENT;
@@ -2103,6 +2214,7 @@
@Override
public void setUserRestrictions(Bundle restrictions, IBinder token, int userHandle) {
checkSystemUid("setUserRestrictions");
+ Preconditions.checkNotNull(restrictions);
Preconditions.checkNotNull(token);
final boolean[] opRestrictions = getOrCreateUserRestrictionsForToken(token, userHandle);
for (int i = 0; i < opRestrictions.length; ++i) {
@@ -2173,6 +2285,10 @@
pruneUserRestrictionsForToken(token, userHandle);
}
+ notifyWatchersOfChange(code);
+ }
+
+ private void notifyWatchersOfChange(int code) {
final ArrayList<Callback> clonedCallbacks;
synchronized (this) {
ArrayList<Callback> callbacks = mOpModeWatchers.get(code);
@@ -2317,6 +2433,17 @@
}
}
+ private static String resolvePackageName(int uid, String packageName) {
+ if (uid == 0) {
+ return "root";
+ } else if (uid == Process.SHELL_UID) {
+ return "com.android.shell";
+ } else if (uid == Process.SYSTEM_UID && packageName == null) {
+ return "android";
+ }
+ return packageName;
+ }
+
private static String[] getPackagesForUid(int uid) {
String[] packageNames = null;
try {
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 799a763..0a814ab 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -92,6 +92,7 @@
private static final int MESSAGE_BLUETOOTH_STATE_CHANGE = 60;
private static final int MESSAGE_TIMEOUT_BIND = 100;
private static final int MESSAGE_TIMEOUT_UNBIND = 101;
+ private static final int MESSAGE_GET_NAME_AND_ADDRESS = 200;
private static final int MESSAGE_USER_SWITCHED = 300;
private static final int MESSAGE_USER_UNLOCKED = 301;
private static final int MESSAGE_ADD_PROXY_DELAYED = 400;
@@ -599,8 +600,8 @@
sendEnableMsg(true);
}
return true;
-
}
+
public boolean enable() {
if ((Binder.getCallingUid() != Process.SYSTEM_UID) &&
(!checkIfCallerIsForegroundUser())) {
@@ -763,9 +764,8 @@
sendEnableMsg(mQuietEnableExternal);
} else if (!isNameAndAddressSet()) {
if (DBG) Slog.d(TAG, "Getting adapter name and address");
- enable();
- waitForOnOff(true, false);
- disable(true);
+ Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
+ mHandler.sendMessage(getMsg);
}
}
@@ -1076,6 +1076,8 @@
private BluetoothServiceConnection mConnection = new BluetoothServiceConnection();
private class BluetoothHandler extends Handler {
+ boolean mGetNameAddressOnly = false;
+
public BluetoothHandler(Looper looper) {
super(looper);
}
@@ -1084,6 +1086,37 @@
public void handleMessage(Message msg) {
if (DBG) Slog.d (TAG, "Message: " + msg.what);
switch (msg.what) {
+ case MESSAGE_GET_NAME_AND_ADDRESS:
+ if (DBG) Slog.d(TAG, "MESSAGE_GET_NAME_AND_ADDRESS");
+ synchronized(mConnection) {
+ if ((mBluetooth == null) && (!mBinding)) {
+ if (DBG) Slog.d(TAG, "Binding to service to get name and address");
+ mGetNameAddressOnly = true;
+ Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
+ mHandler.sendMessageDelayed(timeoutMsg, TIMEOUT_BIND_MS);
+ Intent i = new Intent(IBluetooth.class.getName());
+ if (!doBind(i, mConnection,
+ Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
+ UserHandle.CURRENT)) {
+ mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
+ } else {
+ mBinding = true;
+ }
+ } else if (mBluetooth != null) {
+ try {
+ storeNameAndAddress(mBluetooth.getName(),
+ mBluetooth.getAddress());
+ } catch (RemoteException re) {
+ Slog.e(TAG, "Unable to grab names", re);
+ }
+ if (mGetNameAddressOnly && !mEnable) {
+ unbindAndFinish();
+ }
+ mGetNameAddressOnly = false;
+ }
+ }
+ break;
+
case MESSAGE_ENABLE:
if (DBG) {
Slog.d(TAG, "MESSAGE_ENABLE: mBluetooth = " + mBluetooth);
@@ -1177,6 +1210,12 @@
mBluetoothBinder = service;
mBluetooth = IBluetooth.Stub.asInterface(service);
+ if (!isNameAndAddressSet()) {
+ Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
+ mHandler.sendMessage(getMsg);
+ if (mGetNameAddressOnly) return;
+ }
+
try {
boolean enableHciSnoopLog = (Settings.Secure.getInt(mContentResolver,
Settings.Secure.BLUETOOTH_HCI_LOG, 0) == 1);
@@ -1187,15 +1226,6 @@
Slog.e(TAG,"Unable to call configHciSnoopLog", e);
}
- if (!isNameAndAddressSet()) {
- try {
- storeNameAndAddress(mBluetooth.getName(),
- mBluetooth.getAddress());
- } catch (RemoteException re) {
- Slog.e(TAG, "Unable to grab names", re);
- }
- }
-
//Register callback object
try {
mBluetooth.registerCallback(mBluetoothCallback);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index b1398f1..82a36b4 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -71,6 +71,7 @@
import android.net.RouteInfo;
import android.net.UidRange;
import android.net.Uri;
+import android.net.metrics.ConnectivityServiceChangeEvent;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -125,7 +126,6 @@
import com.android.server.connectivity.NetworkMonitor;
import com.android.server.connectivity.PacManager;
import com.android.server.connectivity.PermissionMonitor;
-import com.android.server.connectivity.ApfFilter;
import com.android.server.connectivity.Tethering;
import com.android.server.connectivity.Vpn;
import com.android.server.net.BaseNetworkObserver;
@@ -354,13 +354,6 @@
*/
private static final int EVENT_REGISTER_NETWORK_LISTENER_WITH_INTENT = 31;
- /**
- * used to push APF program to NetworkAgent
- * replyTo = NetworkAgent message handler
- * obj = byte[] of APF program
- */
- private static final int EVENT_PUSH_APF_PROGRAM_TO_NETWORK = 32;
-
/** Handler thread used for both of the handlers below. */
@VisibleForTesting
protected final HandlerThread mHandlerThread;
@@ -378,8 +371,6 @@
private int mNetTransitionWakeLockTimeout;
private final PowerManager.WakeLock mPendingIntentWakeLock;
- private InetAddress mDefaultDns;
-
// used in DBG mode to track inet condition reports
private static final int INET_CONDITION_LOG_MAX_SIZE = 15;
private ArrayList mInetLog;
@@ -652,19 +643,6 @@
}
}
- // read our default dns server ip
- String dns = Settings.Global.getString(context.getContentResolver(),
- Settings.Global.DEFAULT_DNS_SERVER);
- if (dns == null || dns.length() == 0) {
- dns = context.getResources().getString(
- com.android.internal.R.string.config_default_dns_server);
- }
- try {
- mDefaultDns = NetworkUtils.numericToInetAddress(dns);
- } catch (IllegalArgumentException e) {
- loge("Error setting defaultDns using " + dns);
- }
-
mReleasePendingIntentDelayMs = Settings.Secure.getInt(context.getContentResolver(),
Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, 5_000);
@@ -1802,20 +1780,6 @@
}
}
- private void dumpApf(IndentingPrintWriter pw) {
- pw.println("APF filters:");
- pw.increaseIndent();
- for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
- if (nai.apfFilter != null) {
- pw.println(nai.name() + ":");
- pw.increaseIndent();
- nai.apfFilter.dump(pw);
- pw.decreaseIndent();
- }
- }
- pw.decreaseIndent();
- }
-
@Override
protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
@@ -1833,11 +1797,6 @@
return;
}
- if (argsContain(args, "apf")) {
- dumpApf(pw);
- return;
- }
-
pw.print("NetworkFactories for:");
for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
pw.print(" " + nfi.name);
@@ -1901,7 +1860,6 @@
mKeepaliveTracker.dump(pw);
pw.println();
- dumpApf(pw);
if (mInetLog != null && mInetLog.size() > 0) {
pw.println();
@@ -2223,7 +2181,6 @@
mKeepaliveTracker.handleStopAllKeepalives(nai,
ConnectivityManager.PacketKeepalive.ERROR_INVALID_NETWORK);
nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED);
- if (nai.apfFilter != null) nai.apfFilter.shutdown();
mNetworkAgentInfos.remove(msg.replyTo);
updateClat(null, nai.linkProperties, nai);
synchronized (mNetworkForNetId) {
@@ -2438,13 +2395,6 @@
accept ? 1 : 0, always ? 1: 0, network));
}
- public void pushApfProgramToNetwork(NetworkAgentInfo nai, byte[] program) {
- enforceConnectivityInternalPermission();
- Message msg = mHandler.obtainMessage(EVENT_PUSH_APF_PROGRAM_TO_NETWORK, program);
- msg.replyTo = nai.messenger;
- mHandler.sendMessage(msg);
- }
-
private void handleSetAcceptUnvalidated(Network network, boolean accept, boolean always) {
if (DBG) log("handleSetAcceptUnvalidated network=" + network +
" accept=" + accept + " always=" + always);
@@ -2594,16 +2544,6 @@
handleMobileDataAlwaysOn();
break;
}
- case EVENT_PUSH_APF_PROGRAM_TO_NETWORK: {
- NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
- if (nai == null) {
- loge("EVENT_PUSH_APF_PROGRAM_TO_NETWORK from unknown NetworkAgent");
- } else {
- nai.asyncChannel.sendMessage(NetworkAgent.CMD_PUSH_APF_PROGRAM,
- (byte[]) msg.obj);
- }
- break;
- }
// Sent by KeepaliveTracker to process an app request on the state machine thread.
case NetworkAgent.CMD_START_PACKET_KEEPALIVE: {
mKeepaliveTracker.handleStartKeepalive(msg);
@@ -4185,9 +4125,6 @@
if (networkAgent.clatd != null) {
networkAgent.clatd.fixupLinkProperties(oldLp);
}
- if (networkAgent.apfFilter != null) {
- networkAgent.apfFilter.updateFilter();
- }
updateInterfaces(newLp, oldLp, netId);
updateMtu(newLp, oldLp);
@@ -4197,14 +4134,8 @@
// }
updateTcpBufferSizes(networkAgent);
- // TODO: deprecate and remove mDefaultDns when we can do so safely. See http://b/18327075
- // In L, we used it only when the network had Internet access but provided no DNS servers.
- // For now, just disable it, and if disabling it doesn't break things, remove it.
- // final boolean useDefaultDns = networkAgent.networkCapabilities.hasCapability(
- // NET_CAPABILITY_INTERNET);
- final boolean useDefaultDns = false;
- final boolean flushDns = updateRoutes(newLp, oldLp, netId);
- updateDnses(newLp, oldLp, netId, flushDns, useDefaultDns);
+ updateRoutes(newLp, oldLp, netId);
+ updateDnses(newLp, oldLp, netId);
updateClat(newLp, oldLp, networkAgent);
if (isDefaultNetwork(networkAgent)) {
@@ -4307,37 +4238,24 @@
return !routeDiff.added.isEmpty() || !routeDiff.removed.isEmpty();
}
- private void updateDnses(LinkProperties newLp, LinkProperties oldLp, int netId,
- boolean flush, boolean useDefaultDns) {
- if (oldLp == null || (newLp.isIdenticalDnses(oldLp) == false)) {
- Collection<InetAddress> dnses = newLp.getDnsServers();
- if (dnses.size() == 0 && mDefaultDns != null && useDefaultDns) {
- dnses = new ArrayList();
- dnses.add(mDefaultDns);
- if (DBG) {
- loge("no dns provided for netId " + netId + ", so using defaults");
- }
- }
- if (DBG) log("Setting Dns servers for network " + netId + " to " + dnses);
- try {
- mNetd.setDnsServersForNetwork(netId, NetworkUtils.makeStrings(dnses),
- newLp.getDomains());
- } catch (Exception e) {
- loge("Exception in setDnsServersForNetwork: " + e);
- }
- final NetworkAgentInfo defaultNai = getDefaultNetwork();
- if (defaultNai != null && defaultNai.network.netId == netId) {
- setDefaultDnsSystemProperties(dnses);
- }
- flushVmDnsCache();
- } else if (flush) {
- try {
- mNetd.flushNetworkDnsCache(netId);
- } catch (Exception e) {
- loge("Exception in flushNetworkDnsCache: " + e);
- }
- flushVmDnsCache();
+ private void updateDnses(LinkProperties newLp, LinkProperties oldLp, int netId) {
+ if (oldLp != null && newLp.isIdenticalDnses(oldLp)) {
+ return; // no updating necessary
}
+
+ Collection<InetAddress> dnses = newLp.getDnsServers();
+ if (DBG) log("Setting Dns servers for network " + netId + " to " + dnses);
+ try {
+ mNetd.setDnsServersForNetwork(
+ netId, NetworkUtils.makeStrings(dnses), newLp.getDomains());
+ } catch (Exception e) {
+ loge("Exception in setDnsServersForNetwork: " + e);
+ }
+ final NetworkAgentInfo defaultNai = getDefaultNetwork();
+ if (defaultNai != null && defaultNai.network.netId == netId) {
+ setDefaultDnsSystemProperties(dnses);
+ }
+ flushVmDnsCache();
}
private void setDefaultDnsSystemProperties(Collection<InetAddress> dnses) {
@@ -4511,6 +4429,7 @@
private void makeDefault(NetworkAgentInfo newNetwork) {
if (DBG) log("Switching to new default network: " + newNetwork);
+ ConnectivityServiceChangeEvent.logEvent(newNetwork.network.netId);
setupDataActivityTracking(newNetwork);
try {
mNetd.setDefaultNetId(newNetwork.network.netId);
@@ -5141,5 +5060,4 @@
NetworkAgentInfo nai, NetworkRequest defaultRequest) {
return new NetworkMonitor(context, handler, nai, defaultRequest);
}
-
}
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index b57e14e..ccb4647 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -108,11 +108,12 @@
private static final boolean COMPRESS_TIME = false;
- private static final int EVENT_BUFFER_SIZE = 40;
+ private static final int EVENT_BUFFER_SIZE = 100;
private AlarmManager mAlarmManager;
private IBatteryStats mBatteryStats;
private PowerManagerInternal mLocalPowerManager;
+ private PowerManager mPowerManager;
private AlarmManagerService.LocalService mLocalAlarmManager;
private INetworkPolicyManager mNetworkPolicyManager;
private DisplayManager mDisplayManager;
@@ -168,16 +169,19 @@
private static final int LIGHT_STATE_ACTIVE = 0;
/** Device is inactive (screen off) and we are waiting to for the first light idle. */
private static final int LIGHT_STATE_INACTIVE = 1;
+ /** Device is about to go idle for the first time, wait for current work to complete. */
+ private static final int LIGHT_STATE_PRE_IDLE = 3;
/** Device is in the light idle state, trying to stay asleep as much as possible. */
- private static final int LIGHT_STATE_IDLE = 2;
+ private static final int LIGHT_STATE_IDLE = 4;
/** Device is in the light idle state, but temporarily out of idle to do regular maintenance. */
- private static final int LIGHT_STATE_IDLE_MAINTENANCE = 3;
+ private static final int LIGHT_STATE_IDLE_MAINTENANCE = 5;
/** Device light idle state is overriden, now applying deep doze state. */
- private static final int LIGHT_STATE_OVERRIDE = 4;
+ private static final int LIGHT_STATE_OVERRIDE = 6;
private static String lightStateToString(int state) {
switch (state) {
case LIGHT_STATE_ACTIVE: return "ACTIVE";
case LIGHT_STATE_INACTIVE: return "INACTIVE";
+ case LIGHT_STATE_PRE_IDLE: return "PRE_IDLE";
case LIGHT_STATE_IDLE: return "IDLE";
case LIGHT_STATE_IDLE_MAINTENANCE: return "IDLE_MAINTENANCE";
case LIGHT_STATE_OVERRIDE: return "OVERRIDE";
@@ -192,11 +196,14 @@
private long mNextAlarmTime;
private long mNextIdlePendingDelay;
private long mNextIdleDelay;
+ private long mNextLightIdleDelay;
private long mNextLightAlarmTime;
+ private long mNextSensingTimeoutAlarmTime;
private long mCurIdleBudget;
private long mMaintenanceStartTime;
private int mActiveIdleOpCount;
+ private PowerManager.WakeLock mActiveIdleWakeLock;
private IBinder mDownloadServiceActive;
private boolean mJobsActive;
private boolean mAlarmsActive;
@@ -333,6 +340,18 @@
}
};
+ private final AlarmManager.OnAlarmListener mSensingTimeoutAlarmListener
+ = new AlarmManager.OnAlarmListener() {
+ @Override
+ public void onAlarm() {
+ if (mState == STATE_SENSING) {
+ synchronized (DeviceIdleController.this) {
+ becomeInactiveIfAppropriateLocked();
+ }
+ }
+ }
+ };
+
private final AlarmManager.OnAlarmListener mDeepAlarmListener
= new AlarmManager.OnAlarmListener() {
@Override
@@ -343,19 +362,18 @@
}
};
- private final AlarmManager.OnAlarmListener mMaintenanceMinCheckListener
- = new AlarmManager.OnAlarmListener() {
- @Override
- public void onAlarm() {
- synchronized (DeviceIdleController.this) {
- exitMaintenanceEarlyIfNeededLocked();
- }
- }
- };
-
private final BroadcastReceiver mIdleStartedDoneReceiver = new BroadcastReceiver() {
@Override public void onReceive(Context context, Intent intent) {
- decActiveIdleOps();
+ // When coming out of a deep idle, we will add in some delay before we allow
+ // the system to settle down and finish the maintenance window. This is
+ // to give a chance for any pending work to be scheduled.
+ if (PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(intent.getAction())) {
+ mHandler.sendEmptyMessageDelayed(MSG_FINISH_IDLE_OP,
+ mConstants.MIN_DEEP_MAINTENANCE_TIME);
+ } else {
+ mHandler.sendEmptyMessageDelayed(MSG_FINISH_IDLE_OP,
+ mConstants.MIN_LIGHT_MAINTENANCE_TIME);
+ }
}
};
@@ -477,7 +495,12 @@
*/
private final class Constants extends ContentObserver {
// Key names stored in the settings value.
+ private static final String KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT
+ = "light_after_inactive_to";
+ private static final String KEY_LIGHT_PRE_IDLE_TIMEOUT = "light_pre_idle_to";
private static final String KEY_LIGHT_IDLE_TIMEOUT = "light_idle_to";
+ private static final String KEY_LIGHT_IDLE_FACTOR = "light_idle_factor";
+ private static final String KEY_LIGHT_MAX_IDLE_TIMEOUT = "light_max_idle_to";
private static final String KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET
= "light_idle_maintenance_min_budget";
private static final String KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET
@@ -505,14 +528,44 @@
"sms_temp_app_whitelist_duration";
/**
- * This is the time, after becoming inactive, that we will start going
- * in to light-weight idle mode.
+ * This is the time, after becoming inactive, that we go in to the first
+ * light-weight idle mode.
+ * @see Settings.Global#DEVICE_IDLE_CONSTANTS
+ * @see #KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT
+ */
+ public long LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT;
+
+ /**
+ * This is amount of time we will wait from the point where we decide we would
+ * like to go idle until we actually do, while waiting for jobs and other current
+ * activity to finish.
+ * @see Settings.Global#DEVICE_IDLE_CONSTANTS
+ * @see #KEY_LIGHT_PRE_IDLE_TIMEOUT
+ */
+ public long LIGHT_PRE_IDLE_TIMEOUT;
+
+ /**
+ * This is the initial time that we will run in idle maintenance mode.
* @see Settings.Global#DEVICE_IDLE_CONSTANTS
* @see #KEY_LIGHT_IDLE_TIMEOUT
*/
public long LIGHT_IDLE_TIMEOUT;
/**
+ * Scaling factor to apply to the light idle mode time each time we complete a cycle.
+ * @see Settings.Global#DEVICE_IDLE_CONSTANTS
+ * @see #KEY_LIGHT_IDLE_FACTOR
+ */
+ public float LIGHT_IDLE_FACTOR;
+
+ /**
+ * This is the maximum time we will run in idle maintenence mode.
+ * @see Settings.Global#DEVICE_IDLE_CONSTANTS
+ * @see #KEY_LIGHT_MAX_IDLE_TIMEOUT
+ */
+ public long LIGHT_MAX_IDLE_TIMEOUT;
+
+ /**
* This is the minimum amount of time we want to make available for maintenance mode
* when lightly idling. That is, we will always have at least this amount of time
* available maintenance before timing out and cutting off maintenance mode.
@@ -684,13 +737,18 @@
public long SMS_TEMP_APP_WHITELIST_DURATION;
private final ContentResolver mResolver;
+ private final boolean mHasWatch;
private final KeyValueListParser mParser = new KeyValueListParser(',');
public Constants(Handler handler, ContentResolver resolver) {
super(handler);
mResolver = resolver;
- mResolver.registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.DEVICE_IDLE_CONSTANTS), false, this);
+ mHasWatch = getContext().getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WATCH);
+ mResolver.registerContentObserver(Settings.Global.getUriFor(
+ mHasWatch ? Settings.Global.DEVICE_IDLE_CONSTANTS_WATCH
+ : Settings.Global.DEVICE_IDLE_CONSTANTS),
+ false, this);
updateConstants();
}
@@ -703,14 +761,24 @@
synchronized (DeviceIdleController.this) {
try {
mParser.setString(Settings.Global.getString(mResolver,
- Settings.Global.DEVICE_IDLE_CONSTANTS));
+ mHasWatch ? Settings.Global.DEVICE_IDLE_CONSTANTS_WATCH
+ : Settings.Global.DEVICE_IDLE_CONSTANTS));
} catch (IllegalArgumentException e) {
// Failed to parse the settings string, log this and move on
// with defaults.
Slog.e(TAG, "Bad device idle settings", e);
}
+ LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = mParser.getLong(
+ KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT,
+ !COMPRESS_TIME ? 5 * 60 * 1000L : 15 * 1000L);
+ LIGHT_PRE_IDLE_TIMEOUT = mParser.getLong(KEY_LIGHT_PRE_IDLE_TIMEOUT,
+ !COMPRESS_TIME ? 10 * 60 * 1000L : 30 * 1000L);
LIGHT_IDLE_TIMEOUT = mParser.getLong(KEY_LIGHT_IDLE_TIMEOUT,
+ !COMPRESS_TIME ? 5 * 60 * 1000L : 15 * 1000L);
+ LIGHT_IDLE_FACTOR = mParser.getFloat(KEY_LIGHT_IDLE_FACTOR,
+ 2f);
+ LIGHT_MAX_IDLE_TIMEOUT = mParser.getLong(KEY_LIGHT_MAX_IDLE_TIMEOUT,
!COMPRESS_TIME ? 15 * 60 * 1000L : 60 * 1000L);
LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = mParser.getLong(
KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET,
@@ -724,8 +792,9 @@
MIN_DEEP_MAINTENANCE_TIME = mParser.getLong(
KEY_MIN_DEEP_MAINTENANCE_TIME,
!COMPRESS_TIME ? 30 * 1000L : 5 * 1000L);
+ long inactiveTimeoutDefault = (mHasWatch ? 15 : 30) * 60 * 1000L;
INACTIVE_TIMEOUT = mParser.getLong(KEY_INACTIVE_TIMEOUT,
- !COMPRESS_TIME ? 30 * 60 * 1000L : 3 * 60 * 1000L);
+ !COMPRESS_TIME ? inactiveTimeoutDefault : (inactiveTimeoutDefault / 10));
SENSING_TIMEOUT = mParser.getLong(KEY_SENSING_TIMEOUT,
!DEBUG ? 4 * 60 * 1000L : 60 * 1000L);
LOCATING_TIMEOUT = mParser.getLong(KEY_LOCATING_TIMEOUT,
@@ -733,8 +802,10 @@
LOCATION_ACCURACY = mParser.getFloat(KEY_LOCATION_ACCURACY, 20);
MOTION_INACTIVE_TIMEOUT = mParser.getLong(KEY_MOTION_INACTIVE_TIMEOUT,
!COMPRESS_TIME ? 10 * 60 * 1000L : 60 * 1000L);
+ long idleAfterInactiveTimeout = (mHasWatch ? 15 : 30) * 60 * 1000L;
IDLE_AFTER_INACTIVE_TIMEOUT = mParser.getLong(KEY_IDLE_AFTER_INACTIVE_TIMEOUT,
- !COMPRESS_TIME ? 30 * 60 * 1000L : 3 * 60 * 1000L);
+ !COMPRESS_TIME ? idleAfterInactiveTimeout
+ : (idleAfterInactiveTimeout / 10));
IDLE_PENDING_TIMEOUT = mParser.getLong(KEY_IDLE_PENDING_TIMEOUT,
!COMPRESS_TIME ? 5 * 60 * 1000L : 30 * 1000L);
MAX_IDLE_PENDING_TIMEOUT = mParser.getLong(KEY_MAX_IDLE_PENDING_TIMEOUT,
@@ -761,10 +832,26 @@
void dump(PrintWriter pw) {
pw.println(" Settings:");
+ pw.print(" "); pw.print(KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT); pw.print("=");
+ TimeUtils.formatDuration(LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT, pw);
+ pw.println();
+
+ pw.print(" "); pw.print(KEY_LIGHT_PRE_IDLE_TIMEOUT); pw.print("=");
+ TimeUtils.formatDuration(LIGHT_PRE_IDLE_TIMEOUT, pw);
+ pw.println();
+
pw.print(" "); pw.print(KEY_LIGHT_IDLE_TIMEOUT); pw.print("=");
TimeUtils.formatDuration(LIGHT_IDLE_TIMEOUT, pw);
pw.println();
+ pw.print(" "); pw.print(KEY_LIGHT_IDLE_FACTOR); pw.print("=");
+ pw.print(LIGHT_IDLE_FACTOR);
+ pw.println();
+
+ pw.print(" "); pw.print(KEY_LIGHT_MAX_IDLE_TIMEOUT); pw.print("=");
+ TimeUtils.formatDuration(LIGHT_MAX_IDLE_TIMEOUT, pw);
+ pw.println();
+
pw.print(" "); pw.print(KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET); pw.print("=");
TimeUtils.formatDuration(LIGHT_IDLE_MAINTENANCE_MIN_BUDGET, pw);
pw.println();
@@ -850,6 +937,11 @@
@Override
public void onAnyMotionResult(int result) {
if (DEBUG) Slog.d(TAG, "onAnyMotionResult(" + result + ")");
+ if (result != AnyMotionDetector.RESULT_UNKNOWN) {
+ synchronized (this) {
+ cancelSensingTimeoutAlarmLocked();
+ }
+ }
if (result == AnyMotionDetector.RESULT_MOVED) {
if (DEBUG) Slog.d(TAG, "RESULT_MOVED received.");
synchronized (this) {
@@ -883,6 +975,7 @@
static final int MSG_REPORT_ACTIVE = 5;
static final int MSG_TEMP_APP_WHITELIST_TIMEOUT = 6;
static final int MSG_REPORT_MAINTENANCE_ACTIVITY = 7;
+ static final int MSG_FINISH_IDLE_OP = 8;
final class MyHandler extends Handler {
MyHandler(Looper looper) {
@@ -987,6 +1080,9 @@
mMaintenanceActivityListeners.finishBroadcast();
}
} break;
+ case MSG_FINISH_IDLE_OP: {
+ decActiveIdleOps();
+ } break;
}
}
}
@@ -1243,6 +1339,10 @@
mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
mBatteryStats = BatteryStatsService.getService();
mLocalPowerManager = getLocalService(PowerManagerInternal.class);
+ mPowerManager = getContext().getSystemService(PowerManager.class);
+ mActiveIdleWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ "deviceidle_maint");
+ mActiveIdleWakeLock.setReferenceCounted(false);
mLocalAlarmManager = getLocalService(AlarmManagerService.LocalService.class);
mNetworkPolicyManager = INetworkPolicyManager.Stub.asInterface(
ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
@@ -1631,7 +1731,6 @@
mInactiveTimeout = mConstants.INACTIVE_TIMEOUT;
mCurIdleBudget = 0;
mMaintenanceStartTime = 0;
- mAlarmManager.cancel(mMaintenanceMinCheckListener);
resetIdleManagementLocked();
resetLightIdleManagementLocked();
addEvent(EVENT_NORMAL);
@@ -1654,7 +1753,7 @@
mLightState = LIGHT_STATE_INACTIVE;
if (DEBUG) Slog.d(TAG, "Moved from LIGHT_STATE_ACTIVE to LIGHT_STATE_INACTIVE");
resetLightIdleManagementLocked();
- scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_TIMEOUT);
+ scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT);
EventLogTags.writeDeviceIdleLight(mLightState, "no activity");
}
}
@@ -1663,7 +1762,9 @@
void resetIdleManagementLocked() {
mNextIdlePendingDelay = 0;
mNextIdleDelay = 0;
+ mNextLightIdleDelay = 0;
cancelAlarmLocked();
+ cancelSensingTimeoutAlarmLocked();
cancelLocatingLocked();
stopMonitoringMotionLocked();
mAnyMotionDetector.stop();
@@ -1695,7 +1796,19 @@
switch (mLightState) {
case LIGHT_STATE_INACTIVE:
mCurIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;
+ // Reset the upcoming idle delays.
+ mNextLightIdleDelay = mConstants.LIGHT_IDLE_TIMEOUT;
mMaintenanceStartTime = 0;
+ if (!isOpsInactiveLocked()) {
+ // We have some active ops going on... give them a chance to finish
+ // before going in to our first idle.
+ mLightState = LIGHT_STATE_PRE_IDLE;
+ EventLogTags.writeDeviceIdleLight(mLightState, reason);
+ scheduleLightAlarmLocked(mConstants.LIGHT_PRE_IDLE_TIMEOUT);
+ break;
+ }
+ // Nothing active, fall through to immediately idle.
+ case LIGHT_STATE_PRE_IDLE:
case LIGHT_STATE_IDLE_MAINTENANCE:
if (mMaintenanceStartTime != 0) {
long duration = SystemClock.elapsedRealtime() - mMaintenanceStartTime;
@@ -1708,17 +1821,22 @@
}
}
mMaintenanceStartTime = 0;
- scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_TIMEOUT);
+ scheduleLightAlarmLocked(mNextLightIdleDelay);
+ mNextLightIdleDelay = Math.min(mConstants.LIGHT_MAX_IDLE_TIMEOUT,
+ (long)(mNextLightIdleDelay * mConstants.LIGHT_IDLE_FACTOR));
+ if (mNextLightIdleDelay < mConstants.LIGHT_IDLE_TIMEOUT) {
+ mNextLightIdleDelay = mConstants.LIGHT_IDLE_TIMEOUT;
+ }
if (DEBUG) Slog.d(TAG, "Moved to LIGHT_STATE_IDLE.");
mLightState = LIGHT_STATE_IDLE;
EventLogTags.writeDeviceIdleLight(mLightState, reason);
addEvent(EVENT_LIGHT_IDLE);
mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON_LIGHT);
- mAlarmManager.cancel(mMaintenanceMinCheckListener);
break;
case LIGHT_STATE_IDLE:
// We have been idling long enough, now it is time to do some work.
mActiveIdleOpCount = 1;
+ mActiveIdleWakeLock.acquire();
mMaintenanceStartTime = SystemClock.elapsedRealtime();
if (mCurIdleBudget < mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET) {
mCurIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;
@@ -1732,9 +1850,6 @@
EventLogTags.writeDeviceIdleLight(mLightState, reason);
addEvent(EVENT_LIGHT_MAINTENANCE);
mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF);
- mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME,
- mMaintenanceStartTime + mConstants.MIN_LIGHT_MAINTENANCE_TIME,
- "DeviceIdleController.maint-check", mMaintenanceMinCheckListener, mHandler);
break;
}
}
@@ -1770,15 +1885,16 @@
mState = STATE_SENSING;
if (DEBUG) Slog.d(TAG, "Moved from STATE_IDLE_PENDING to STATE_SENSING.");
EventLogTags.writeDeviceIdle(mState, reason);
- scheduleAlarmLocked(mConstants.SENSING_TIMEOUT, false);
+ scheduleSensingTimeoutAlarmLocked(mConstants.SENSING_TIMEOUT);
cancelLocatingLocked();
- mAnyMotionDetector.checkForAnyMotion();
mNotMoving = false;
mLocated = false;
mLastGenericLocation = null;
mLastGpsLocation = null;
+ mAnyMotionDetector.checkForAnyMotion();
break;
case STATE_SENSING:
+ cancelSensingTimeoutAlarmLocked();
mState = STATE_LOCATING;
if (DEBUG) Slog.d(TAG, "Moved from STATE_SENSING to STATE_LOCATING.");
EventLogTags.writeDeviceIdle(mState, reason);
@@ -1811,6 +1927,7 @@
cancelAlarmLocked();
cancelLocatingLocked();
mAnyMotionDetector.stop();
+
case STATE_IDLE_MAINTENANCE:
scheduleAlarmLocked(mNextIdleDelay, true);
if (DEBUG) Slog.d(TAG, "Moved to STATE_IDLE. Next alarm in " + mNextIdleDelay +
@@ -1818,6 +1935,9 @@
mNextIdleDelay = (long)(mNextIdleDelay * mConstants.IDLE_FACTOR);
if (DEBUG) Slog.d(TAG, "Setting mNextIdleDelay = " + mNextIdleDelay);
mNextIdleDelay = Math.min(mNextIdleDelay, mConstants.MAX_IDLE_TIMEOUT);
+ if (mNextIdleDelay < mConstants.IDLE_TIMEOUT) {
+ mNextIdleDelay = mConstants.IDLE_TIMEOUT;
+ }
mState = STATE_IDLE;
if (mLightState != LIGHT_STATE_OVERRIDE) {
mLightState = LIGHT_STATE_OVERRIDE;
@@ -1826,24 +1946,24 @@
EventLogTags.writeDeviceIdle(mState, reason);
addEvent(EVENT_DEEP_IDLE);
mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON);
- mAlarmManager.cancel(mMaintenanceMinCheckListener);
break;
case STATE_IDLE:
// We have been idling long enough, now it is time to do some work.
mActiveIdleOpCount = 1;
+ mActiveIdleWakeLock.acquire();
scheduleAlarmLocked(mNextIdlePendingDelay, false);
if (DEBUG) Slog.d(TAG, "Moved from STATE_IDLE to STATE_IDLE_MAINTENANCE. " +
"Next alarm in " + mNextIdlePendingDelay + " ms.");
mMaintenanceStartTime = SystemClock.elapsedRealtime();
mNextIdlePendingDelay = Math.min(mConstants.MAX_IDLE_PENDING_TIMEOUT,
(long)(mNextIdlePendingDelay * mConstants.IDLE_PENDING_FACTOR));
+ if (mNextIdlePendingDelay < mConstants.IDLE_PENDING_TIMEOUT) {
+ mNextIdlePendingDelay = mConstants.IDLE_PENDING_TIMEOUT;
+ }
mState = STATE_IDLE_MAINTENANCE;
EventLogTags.writeDeviceIdle(mState, reason);
addEvent(EVENT_DEEP_MAINTENANCE);
mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF);
- mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME,
- mMaintenanceStartTime + mConstants.MIN_DEEP_MAINTENANCE_TIME,
- "DeviceIdleController.maint-check", mMaintenanceMinCheckListener, mHandler);
break;
}
}
@@ -1859,6 +1979,7 @@
mActiveIdleOpCount--;
if (mActiveIdleOpCount <= 0) {
exitMaintenanceEarlyIfNeededLocked();
+ mActiveIdleWakeLock.release();
}
}
}
@@ -1930,10 +2051,15 @@
mHandler.sendMessage(msg);
}
+ boolean isOpsInactiveLocked() {
+ return mActiveIdleOpCount <= 0 && mDownloadServiceActive == null
+ && !mJobsActive && !mAlarmsActive;
+ }
+
void exitMaintenanceEarlyIfNeededLocked() {
- if (mState == STATE_IDLE_MAINTENANCE || mLightState == LIGHT_STATE_IDLE_MAINTENANCE) {
- if (mActiveIdleOpCount <= 0 && mDownloadServiceActive == null
- && !mJobsActive && !mAlarmsActive) {
+ if (mState == STATE_IDLE_MAINTENANCE || mLightState == LIGHT_STATE_IDLE_MAINTENANCE
+ || mLightState == LIGHT_STATE_PRE_IDLE) {
+ if (isOpsInactiveLocked()) {
final long now = SystemClock.elapsedRealtime();
if (DEBUG) {
StringBuilder sb = new StringBuilder();
@@ -1944,13 +2070,11 @@
Slog.d(TAG, sb.toString());
}
if (mState == STATE_IDLE_MAINTENANCE) {
- if (now >= (mMaintenanceStartTime + mConstants.MIN_DEEP_MAINTENANCE_TIME)) {
- stepIdleStateLocked("s:early");
- }
+ stepIdleStateLocked("s:early");
+ } else if (mLightState == LIGHT_STATE_PRE_IDLE) {
+ stepLightIdleStateLocked("s:predone");
} else {
- if (now >= (mMaintenanceStartTime + mConstants.MIN_LIGHT_MAINTENANCE_TIME)) {
- stepLightIdleStateLocked("s:early");
- }
+ stepLightIdleStateLocked("s:early");
}
}
}
@@ -2057,6 +2181,13 @@
}
}
+ void cancelSensingTimeoutAlarmLocked() {
+ if (mNextSensingTimeoutAlarmTime != 0) {
+ mNextSensingTimeoutAlarmTime = 0;
+ mAlarmManager.cancel(mSensingTimeoutAlarmListener);
+ }
+ }
+
void scheduleAlarmLocked(long delay, boolean idleUntil) {
if (DEBUG) Slog.d(TAG, "scheduleAlarmLocked(" + delay + ", " + idleUntil + ")");
if (mMotionSensor == null) {
@@ -2090,6 +2221,13 @@
mNextLightAlarmTime, "DeviceIdleController.light", mLightAlarmListener, mHandler);
}
+ void scheduleSensingTimeoutAlarmLocked(long delay) {
+ if (DEBUG) Slog.d(TAG, "scheduleSensingAlarmLocked(" + delay + ")");
+ mNextSensingTimeoutAlarmTime = SystemClock.elapsedRealtime() + delay;
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, mNextSensingTimeoutAlarmTime,
+ "DeviceIdleController.sensing", mSensingTimeoutAlarmListener, mHandler);
+ }
+
private static int[] buildAppIdArray(ArrayMap<String, Integer> systemApps,
ArrayMap<String, Integer> userApps, SparseBooleanArray outAppIds) {
outAppIds.clear();
@@ -2726,6 +2864,11 @@
TimeUtils.formatDuration(mNextIdleDelay, pw);
pw.println();
}
+ if (mNextLightIdleDelay != 0) {
+ pw.print(" mNextIdleDelay=");
+ TimeUtils.formatDuration(mNextLightIdleDelay, pw);
+ pw.println();
+ }
if (mNextLightAlarmTime != 0) {
pw.print(" mNextLightAlarmTime=");
TimeUtils.formatDuration(mNextLightAlarmTime, SystemClock.elapsedRealtime(), pw);
diff --git a/services/core/java/com/android/server/DisplayThread.java b/services/core/java/com/android/server/DisplayThread.java
index aa0a805..9ef0259 100644
--- a/services/core/java/com/android/server/DisplayThread.java
+++ b/services/core/java/com/android/server/DisplayThread.java
@@ -17,6 +17,7 @@
package com.android.server;
import android.os.Handler;
+import android.os.Trace;
/**
* Shared singleton foreground thread for the system. This is a thread for
@@ -36,6 +37,7 @@
if (sInstance == null) {
sInstance = new DisplayThread();
sInstance.start();
+ sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_ACTIVITY_MANAGER);
sHandler = new Handler(sInstance.getLooper());
}
}
diff --git a/services/core/java/com/android/server/FgThread.java b/services/core/java/com/android/server/FgThread.java
index 03765db..5f85cba 100644
--- a/services/core/java/com/android/server/FgThread.java
+++ b/services/core/java/com/android/server/FgThread.java
@@ -17,6 +17,7 @@
package com.android.server;
import android.os.Handler;
+import android.os.Trace;
/**
* Shared singleton foreground thread for the system. This is a thread for regular
@@ -38,6 +39,7 @@
if (sInstance == null) {
sInstance = new FgThread();
sInstance.start();
+ sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_ACTIVITY_MANAGER);
sHandler = new Handler(sInstance.getLooper());
}
}
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index d6575e8..aa98648 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -34,6 +34,7 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.provider.Settings;
+import android.util.MutableBoolean;
import android.util.Slog;
import android.view.KeyEvent;
@@ -251,7 +252,8 @@
return isCameraLaunchEnabled(resources) || isCameraDoubleTapPowerEnabled(resources);
}
- public boolean interceptPowerKeyDown(KeyEvent event, boolean interactive) {
+ public boolean interceptPowerKeyDown(KeyEvent event, boolean interactive,
+ MutableBoolean outLaunched) {
boolean launched = false;
boolean intercept = false;
long doubleTapInterval;
@@ -276,6 +278,7 @@
}
}
MetricsLogger.histogram(mContext, "power_double_tap_interval", (int) doubleTapInterval);
+ outLaunched.value = launched;
return intercept && launched;
}
diff --git a/services/core/java/com/android/server/GraphicsStatsService.java b/services/core/java/com/android/server/GraphicsStatsService.java
index 044bb04..ecbe1ca 100644
--- a/services/core/java/com/android/server/GraphicsStatsService.java
+++ b/services/core/java/com/android/server/GraphicsStatsService.java
@@ -51,17 +51,15 @@
* 2) ASHMEM_SIZE (for scratch space used during dumping)
* 3) ASHMEM_SIZE * HISTORY_SIZE
*
- * Currently ASHMEM_SIZE is 256 bytes and HISTORY_SIZE is 20. Assuming
- * the system then also has 10 active rendering processes in the worst case
- * this would end up using under 14KiB (12KiB for the buffers, plus some overhead
- * for userId, pid, package name, and a couple other objects)
+ * This is currently under 20KiB total memory in the worst case of
+ * 20 processes in history + 10 unique active processes.
*
* @hide */
public class GraphicsStatsService extends IGraphicsStats.Stub {
public static final String GRAPHICS_STATS_SERVICE = "graphicsstats";
private static final String TAG = "GraphicsStatsService";
- private static final int ASHMEM_SIZE = 256;
+ private static final int ASHMEM_SIZE = 464;
private static final int HISTORY_SIZE = 20;
private final Context mContext;
diff --git a/services/core/java/com/android/server/HardwarePropertiesManagerService.java b/services/core/java/com/android/server/HardwarePropertiesManagerService.java
index 575d99e..23cf64a 100644
--- a/services/core/java/com/android/server/HardwarePropertiesManagerService.java
+++ b/services/core/java/com/android/server/HardwarePropertiesManagerService.java
@@ -23,6 +23,8 @@
import android.os.CpuUsageInfo;
import android.os.IHardwarePropertiesManager;
import android.os.Process;
+import android.os.UserHandle;
+import com.android.server.vr.VrManagerInternal;
import java.util.Arrays;
@@ -78,14 +80,15 @@
*
* @param callingPackage The calling package name.
*
- * @throws SecurityException if a non profile or device owner or system tries to retrieve
- * information provided by the service.
+ * @throws SecurityException if something other than the profile or device owner, or the
+ * current VR service tries to retrieve information provided by this service.
*/
private void enforceHardwarePropertiesRetrievalAllowed(String callingPackage)
throws SecurityException {
final PackageManager pm = mContext.getPackageManager();
+ int uid = 0;
try {
- final int uid = pm.getPackageUid(callingPackage, 0);
+ uid = pm.getPackageUid(callingPackage, 0);
if (Binder.getCallingUid() != uid) {
throw new SecurityException("The caller has faked the package name.");
}
@@ -93,10 +96,13 @@
throw new SecurityException("The caller has faked the package name.");
}
+ final int userId = UserHandle.getUserId(uid);
+ final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
final DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
if (!dpm.isDeviceOwnerApp(callingPackage) && !dpm.isProfileOwnerApp(callingPackage)
- && Binder.getCallingUid() != Process.SYSTEM_UID) {
- throw new SecurityException("The caller is not a device or profile owner or system.");
+ && !vrService.isCurrentVrListener(callingPackage, userId)) {
+ throw new SecurityException("The caller is not a device or profile owner or bound "
+ + "VrListenerService.");
}
}
}
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 898d5b73..ac7872a 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -15,6 +15,8 @@
package com.android.server;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
import com.android.internal.content.PackageMonitor;
import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController;
import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
@@ -37,6 +39,7 @@
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -125,6 +128,7 @@
import android.widget.RadioButton;
import android.widget.Switch;
import android.widget.TextView;
+import android.widget.Toast;
import java.io.File;
import java.io.FileDescriptor;
@@ -132,6 +136,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
@@ -177,6 +182,12 @@
private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID;
private static final String TAG_TRY_SUPPRESSING_IME_SWITCHER = "TrySuppressingImeSwitcher";
+ @Retention(SOURCE)
+ @IntDef({HardKeyboardBehavior.WIRELESS_AFFORDANCE, HardKeyboardBehavior.WIRED_AFFORDANCE})
+ private @interface HardKeyboardBehavior {
+ int WIRELESS_AFFORDANCE = 0;
+ int WIRED_AFFORDANCE = 1;
+ }
final Context mContext;
final Resources mRes;
@@ -215,7 +226,7 @@
// Ongoing notification
private NotificationManager mNotificationManager;
private KeyguardManager mKeyguardManager;
- private StatusBarManagerService mStatusBar;
+ private @Nullable StatusBarManagerService mStatusBar;
private Notification.Builder mImeSwitcherNotification;
private PendingIntent mImeSwitchPendingIntent;
private boolean mShowOngoingImeSwitcherForPhones;
@@ -452,6 +463,7 @@
private AlertDialog.Builder mDialogBuilder;
private AlertDialog mSwitchingDialog;
private View mSwitchingDialogTitleView;
+ private Toast mSubtypeSwitchedByShortCutToast;
private InputMethodInfo[] mIms;
private int[] mSubtypeIds;
private LocaleList mLastSystemLocales;
@@ -460,6 +472,8 @@
private final MyPackageMonitor mMyPackageMonitor = new MyPackageMonitor();
private final IPackageManager mIPackageManager;
private final String mSlotIme;
+ @HardKeyboardBehavior
+ private final int mHardKeyboardBehavior;
class SettingsObserver extends ContentObserver {
int mUserId;
@@ -852,6 +866,8 @@
mHasFeature = context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_INPUT_METHODS);
mSlotIme = mContext.getString(com.android.internal.R.string.status_bar_ime);
+ mHardKeyboardBehavior = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_externalHardKeyboardBehavior);
Bundle extras = new Bundle();
extras.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, true);
@@ -1070,7 +1086,9 @@
mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
mNotificationManager = mContext.getSystemService(NotificationManager.class);
mStatusBar = statusBar;
- statusBar.setIconVisibility(mSlotIme, false);
+ if (mStatusBar != null) {
+ mStatusBar.setIconVisibility(mSlotIme, false);
+ }
updateSystemUiLocked(mCurToken, mImeWindowVis, mBackDisposition);
mShowOngoingImeSwitcherForPhones = mRes.getBoolean(
com.android.internal.R.bool.show_ongoing_ime_switcher);
@@ -1708,13 +1726,16 @@
if (isScreenLocked()) return false;
if ((visibility & InputMethodService.IME_ACTIVE) == 0) return false;
if (mWindowManagerInternal.isHardKeyboardAvailable()) {
- // When physical keyboard is attached, we show the ime switcher (or notification if
- // NavBar is not available) because SHOW_IME_WITH_HARD_KEYBOARD settings currently
- // exists in the IME switcher dialog. Might be OK to remove this condition once
- // SHOW_IME_WITH_HARD_KEYBOARD settings finds a good place to live.
- return true;
+ if (mHardKeyboardBehavior == HardKeyboardBehavior.WIRELESS_AFFORDANCE) {
+ // When physical keyboard is attached, we show the ime switcher (or notification if
+ // NavBar is not available) because SHOW_IME_WITH_HARD_KEYBOARD settings currently
+ // exists in the IME switcher dialog. Might be OK to remove this condition once
+ // SHOW_IME_WITH_HARD_KEYBOARD settings finds a good place to live.
+ return true;
+ }
+ } else if ((visibility & InputMethodService.IME_VISIBLE) == 0) {
+ return false;
}
- if ((visibility & InputMethodService.IME_VISIBLE) == 0) return false;
List<InputMethodInfo> imis = mSettings.getEnabledInputMethodListLocked();
final int N = imis.size();
@@ -2949,6 +2970,27 @@
return;
}
setInputMethodLocked(nextSubtype.mImi.getId(), nextSubtype.mSubtypeId);
+ if (mSubtypeSwitchedByShortCutToast != null) {
+ mSubtypeSwitchedByShortCutToast.cancel();
+ mSubtypeSwitchedByShortCutToast = null;
+ }
+ if ((mImeWindowVis & InputMethodService.IME_VISIBLE) != 0) {
+ // IME window is shown. The user should be able to visually understand that the
+ // subtype is changed in most of cases. To avoid UI overlap, we do not show a toast
+ // in this case.
+ return;
+ }
+ final InputMethodInfo newInputMethodInfo = mMethodMap.get(mCurMethodId);
+ if (newInputMethodInfo == null) {
+ return;
+ }
+ final CharSequence toastText = InputMethodUtils.getImeAndSubtypeDisplayName(mContext,
+ newInputMethodInfo, mCurrentSubtype);
+ if (!TextUtils.isEmpty(toastText)) {
+ mSubtypeSwitchedByShortCutToast = Toast.makeText(mContext, toastText.toString(),
+ Toast.LENGTH_SHORT);
+ mSubtypeSwitchedByShortCutToast.show();
+ }
}
}
@@ -3218,16 +3260,6 @@
};
mDialogBuilder.setSingleChoiceItems(adapter, checkedItem, choiceListener);
- if (!isScreenLocked) {
- final OnClickListener positiveListener = new OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int whichButton) {
- showConfigureInputMethods();
- }
- };
- mDialogBuilder.setPositiveButton(
- com.android.internal.R.string.configure_input_methods, positiveListener);
- }
mSwitchingDialog = mDialogBuilder.create();
mSwitchingDialog.setCanceledOnTouchOutside(true);
mSwitchingDialog.getWindow().setType(
@@ -3830,6 +3862,22 @@
}
}
+ private static String imeWindowStatusToString(final int imeWindowVis) {
+ final StringBuilder sb = new StringBuilder();
+ boolean first = true;
+ if ((imeWindowVis & InputMethodService.IME_ACTIVE) != 0) {
+ sb.append("Active");
+ first = false;
+ }
+ if ((imeWindowVis & InputMethodService.IME_VISIBLE) != 0) {
+ if (!first) {
+ sb.append("|");
+ }
+ sb.append("Visible");
+ }
+ return sb.toString();
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
@@ -3877,6 +3925,7 @@
method = mCurMethod;
p.println(" mCurMethod=" + mCurMethod);
p.println(" mEnabledSession=" + mEnabledSession);
+ p.println(" mImeWindowVis=" + imeWindowStatusToString(mImeWindowVis));
p.println(" mShowRequested=" + mShowRequested
+ " mShowExplicitlyRequested=" + mShowExplicitlyRequested
+ " mShowForced=" + mShowForced
diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java
index 43d10c7..3fdcceb 100644
--- a/services/core/java/com/android/server/IntentResolver.java
+++ b/services/core/java/com/android/server/IntentResolver.java
@@ -779,11 +779,11 @@
}
}
- if (hasNonDefaults) {
+ if (debug && hasNonDefaults) {
if (dest.size() == 0) {
- Slog.w(TAG, "resolveIntent failed: found match, but none with CATEGORY_DEFAULT");
+ Slog.v(TAG, "resolveIntent failed: found match, but none with CATEGORY_DEFAULT");
} else if (dest.size() > 1) {
- Slog.w(TAG, "resolveIntent: multiple matches, only some with CATEGORY_DEFAULT");
+ Slog.v(TAG, "resolveIntent: multiple matches, only some with CATEGORY_DEFAULT");
}
}
}
diff --git a/services/core/java/com/android/server/IoThread.java b/services/core/java/com/android/server/IoThread.java
index 0f29857..ad4c194 100644
--- a/services/core/java/com/android/server/IoThread.java
+++ b/services/core/java/com/android/server/IoThread.java
@@ -17,6 +17,7 @@
package com.android.server;
import android.os.Handler;
+import android.os.Trace;
/**
* Shared singleton I/O thread for the system. This is a thread for non-background
@@ -35,6 +36,7 @@
if (sInstance == null) {
sInstance = new IoThread();
sInstance.start();
+ sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_ACTIVITY_MANAGER);
sHandler = new Handler(sInstance.getLooper());
}
}
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 6fb0671..ab0f55e 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -42,7 +42,10 @@
import android.database.sqlite.SQLiteDatabase;
import android.os.Binder;
+import android.os.Bundle;
import android.os.IBinder;
+import android.os.IProgressListener;
+import android.os.Parcel;
import android.os.RemoteException;
import android.os.storage.IMountService;
import android.os.ServiceManager;
@@ -56,6 +59,7 @@
import android.service.gatekeeper.GateKeeperResponse;
import android.service.gatekeeper.IGateKeeperService;
import android.text.TextUtils;
+import android.util.Log;
import android.util.Slog;
import com.android.internal.util.ArrayUtils;
@@ -70,6 +74,8 @@
import java.util.Arrays;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
/**
* Keeps the lock pattern/password data and related settings for each user.
@@ -248,7 +254,7 @@
com.android.internal.R.color.system_notification_accent_color))
.setContentTitle(title)
.setContentText(message)
- .setContentInfo(detail)
+ .setSubText(detail)
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setContentIntent(intent)
.build();
@@ -590,11 +596,37 @@
}
private void unlockUser(int userId, byte[] token, byte[] secret) {
+ // TODO: make this method fully async so we can update UI with progress strings
+ final CountDownLatch latch = new CountDownLatch(1);
+ final IProgressListener listener = new IProgressListener.Stub() {
+ @Override
+ public void onStarted(int id, Bundle extras) throws RemoteException {
+ // Ignored
+ }
+
+ @Override
+ public void onProgress(int id, int progress, Bundle extras) throws RemoteException {
+ // Ignored
+ }
+
+ @Override
+ public void onFinished(int id, Bundle extras) throws RemoteException {
+ Log.d(TAG, "unlockUser finished!");
+ latch.countDown();
+ }
+ };
+
try {
- ActivityManagerNative.getDefault().unlockUser(userId, token, secret);
+ ActivityManagerNative.getDefault().unlockUser(userId, token, secret, listener);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
+
+ try {
+ latch.await(15, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
}
private byte[] getCurrentHandle(int userId) {
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index ccca5ba..440d8b7 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -165,6 +165,7 @@
public void onStart() {
mMountService = new MountService(getContext());
publishBinderService("mount", mMountService);
+ mMountService.start();
}
@Override
@@ -430,9 +431,13 @@
= { "password", "default", "pattern", "pin" };
private final Context mContext;
+
private final NativeDaemonConnector mConnector;
private final NativeDaemonConnector mCryptConnector;
+ private final Thread mConnectorThread;
+ private final Thread mCryptConnectorThread;
+
private volatile boolean mSystemReady = false;
private volatile boolean mBootCompleted = false;
private volatile boolean mDaemonConnected = false;
@@ -1226,7 +1231,8 @@
}
final Intent intent = new Intent(DiskInfo.ACTION_DISK_SCANNED);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
intent.putExtra(DiskInfo.EXTRA_DISK_ID, disk.id);
intent.putExtra(DiskInfo.EXTRA_VOLUME_COUNT, volumeCount);
mHandler.obtainMessage(H_INTERNAL_BROADCAST, intent).sendToTarget();
@@ -1241,6 +1247,11 @@
}
private void onVolumeCreatedLocked(VolumeInfo vol) {
+ if (mPms.isOnlyCoreApps()) {
+ Slog.d(TAG, "System booted in core-only mode; ignoring volume " + vol.getId());
+ return;
+ }
+
if (vol.type == VolumeInfo.TYPE_EMULATED) {
final StorageManager storage = mContext.getSystemService(StorageManager.class);
final VolumeInfo privateVol = storage.findPrivateForEmulated(vol);
@@ -1342,7 +1353,8 @@
intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.id);
intent.putExtra(VolumeInfo.EXTRA_VOLUME_STATE, newState);
intent.putExtra(VolumeRecord.EXTRA_FS_UUID, vol.fsUuid);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
mHandler.obtainMessage(H_INTERNAL_BROADCAST, intent).sendToTarget();
}
@@ -1494,17 +1506,13 @@
null);
mConnector.setDebug(true);
mConnector.setWarnIfHeld(mLock);
-
- Thread thread = new Thread(mConnector, VOLD_TAG);
- thread.start();
+ mConnectorThread = new Thread(mConnector, VOLD_TAG);
// Reuse parameters from first connector since they are tested and safe
mCryptConnector = new NativeDaemonConnector(this, "cryptd",
MAX_CONTAINERS * 2, CRYPTD_TAG, 25, null);
mCryptConnector.setDebug(true);
-
- Thread crypt_thread = new Thread(mCryptConnector, CRYPTD_TAG);
- crypt_thread.start();
+ mCryptConnectorThread = new Thread(mCryptConnector, CRYPTD_TAG);
final IntentFilter userFilter = new IntentFilter();
userFilter.addAction(Intent.ACTION_USER_ADDED);
@@ -1521,6 +1529,11 @@
}
}
+ private void start() {
+ mConnectorThread.start();
+ mCryptConnectorThread.start();
+ }
+
private void systemReady() {
mSystemReady = true;
mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget();
@@ -2912,36 +2925,57 @@
@Override
public StorageVolume[] getVolumeList(int uid, String packageName, int flags) {
final int userId = UserHandle.getUserId(uid);
+
final boolean forWrite = (flags & StorageManager.FLAG_FOR_WRITE) != 0;
+ final boolean realState = (flags & StorageManager.FLAG_REAL_STATE) != 0;
+ final boolean includeInvisible = (flags & StorageManager.FLAG_INCLUDE_INVISIBLE) != 0;
- boolean reportUnmounted = false;
- boolean foundPrimary = false;
-
- final long identity = Binder.clearCallingIdentity();
+ final boolean userKeyUnlocked;
+ final boolean storagePermission;
+ final long token = Binder.clearCallingIdentity();
try {
- if (!mMountServiceInternal.hasExternalStorage(uid, packageName)) {
- reportUnmounted = true;
- }
- if (!isUserKeyUnlocked(userId)) {
- reportUnmounted = true;
- }
+ userKeyUnlocked = isUserKeyUnlocked(userId);
+ storagePermission = mMountServiceInternal.hasExternalStorage(uid, packageName);
} finally {
- Binder.restoreCallingIdentity(identity);
+ Binder.restoreCallingIdentity(token);
}
+ boolean foundPrimary = false;
+
final ArrayList<StorageVolume> res = new ArrayList<>();
synchronized (mLock) {
for (int i = 0; i < mVolumes.size(); i++) {
final VolumeInfo vol = mVolumes.valueAt(i);
- if (forWrite ? vol.isVisibleForWrite(userId) : vol.isVisibleForRead(userId)) {
- final StorageVolume userVol = vol.buildStorageVolume(mContext, userId,
- reportUnmounted);
- if (vol.isPrimary()) {
- res.add(0, userVol);
- foundPrimary = true;
- } else {
- res.add(userVol);
- }
+ switch (vol.getType()) {
+ case VolumeInfo.TYPE_PUBLIC:
+ case VolumeInfo.TYPE_EMULATED:
+ break;
+ default:
+ continue;
+ }
+
+ boolean match = false;
+ if (forWrite) {
+ match = vol.isVisibleForWrite(userId);
+ } else {
+ match = vol.isVisibleForRead(userId) || includeInvisible;
+ }
+ if (!match) continue;
+
+ boolean reportUnmounted = false;
+ if ((vol.getType() == VolumeInfo.TYPE_EMULATED) && !userKeyUnlocked) {
+ reportUnmounted = true;
+ } else if (!storagePermission && !realState) {
+ reportUnmounted = true;
+ }
+
+ final StorageVolume userVol = vol.buildStorageVolume(mContext, userId,
+ reportUnmounted);
+ if (vol.isPrimary()) {
+ res.add(0, userVol);
+ foundPrimary = true;
+ } else {
+ res.add(userVol);
}
}
}
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 548b662..bf4df94 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -209,9 +209,12 @@
/** Set of interfaces with active alerts. */
@GuardedBy("mQuotaLock")
private HashMap<String, Long> mActiveAlerts = Maps.newHashMap();
- /** Set of UIDs with active reject rules. */
+ /** Set of UIDs blacklisted on metered networks. */
@GuardedBy("mQuotaLock")
- private SparseBooleanArray mUidRejectOnQuota = new SparseBooleanArray();
+ private SparseBooleanArray mUidRejectOnMetered = new SparseBooleanArray();
+ /** Set of UIDs whitelisted on metered networks. */
+ @GuardedBy("mQuotaLock")
+ private SparseBooleanArray mUidAllowOnMetered = new SparseBooleanArray();
/** Set of UIDs with cleartext penalties. */
@GuardedBy("mQuotaLock")
private SparseIntArray mUidCleartextPolicy = new SparseIntArray();
@@ -240,6 +243,9 @@
@GuardedBy("mQuotaLock")
final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray();
+ @GuardedBy("mQuotaLock")
+ private boolean mDataSaverMode;
+
private Object mIdleTimerLock = new Object();
/** Set of interfaces with active idle timers. */
private static class IdleTimerParams {
@@ -513,6 +519,28 @@
}
}
+ // Sync the state of the given chain with the native daemon.
+ private void syncFirewallChainLocked(int chain, SparseIntArray uidFirewallRules, String name) {
+ int size = uidFirewallRules.size();
+ if (size > 0) {
+ // Make a copy of the current rules, and then clear them. This is because
+ // setFirewallUidRuleInternal only pushes down rules to the native daemon if they are
+ // different from the current rules stored in the mUidFirewall*Rules array for the
+ // specified chain. If we don't clear the rules, setFirewallUidRuleInternal will do
+ // nothing.
+ final SparseIntArray rules = uidFirewallRules.clone();
+ uidFirewallRules.clear();
+
+ // Now push the rules. setFirewallUidRuleInternal will push each of these down to the
+ // native daemon, and also add them to the mUidFirewall*Rules array for the specified
+ // chain.
+ if (DBG) Slog.d(TAG, "Pushing " + size + " active firewall " + name + "UID rules");
+ for (int i = 0; i < rules.size(); i++) {
+ setFirewallUidRuleInternal(chain, rules.keyAt(i), rules.valueAt(i));
+ }
+ }
+ }
+
/**
* Prepare native daemon once connected, enabling modules and pushing any
* existing in-memory rules.
@@ -561,6 +589,9 @@
// push any existing quota or UID rules
synchronized (mQuotaLock) {
+
+ setDataSaverModeEnabled(mDataSaverMode);
+
int size = mActiveQuotas.size();
if (size > 0) {
if (DBG) Slog.d(TAG, "Pushing " + size + " active quota rules");
@@ -581,13 +612,25 @@
}
}
- size = mUidRejectOnQuota.size();
+ size = mUidRejectOnMetered.size();
if (size > 0) {
- if (DBG) Slog.d(TAG, "Pushing " + size + " active UID rules");
- final SparseBooleanArray uidRejectOnQuota = mUidRejectOnQuota;
- mUidRejectOnQuota = new SparseBooleanArray();
+ if (DBG) Slog.d(TAG, "Pushing " + size + " UIDs to metered whitelist rules");
+ final SparseBooleanArray uidRejectOnQuota = mUidRejectOnMetered;
+ mUidRejectOnMetered = new SparseBooleanArray();
for (int i = 0; i < uidRejectOnQuota.size(); i++) {
- setUidNetworkRules(uidRejectOnQuota.keyAt(i), uidRejectOnQuota.valueAt(i));
+ setUidMeteredNetworkBlacklist(uidRejectOnQuota.keyAt(i),
+ uidRejectOnQuota.valueAt(i));
+ }
+ }
+
+ size = mUidAllowOnMetered.size();
+ if (size > 0) {
+ if (DBG) Slog.d(TAG, "Pushing " + size + " UIDs to metered blacklist rules");
+ final SparseBooleanArray uidAcceptOnQuota = mUidAllowOnMetered;
+ mUidAllowOnMetered = new SparseBooleanArray();
+ for (int i = 0; i < uidAcceptOnQuota.size(); i++) {
+ setUidMeteredNetworkWhitelist(uidAcceptOnQuota.keyAt(i),
+ uidAcceptOnQuota.valueAt(i));
}
}
@@ -603,55 +646,18 @@
setFirewallEnabled(mFirewallEnabled || LockdownVpnTracker.isEnabled());
- size = mUidFirewallRules.size();
- if (size > 0) {
- if (DBG) Slog.d(TAG, "Pushing " + size + " active firewall UID rules");
- final SparseIntArray uidFirewallRules = mUidFirewallRules;
- mUidFirewallRules = new SparseIntArray();
- for (int i = 0; i < uidFirewallRules.size(); i++) {
- setFirewallUidRuleInternal(FIREWALL_CHAIN_NONE, uidFirewallRules.keyAt(i),
- uidFirewallRules.valueAt(i));
- }
- }
+ syncFirewallChainLocked(FIREWALL_CHAIN_NONE, mUidFirewallRules, "");
+ syncFirewallChainLocked(FIREWALL_CHAIN_STANDBY, mUidFirewallStandbyRules, "standby ");
+ syncFirewallChainLocked(FIREWALL_CHAIN_DOZABLE, mUidFirewallDozableRules, "dozable ");
+ syncFirewallChainLocked(FIREWALL_CHAIN_POWERSAVE, mUidFirewallPowerSaveRules,
+ "powersave ");
- size = mUidFirewallStandbyRules.size();
- if (size > 0) {
- if (DBG) Slog.d(TAG, "Pushing " + size + " active firewall standby UID rules");
- final SparseIntArray uidFirewallRules = mUidFirewallStandbyRules;
- mUidFirewallStandbyRules = new SparseIntArray();
- for (int i = 0; i < uidFirewallRules.size(); i++) {
- setFirewallUidRuleInternal(FIREWALL_CHAIN_STANDBY, uidFirewallRules.keyAt(i),
- uidFirewallRules.valueAt(i));
- }
- }
if (mFirewallChainStates.get(FIREWALL_CHAIN_STANDBY)) {
setFirewallChainEnabled(FIREWALL_CHAIN_STANDBY, true);
}
-
- size = mUidFirewallDozableRules.size();
- if (size > 0) {
- if (DBG) Slog.d(TAG, "Pushing " + size + " active firewall dozable UID rules");
- final SparseIntArray uidFirewallRules = mUidFirewallDozableRules;
- mUidFirewallDozableRules = new SparseIntArray();
- for (int i = 0; i < uidFirewallRules.size(); i++) {
- setFirewallUidRuleInternal(FIREWALL_CHAIN_DOZABLE, uidFirewallRules.keyAt(i),
- uidFirewallRules.valueAt(i));
- }
- }
if (mFirewallChainStates.get(FIREWALL_CHAIN_DOZABLE)) {
setFirewallChainEnabled(FIREWALL_CHAIN_DOZABLE, true);
}
-
- size = mUidFirewallPowerSaveRules.size();
- if (size > 0) {
- Slog.d(TAG, "Pushing " + size + " active firewall powersave UID rules");
- final SparseIntArray uidFirewallRules = mUidFirewallPowerSaveRules;
- mUidFirewallPowerSaveRules = new SparseIntArray();
- for (int i = 0; i < uidFirewallRules.size(); i++) {
- setFirewallUidRuleInternal(FIREWALL_CHAIN_POWERSAVE, uidFirewallRules.keyAt(i),
- uidFirewallRules.valueAt(i));
- }
- }
if (mFirewallChainStates.get(FIREWALL_CHAIN_POWERSAVE)) {
setFirewallChainEnabled(FIREWALL_CHAIN_POWERSAVE, true);
}
@@ -738,6 +744,7 @@
private class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks {
@Override
public void onDaemonConnected() {
+ Slog.i(TAG, "onDaemonConnected()");
// event is dispatched from internal NDC thread, so we prepare the
// daemon back on main thread.
if (mConnectedSignal != null) {
@@ -1698,28 +1705,30 @@
}
}
- @Override
- public void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) {
+ private void setUidOnMeteredNetworkList(SparseBooleanArray quotaList, int uid,
+ boolean blacklist, boolean enable) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
// silently discard when control disabled
// TODO: eventually migrate to be always enabled
if (!mBandwidthControlEnabled) return;
+ final String chain = blacklist ? "naughtyapps" : "niceapps";
+ final String suffix = enable ? "add" : "remove";
+
synchronized (mQuotaLock) {
- final boolean oldRejectOnQuota = mUidRejectOnQuota.get(uid, false);
- if (oldRejectOnQuota == rejectOnQuotaInterfaces) {
+ final boolean oldEnable = quotaList.get(uid, false);
+ if (oldEnable == enable) {
// TODO: eventually consider throwing
return;
}
try {
- mConnector.execute("bandwidth",
- rejectOnQuotaInterfaces ? "addnaughtyapps" : "removenaughtyapps", uid);
- if (rejectOnQuotaInterfaces) {
- mUidRejectOnQuota.put(uid, true);
+ mConnector.execute("bandwidth", suffix + chain, uid);
+ if (enable) {
+ quotaList.put(uid, true);
} else {
- mUidRejectOnQuota.delete(uid);
+ quotaList.delete(uid);
}
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
@@ -1728,6 +1737,39 @@
}
@Override
+ public void setUidMeteredNetworkBlacklist(int uid, boolean enable) {
+ setUidOnMeteredNetworkList(mUidRejectOnMetered, uid, true, enable);
+ }
+
+ @Override
+ public void setUidMeteredNetworkWhitelist(int uid, boolean enable) {
+ setUidOnMeteredNetworkList(mUidAllowOnMetered, uid, false, enable);
+ }
+
+ @Override
+ public boolean setDataSaverModeEnabled(boolean enable) {
+ if (DBG) Log.d(TAG, "setDataSaverMode: " + enable);
+ synchronized (mQuotaLock) {
+ if (mDataSaverMode == enable) {
+ Log.w(TAG, "setDataSaverMode(): already " + mDataSaverMode);
+ return true;
+ }
+ try {
+ final boolean changed = mNetdService.bandwidthEnableDataSaver(enable);
+ if (changed) {
+ mDataSaverMode = enable;
+ } else {
+ Log.w(TAG, "setDataSaverMode(" + enable + "): netd command silently failed");
+ }
+ return changed;
+ } catch (RemoteException e) {
+ Log.w(TAG, "setDataSaverMode(" + enable + "): netd command failed", e);
+ return false;
+ }
+ }
+ }
+
+ @Override
public void setUidCleartextNetworkPolicy(int uid, int policy) {
if (Binder.getCallingUid() != uid) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
@@ -1895,16 +1937,6 @@
}
@Override
- public void flushNetworkDnsCache(int netId) {
- mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- try {
- mConnector.execute("resolver", "flushnet", netId);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
- }
- }
-
- @Override
public void setFirewallEnabled(boolean enabled) {
enforceSystemUid();
try {
@@ -2221,29 +2253,22 @@
synchronized (mQuotaLock) {
pw.print("Active quota ifaces: "); pw.println(mActiveQuotas.toString());
pw.print("Active alert ifaces: "); pw.println(mActiveAlerts.toString());
- }
-
- synchronized (mUidRejectOnQuota) {
- pw.print("UID reject on quota ifaces: [");
- final int size = mUidRejectOnQuota.size();
- for (int i = 0; i < size; i++) {
- pw.print(mUidRejectOnQuota.keyAt(i));
- if (i < size - 1) pw.print(",");
- }
- pw.println("]");
+ pw.print("Data saver mode: "); pw.println(mDataSaverMode);
+ dumpUidRuleOnQuotaLocked(pw, "blacklist", mUidRejectOnMetered);
+ dumpUidRuleOnQuotaLocked(pw, "whitelist", mUidAllowOnMetered);
}
synchronized (mUidFirewallRules) {
dumpUidFirewallRule(pw, "", mUidFirewallRules);
}
- pw.println("UID firewall standby chain enabled: " +
+ pw.print("UID firewall standby chain enabled: "); pw.println(
mFirewallChainStates.get(FIREWALL_CHAIN_STANDBY));
synchronized (mUidFirewallStandbyRules) {
dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_STANDBY, mUidFirewallStandbyRules);
}
- pw.println("UID firewall dozable chain enabled: " +
+ pw.print("UID firewall dozable chain enabled: "); pw.println(
mFirewallChainStates.get(FIREWALL_CHAIN_DOZABLE));
synchronized (mUidFirewallDozableRules) {
dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_DOZABLE, mUidFirewallDozableRules);
@@ -2267,6 +2292,29 @@
}
pw.print("Firewall enabled: "); pw.println(mFirewallEnabled);
+ pw.print("Netd service status: " );
+ if (mNetdService == null) {
+ pw.println("disconnected");
+ } else {
+ try {
+ final boolean alive = mNetdService.isAlive();
+ pw.println(alive ? "alive": "dead");
+ } catch (RemoteException e) {
+ pw.println("unreachable");
+ }
+ }
+ }
+
+ private void dumpUidRuleOnQuotaLocked(PrintWriter pw, String name, SparseBooleanArray list) {
+ pw.print("UID bandwith control ");
+ pw.print(name);
+ pw.print(" rule: [");
+ final int size = list.size();
+ for (int i = 0; i < size; i++) {
+ pw.print(list.keyAt(i));
+ if (i < size - 1) pw.print(",");
+ }
+ pw.println("]");
}
private void dumpUidFirewallRule(PrintWriter pw, String name, SparseIntArray rules) {
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index 879bb6f..2a78f90 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -18,10 +18,12 @@
import android.Manifest.permission;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.net.INetworkScoreCache;
import android.net.INetworkScoreService;
@@ -30,6 +32,7 @@
import android.net.NetworkScorerAppManager.NetworkScorerAppData;
import android.net.ScoredNetwork;
import android.os.Binder;
+import android.os.IBinder;
import android.os.PatternMatcher;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -55,17 +58,17 @@
*/
public class NetworkScoreService extends INetworkScoreService.Stub {
private static final String TAG = "NetworkScoreService";
+ private static final boolean DBG = false;
private final Context mContext;
-
private final Map<Integer, INetworkScoreCache> mScoreCaches;
-
/** Lock used to update mReceiver when scorer package changes occur. */
- private Object mReceiverLock = new Object[0];
+ private final Object mReceiverLock = new Object[0];
/** Clears scores when the active scorer package is no longer valid. */
@GuardedBy("mReceiverLock")
private ScorerChangedReceiver mReceiver;
+ private ScoringServiceConnection mServiceConnection;
private class ScorerChangedReceiver extends BroadcastReceiver {
final String mRegisteredPackage;
@@ -77,14 +80,23 @@
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
- if ((Intent.ACTION_PACKAGE_CHANGED.equals(action) ||
- Intent.ACTION_PACKAGE_REPLACED.equals(action) ||
- Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) &&
- NetworkScorerAppManager.getActiveScorer(mContext) == null) {
- // Package change has invalidated a scorer.
- Log.i(TAG, "Package " + mRegisteredPackage +
- " is no longer valid, disabling scoring");
- setScorerInternal(null);
+ if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
+ || Intent.ACTION_PACKAGE_REPLACED.equals(action)
+ || Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) {
+ NetworkScorerAppData activeScorer =
+ NetworkScorerAppManager.getActiveScorer(mContext);
+ if (activeScorer == null) {
+ // Package change has invalidated a scorer.
+ Log.i(TAG, "Package " + mRegisteredPackage +
+ " is no longer valid, disabling scoring.");
+ setScorerInternal(null);
+ } else if (activeScorer.mScoringServiceClassName == null) {
+ // The scoring service is not available, make sure it's unbound.
+ unbindFromScoringServiceIfNeeded();
+ } else {
+ // The scoring service may have changed or been added.
+ bindToScoringServiceIfNeeded(activeScorer);
+ }
}
}
}
@@ -96,6 +108,7 @@
/** Called when the system is ready to run third-party code but before it actually does so. */
void systemReady() {
+ if (DBG) Log.d(TAG, "systemReady");
ContentResolver cr = mContext.getContentResolver();
if (Settings.Global.getInt(cr, Settings.Global.NETWORK_SCORING_PROVISIONED, 0) == 0) {
// On first run, we try to initialize the scorer to the one configured at build time.
@@ -111,7 +124,14 @@
registerPackageReceiverIfNeeded();
}
+ /** Called when the system is ready for us to start third-party code. */
+ void systemRunning() {
+ if (DBG) Log.d(TAG, "systemRunning");
+ bindToScoringServiceIfNeeded();
+ }
+
private void registerPackageReceiverIfNeeded() {
+ if (DBG) Log.d(TAG, "registerPackageReceiverIfNeeded");
NetworkScorerAppData scorer = NetworkScorerAppManager.getActiveScorer(mContext);
synchronized (mReceiverLock) {
// Unregister the receiver if the current scorer has changed since last registration.
@@ -142,6 +162,41 @@
}
}
+ private void bindToScoringServiceIfNeeded() {
+ if (DBG) Log.d(TAG, "bindToScoringServiceIfNeeded");
+ NetworkScorerAppData scorerData = NetworkScorerAppManager.getActiveScorer(mContext);
+ bindToScoringServiceIfNeeded(scorerData);
+ }
+
+ private void bindToScoringServiceIfNeeded(NetworkScorerAppData scorerData) {
+ if (DBG) Log.d(TAG, "bindToScoringServiceIfNeeded(" + scorerData + ")");
+ if (scorerData != null && scorerData.mScoringServiceClassName != null) {
+ ComponentName componentName =
+ new ComponentName(scorerData.mPackageName, scorerData.mScoringServiceClassName);
+ // If we're connected to a different component then drop it.
+ if (mServiceConnection != null
+ && !mServiceConnection.mComponentName.equals(componentName)) {
+ unbindFromScoringServiceIfNeeded();
+ }
+
+ // If we're not connected at all then create a new connection.
+ if (mServiceConnection == null) {
+ mServiceConnection = new ScoringServiceConnection(componentName);
+ }
+
+ // Make sure the connection is connected (idempotent)
+ mServiceConnection.connect(mContext);
+ }
+ }
+
+ private void unbindFromScoringServiceIfNeeded() {
+ if (DBG) Log.d(TAG, "unbindFromScoringServiceIfNeeded");
+ if (mServiceConnection != null) {
+ mServiceConnection.disconnect(mContext);
+ }
+ mServiceConnection = null;
+ }
+
@Override
public boolean updateScores(ScoredNetwork[] networks) {
if (!NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid())) {
@@ -228,8 +283,10 @@
/** Set the active scorer. Callers are responsible for checking permissions as appropriate. */
private boolean setScorerInternal(String packageName) {
+ if (DBG) Log.d(TAG, "setScorerInternal(" + packageName + ")");
long token = Binder.clearCallingIdentity();
try {
+ unbindFromScoringServiceIfNeeded();
// Preemptively clear scores even though the set operation could fail. We do this for
// safety as scores should never be compared across apps; in practice, Settings should
// only be allowing valid apps to be set as scorers, so failure here should be rare.
@@ -237,8 +294,13 @@
// Get the scorer that is about to be replaced, if any, so we can notify it directly.
NetworkScorerAppData prevScorer = NetworkScorerAppManager.getActiveScorer(mContext);
boolean result = NetworkScorerAppManager.setActiveScorer(mContext, packageName);
+ // Unconditionally attempt to bind to the current scorer. If setActiveScorer() failed
+ // then we'll attempt to restore the previous binding (if any), otherwise an attempt
+ // will be made to bind to the new scorer.
+ bindToScoringServiceIfNeeded();
if (result) { // new scorer successfully set
registerPackageReceiverIfNeeded();
+
Intent intent = new Intent(NetworkScoreManager.ACTION_SCORER_CHANGED);
if (prevScorer != null) { // Directly notify the old scorer.
intent.setPackage(prevScorer.mPackageName);
@@ -295,7 +357,6 @@
return;
}
writer.println("Current scorer: " + currentScorer.mPackageName);
- writer.flush();
for (INetworkScoreCache scoreCache : getScoreCaches()) {
try {
@@ -307,6 +368,12 @@
}
}
}
+ if (mServiceConnection != null) {
+ mServiceConnection.dump(fd, writer, args);
+ } else {
+ writer.println("ScoringServiceConnection: null");
+ }
+ writer.flush();
}
/**
@@ -320,4 +387,50 @@
return new HashSet<>(mScoreCaches.values());
}
}
+
+ private static class ScoringServiceConnection implements ServiceConnection {
+ private final ComponentName mComponentName;
+ private boolean mBound = false;
+
+ ScoringServiceConnection(ComponentName componentName) {
+ mComponentName = componentName;
+ }
+
+ void connect(Context context) {
+ disconnect(context);
+ Intent service = new Intent();
+ service.setComponent(mComponentName);
+ mBound = context.bindServiceAsUser(service, this,
+ Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
+ UserHandle.SYSTEM);
+ if (!mBound) {
+ Log.w(TAG, "Bind call failed for " + service);
+ }
+ }
+
+ void disconnect(Context context) {
+ try {
+ if (mBound) {
+ mBound = false;
+ context.unbindService(this);
+ }
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Unbind failed.", e);
+ }
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ if (DBG) Log.d(TAG, "ScoringServiceConnection: " + name.flattenToString());
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ if (DBG) Log.d(TAG, "ScoringServiceConnection, disconnected: " + name.flattenToString());
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ writer.println("ScoringServiceConnection: " + mComponentName + ", bound: " + mBound);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/NsdService.java b/services/core/java/com/android/server/NsdService.java
index 11aef17..a44b065 100644
--- a/services/core/java/com/android/server/NsdService.java
+++ b/services/core/java/com/android/server/NsdService.java
@@ -30,16 +30,14 @@
import android.os.Messenger;
import android.os.UserHandle;
import android.provider.Settings;
+import android.util.Base64;
import android.util.Slog;
import android.util.SparseArray;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
import java.util.concurrent.CountDownLatch;
import com.android.internal.util.AsyncChannel;
@@ -492,6 +490,7 @@
clientInfo.mResolvedService.setServiceName(name);
clientInfo.mResolvedService.setServiceType(type);
clientInfo.mResolvedService.setPort(Integer.parseInt(cooked[4]));
+ clientInfo.mResolvedService.setTxtRecords(cooked[6]);
stopResolveService(id);
removeRequestMap(clientId, id, clientInfo);
@@ -708,20 +707,9 @@
if (DBG) Slog.d(TAG, "registerService: " + regId + " " + service);
try {
Command cmd = new Command("mdnssd", "register", regId, service.getServiceName(),
- service.getServiceType(), service.getPort());
-
- // Add TXT records as additional arguments.
- Map<String, byte[]> txtRecords = service.getAttributes();
- for (String key : txtRecords.keySet()) {
- try {
- // TODO: Send encoded TXT record as bytes once NDC/netd supports binary data.
- byte[] recordValue = txtRecords.get(key);
- cmd.appendArg(String.format(Locale.US, "%s=%s", key,
- recordValue != null ? new String(recordValue, "UTF_8") : ""));
- } catch (UnsupportedEncodingException e) {
- Slog.e(TAG, "Failed to encode txtRecord " + e);
- }
- }
+ service.getServiceType(), service.getPort(),
+ Base64.encodeToString(service.getTxtRecord(), Base64.DEFAULT)
+ .replace("\n", ""));
mNativeConnector.execute(cmd);
} catch(NativeDaemonConnectorException e) {
diff --git a/services/core/java/com/android/server/RecoverySystemService.java b/services/core/java/com/android/server/RecoverySystemService.java
index d284d07..276687f 100644
--- a/services/core/java/com/android/server/RecoverySystemService.java
+++ b/services/core/java/com/android/server/RecoverySystemService.java
@@ -79,7 +79,7 @@
uncryptFile.write(filename + "\n");
} catch (IOException e) {
Slog.e(TAG, "IOException when writing \"" + RecoverySystem.UNCRYPT_PACKAGE_FILE +
- "\": " + e.getMessage());
+ "\": ", e);
return false;
}
@@ -94,8 +94,11 @@
}
// Read the status from the socket.
- try (DataInputStream dis = new DataInputStream(socket.getInputStream());
- DataOutputStream dos = new DataOutputStream(socket.getOutputStream())) {
+ DataInputStream dis = null;
+ DataOutputStream dos = null;
+ try {
+ dis = new DataInputStream(socket.getInputStream());
+ dos = new DataOutputStream(socket.getOutputStream());
int lastStatus = Integer.MIN_VALUE;
while (true) {
int status = dis.readInt();
@@ -111,7 +114,7 @@
if (listener != null) {
try {
listener.onProgress(status);
- } catch (RemoteException unused) {
+ } catch (RemoteException ignored) {
Slog.w(TAG, "RemoteException when posting progress");
}
}
@@ -121,7 +124,6 @@
// waits for the ack so the socket won't be
// destroyed before we receive the code.
dos.writeInt(0);
- dos.flush();
break;
}
} else {
@@ -131,14 +133,15 @@
// for the ack so the socket won't be destroyed before
// we receive the code.
dos.writeInt(0);
- dos.flush();
return false;
}
}
} catch (IOException e) {
- Slog.e(TAG, "IOException when reading status: " + e);
+ Slog.e(TAG, "IOException when reading status: ", e);
return false;
} finally {
+ IoUtils.closeQuietly(dis);
+ IoUtils.closeQuietly(dos);
IoUtils.closeQuietly(socket);
}
@@ -169,11 +172,11 @@
LocalSocketAddress.Namespace.RESERVED));
done = true;
break;
- } catch (IOException unused) {
+ } catch (IOException ignored) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
- Slog.w(TAG, "Interrupted: " + e);
+ Slog.w(TAG, "Interrupted: ", e);
}
}
}
@@ -200,8 +203,12 @@
return false;
}
- try (DataInputStream dis = new DataInputStream(socket.getInputStream());
- DataOutputStream dos = new DataOutputStream(socket.getOutputStream())) {
+ DataInputStream dis = null;
+ DataOutputStream dos = null;
+ try {
+ dis = new DataInputStream(socket.getInputStream());
+ dos = new DataOutputStream(socket.getOutputStream());
+
// Send the BCB commands if it's to setup BCB.
if (isSetup) {
dos.writeInt(command.length());
@@ -215,7 +222,6 @@
// Ack receipt of the status code. uncrypt waits for the ack so
// the socket won't be destroyed before we receive the code.
dos.writeInt(0);
- dos.flush();
if (status == 100) {
Slog.i(TAG, "uncrypt " + (isSetup ? "setup" : "clear") +
@@ -226,9 +232,11 @@
return false;
}
} catch (IOException e) {
- Slog.e(TAG, "IOException when getting output stream: " + e);
+ Slog.e(TAG, "IOException when communicating with uncrypt: ", e);
return false;
} finally {
+ IoUtils.closeQuietly(dis);
+ IoUtils.closeQuietly(dos);
IoUtils.closeQuietly(socket);
}
diff --git a/services/core/java/com/android/server/ThermalObserver.java b/services/core/java/com/android/server/ThermalObserver.java
deleted file mode 100644
index aee28fb..0000000
--- a/services/core/java/com/android/server/ThermalObserver.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.UEventObserver;
-import android.os.UserHandle;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * ThermalObserver for monitoring temperature changes.
- */
-public class ThermalObserver extends SystemService {
- private static final String TAG = "ThermalObserver";
-
- private static final String CALLSTATE_UEVENT_MATCH =
- "DEVPATH=/devices/virtual/switch/thermalstate";
-
- private static final int MSG_THERMAL_STATE_CHANGED = 0;
-
- private static final int SWITCH_STATE_NORMAL = 0;
- private static final int SWITCH_STATE_WARNING = 1;
- private static final int SWITCH_STATE_EXCEEDED = 2;
-
- private final PowerManager mPowerManager;
- private final PowerManager.WakeLock mWakeLock;
-
- private final Object mLock = new Object();
- private Integer mLastState;
-
- private final UEventObserver mThermalWarningObserver = new UEventObserver() {
- @Override
- public void onUEvent(UEventObserver.UEvent event) {
- updateLocked(Integer.parseInt(event.get("SWITCH_STATE")));
- }
- };
-
- private final Handler mHandler = new Handler(true /*async*/) {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_THERMAL_STATE_CHANGED:
- handleThermalStateChange(msg.arg1);
- mWakeLock.release();
- break;
- }
- }
- };
-
- public ThermalObserver(Context context) {
- super(context);
- mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
- mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
-
- mThermalWarningObserver.startObserving(CALLSTATE_UEVENT_MATCH);
- }
-
- private void updateLocked(int state) {
- Message message = new Message();
- message.what = MSG_THERMAL_STATE_CHANGED;
- message.arg1 = state;
-
- mWakeLock.acquire();
- mHandler.sendMessage(message);
- }
-
- private void handleThermalStateChange(int state) {
- synchronized (mLock) {
- mLastState = state;
- Intent intent = new Intent(Intent.ACTION_THERMAL_EVENT);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
-
- final int thermalState;
-
- switch (state) {
- case SWITCH_STATE_WARNING:
- thermalState = Intent.EXTRA_THERMAL_STATE_WARNING;
- break;
- case SWITCH_STATE_EXCEEDED:
- thermalState = Intent.EXTRA_THERMAL_STATE_EXCEEDED;
- break;
- case SWITCH_STATE_NORMAL:
- default:
- thermalState = Intent.EXTRA_THERMAL_STATE_NORMAL;
- break;
- }
-
- intent.putExtra(Intent.EXTRA_THERMAL_STATE, thermalState);
-
- getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
- }
- }
-
- @Override
- public void onStart() {
- publishBinderService(TAG, new BinderService());
- }
-
- private final class BinderService extends Binder {
- @Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
- != PackageManager.PERMISSION_GRANTED) {
- pw.println("Permission Denial: can't dump thermal observer service from from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid());
- return;
- }
-
- final long ident = Binder.clearCallingIdentity();
- try {
- synchronized (mLock) {
- if (args == null || args.length == 0 || "-a".equals(args[0])) {
- pw.println("Current Thermal Observer Service state:");
- pw.println(" last state change: "
- + (mLastState != null ? mLastState : "none"));
- }
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- }
-}
diff --git a/services/core/java/com/android/server/UiThread.java b/services/core/java/com/android/server/UiThread.java
index 0beb77f..c06afc2 100644
--- a/services/core/java/com/android/server/UiThread.java
+++ b/services/core/java/com/android/server/UiThread.java
@@ -17,6 +17,7 @@
package com.android.server;
import android.os.Handler;
+import android.os.Trace;
/**
* Shared singleton thread for showing UI. This is a foreground thread, and in
@@ -35,6 +36,7 @@
if (sInstance == null) {
sInstance = new UiThread();
sInstance.start();
+ sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_ACTIVITY_MANAGER);
sHandler = new Handler(sInstance.getLooper());
}
}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 1632f92..8a0a62a 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -65,6 +65,7 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.Environment;
+import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -75,11 +76,13 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.storage.StorageManager;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import com.android.internal.R;
import com.android.internal.util.ArrayUtils;
@@ -91,6 +94,7 @@
import java.io.File;
import java.io.FileDescriptor;
+import java.io.IOException;
import java.io.PrintWriter;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
@@ -107,7 +111,6 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
-import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
@@ -126,7 +129,9 @@
private static final String TAG = "AccountManagerService";
private static final String DATABASE_NAME = "accounts.db";
- private static final int DATABASE_VERSION = 9;
+ private static final int PRE_N_DATABASE_VERSION = 9;
+ private static final int CE_DATABASE_VERSION = 10;
+ private static final int DE_DATABASE_VERSION = 1;
private static final int MAX_DEBUG_DB_SIZE = 64;
@@ -176,6 +181,15 @@
private static final String META_VALUE = "value";
private static final String TABLE_SHARED_ACCOUNTS = "shared_accounts";
+ private static final String SHARED_ACCOUNTS_ID = "_id";
+
+ private static final String PRE_N_DATABASE_NAME = "accounts.db";
+ private static final String CE_DATABASE_NAME = "accounts_ce.db";
+ private static final String DE_DATABASE_NAME = "accounts_de.db";
+ private static final String CE_DB_PREFIX = "ceDb.";
+ private static final String CE_TABLE_ACCOUNTS = CE_DB_PREFIX + TABLE_ACCOUNTS;
+ private static final String CE_TABLE_AUTHTOKENS = CE_DB_PREFIX + TABLE_AUTHTOKENS;
+ private static final String CE_TABLE_EXTRAS = CE_DB_PREFIX + TABLE_EXTRAS;
private static final String[] ACCOUNT_TYPE_COUNT_PROJECTION =
new String[] { ACCOUNTS_TYPE, ACCOUNTS_TYPE_COUNT};
@@ -214,7 +228,7 @@
static class UserAccounts {
private final int userId;
- private final DatabaseHelper openHelper;
+ private final DeDatabaseHelper openHelper;
private final HashMap<Pair<Pair<Account, String>, Integer>, Integer>
credentialsPermissionNotificationIds =
new HashMap<Pair<Pair<Account, String>, Integer>, Integer>();
@@ -255,15 +269,15 @@
UserAccounts(Context context, int userId) {
this.userId = userId;
synchronized (cacheLock) {
- openHelper = new DatabaseHelper(context, userId);
+ openHelper = DeDatabaseHelper.create(context, userId);
}
}
}
- private final SparseArray<UserAccounts> mUsers = new SparseArray<UserAccounts>();
+ private final SparseArray<UserAccounts> mUsers = new SparseArray<>();
+ private final SparseBooleanArray mUnlockedUsers = new SparseBooleanArray();
- private static AtomicReference<AccountManagerService> sThis =
- new AtomicReference<AccountManagerService>();
+ private static AtomicReference<AccountManagerService> sThis = new AtomicReference<>();
private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{};
/**
@@ -381,6 +395,11 @@
*/
private void validateAccountsInternal(
UserAccounts accounts, boolean invalidateAuthenticatorCache) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "validateAccountsInternal " + accounts.userId
+ + " isCeDatabaseAttached=" + accounts.openHelper.isCeDatabaseAttached()
+ + " userLocked=" + mUnlockedUsers.get(accounts.userId));
+ }
if (invalidateAuthenticatorCache) {
mAuthenticatorCache.invalidateCache(accounts.userId);
}
@@ -453,8 +472,7 @@
null, null, null, null, ACCOUNTS_ID);
try {
accounts.accountCache.clear();
- final HashMap<String, ArrayList<String>> accountNamesByType =
- new LinkedHashMap<String, ArrayList<String>>();
+ final HashMap<String, ArrayList<String>> accountNamesByType = new LinkedHashMap<>();
while (cursor.moveToNext()) {
final long accountId = cursor.getLong(0);
final String accountType = cursor.getString(1);
@@ -482,15 +500,12 @@
accountNames.add(accountName);
}
}
- for (Map.Entry<String, ArrayList<String>> cur
- : accountNamesByType.entrySet()) {
+ for (Map.Entry<String, ArrayList<String>> cur : accountNamesByType.entrySet()) {
final String accountType = cur.getKey();
final ArrayList<String> accountNames = cur.getValue();
final Account[] accountsForType = new Account[accountNames.size()];
- int i = 0;
- for (String accountName : accountNames) {
- accountsForType[i] = new Account(accountName, accountType);
- ++i;
+ for (int i = 0; i < accountsForType.length; i++) {
+ accountsForType[i] = new Account(accountNames.get(i), accountType);
}
accounts.accountCache.put(accountType, accountsForType);
}
@@ -522,12 +537,25 @@
protected UserAccounts getUserAccounts(int userId) {
synchronized (mUsers) {
UserAccounts accounts = mUsers.get(userId);
+ boolean validateAccounts = false;
if (accounts == null) {
accounts = new UserAccounts(mContext, userId);
initializeDebugDbSizeAndCompileSqlStatementForLogging(
accounts.openHelper.getWritableDatabase(), accounts);
mUsers.append(userId, accounts);
purgeOldGrants(accounts);
+ validateAccounts = true;
+ }
+ // open CE database if necessary
+ if (!accounts.openHelper.isCeDatabaseAttached() && mUnlockedUsers.get(userId)) {
+ Log.i(TAG, "User " + userId + " is unlocked - opening CE database");
+ synchronized (accounts.cacheLock) {
+ CeDatabaseHelper.create(mContext, userId);
+ accounts.openHelper.attachCeDatabase();
+ }
+ // TODO Synchronize accounts by removing CE account not available in DE
+ }
+ if (validateAccounts) {
validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
}
return accounts;
@@ -571,27 +599,51 @@
if (userId < 1) return;
UserAccounts accounts;
+ boolean userUnlocked;
synchronized (mUsers) {
accounts = mUsers.get(userId);
mUsers.remove(userId);
+ userUnlocked = mUnlockedUsers.get(userId);
+ mUnlockedUsers.delete(userId);
}
- if (accounts == null) {
- File dbFile = new File(getDatabaseName(userId));
- dbFile.delete();
- return;
+ if (accounts != null) {
+ synchronized (accounts.cacheLock) {
+ accounts.openHelper.close();
+ }
}
+ Log.i(TAG, "Removing database files for user " + userId);
+ File dbFile = new File(getDeDatabaseName(userId));
- synchronized (accounts.cacheLock) {
- accounts.openHelper.close();
- File dbFile = new File(getDatabaseName(userId));
- dbFile.delete();
+ deleteDbFileWarnIfFailed(dbFile);
+ // Remove CE file if user is unlocked, or FBE is not enabled
+ boolean fbeEnabled = StorageManager.isFileEncryptedNativeOrEmulated();
+ if (!fbeEnabled || userUnlocked) {
+ File ceDb = new File(getCeDatabaseName(userId));
+ if (ceDb.exists()) {
+ deleteDbFileWarnIfFailed(ceDb);
+ }
+ }
+ }
+
+ private static void deleteDbFileWarnIfFailed(File dbFile) {
+ if (!SQLiteDatabase.deleteDatabase(dbFile)) {
+ Log.w(TAG, "Database at " + dbFile + " was not deleted successfully");
}
}
private void onUserUnlocked(Intent intent) {
int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "onUserUnlocked " + userId);
+ }
+ synchronized (mUsers) {
+ mUnlockedUsers.put(userId, true);
+ }
if (userId < 1) return;
+ syncSharedAccounts(userId);
+ }
+ private void syncSharedAccounts(int userId) {
// Check if there's a shared account that needs to be created as an account
Account[] sharedAccounts = getSharedAccountsAsUser(userId);
if (sharedAccounts == null || sharedAccounts.length == 0) return;
@@ -645,20 +697,15 @@
if (account == null) {
return null;
}
+ if (!isUserUnlocked(accounts.userId)) {
+ Log.w(TAG, "Password is not available - user " + accounts.userId + " data is locked");
+ return null;
+ }
synchronized (accounts.cacheLock) {
- final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
- Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD},
- ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
- new String[]{account.name, account.type}, null, null, null);
- try {
- if (cursor.moveToNext()) {
- return cursor.getString(0);
- }
- return null;
- } finally {
- cursor.close();
- }
+ final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
+ return CeDatabaseHelper.findAccountPasswordByNameAndType(db, account.name,
+ account.type);
}
}
@@ -699,7 +746,7 @@
try {
if (cursor.moveToNext()) {
String previousName = cursor.getString(0);
- previousNameRef = new AtomicReference<String>(previousName);
+ previousNameRef = new AtomicReference<>(previousName);
accounts.previousNameCache.put(account, previousNameRef);
return previousName;
} else {
@@ -735,7 +782,12 @@
long identityToken = clearCallingIdentity();
try {
UserAccounts accounts = getUserAccounts(userId);
- return readUserDataInternal(accounts, account, key);
+ synchronized (accounts.cacheLock) {
+ if (!accountExistsCacheLocked(accounts, account)) {
+ return null;
+ }
+ return readUserDataInternalLocked(accounts, account, key);
+ }
} finally {
restoreCallingIdentity(identityToken);
}
@@ -825,7 +877,7 @@
long identityToken = clearCallingIdentity();
try {
UserAccounts accounts = getUserAccounts(userId);
- return addAccountInternal(accounts, account, password, extras, false, callingUid);
+ return addAccountInternal(accounts, account, password, extras, callingUid);
} finally {
restoreCallingIdentity(identityToken);
}
@@ -999,17 +1051,22 @@
}
private boolean addAccountInternal(UserAccounts accounts, Account account, String password,
- Bundle extras, boolean restricted, int callingUid) {
+ Bundle extras, int callingUid) {
Bundle.setDefusable(extras, true);
if (account == null) {
return false;
}
+ if (!isUserUnlocked(accounts.userId)) {
+ Log.w(TAG, "Account " + account + " cannot be added - user " + accounts.userId
+ + " is locked. callingUid=" + callingUid);
+ return false;
+ }
synchronized (accounts.cacheLock) {
- final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
+ final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
db.beginTransaction();
try {
long numMatches = DatabaseUtils.longForQuery(db,
- "select count(*) from " + TABLE_ACCOUNTS
+ "select count(*) from " + CE_TABLE_ACCOUNTS
+ " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
new String[]{account.name, account.type});
if (numMatches > 0) {
@@ -1021,13 +1078,24 @@
values.put(ACCOUNTS_NAME, account.name);
values.put(ACCOUNTS_TYPE, account.type);
values.put(ACCOUNTS_PASSWORD, password);
- values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
- long accountId = db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
+ long accountId = db.insert(CE_TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
if (accountId < 0) {
Log.w(TAG, "insertAccountIntoDatabase: " + account
+ ", skipping the DB insert failed");
return false;
}
+ // Insert into DE table
+ values = new ContentValues();
+ values.put(ACCOUNTS_ID, accountId);
+ values.put(ACCOUNTS_NAME, account.name);
+ values.put(ACCOUNTS_TYPE, account.type);
+ values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS,
+ System.currentTimeMillis());
+ if (db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values) < 0) {
+ Log.w(TAG, "insertAccountIntoDatabase: " + account
+ + ", skipping the DB insert failed");
+ return false;
+ }
if (extras != null) {
for (String key : extras.keySet()) {
final String value = extras.getString(key);
@@ -1055,6 +1123,12 @@
return true;
}
+ private boolean isUserUnlocked(int userId) {
+ synchronized (mUsers) {
+ return mUnlockedUsers.get(userId);
+ }
+ }
+
/**
* Adds the account to all linked restricted users as shared accounts. If the user is currently
* running, then clone the account too.
@@ -1079,7 +1153,7 @@
values.put(EXTRAS_KEY, key);
values.put(EXTRAS_ACCOUNTS_ID, accountId);
values.put(EXTRAS_VALUE, value);
- return db.insert(TABLE_EXTRAS, EXTRAS_KEY, values);
+ return db.insert(CE_TABLE_EXTRAS, EXTRAS_KEY, values);
}
@Override
@@ -1226,17 +1300,19 @@
}
}
synchronized (accounts.cacheLock) {
- final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
+ final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
db.beginTransaction();
boolean isSuccessful = false;
Account renamedAccount = new Account(newName, accountToRename.type);
try {
- final ContentValues values = new ContentValues();
- values.put(ACCOUNTS_NAME, newName);
- values.put(ACCOUNTS_PREVIOUS_NAME, accountToRename.name);
final long accountId = getAccountIdLocked(db, accountToRename);
if (accountId >= 0) {
+ final ContentValues values = new ContentValues();
+ values.put(ACCOUNTS_NAME, newName);
final String[] argsAccountId = { String.valueOf(accountId) };
+ db.update(CE_TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
+ // Update NAME/PREVIOUS_NAME in DE accounts table
+ values.put(ACCOUNTS_PREVIOUS_NAME, accountToRename.name);
db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
db.setTransactionSuccessful();
isSuccessful = true;
@@ -1332,7 +1408,7 @@
* authenticator. This will let users remove accounts (via Settings in the system) but not
* arbitrary applications (like competing authenticators).
*/
- UserHandle user = new UserHandle(userId);
+ UserHandle user = UserHandle.of(userId);
if (!isAccountManagedByCaller(account.type, callingUid, user.getIdentifier())
&& !isSystemUid(callingUid)) {
String msg = String.format(
@@ -1467,13 +1543,17 @@
}
private boolean removeAccountInternal(UserAccounts accounts, Account account, int callingUid) {
+ // For now user is required to be unlocked. TODO: Handle both cases in the future
int deleted;
synchronized (accounts.cacheLock) {
- final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
+ final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
final long accountId = getAccountIdLocked(db, account);
deleted = db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE
+ "=?",
new String[]{account.name, account.type});
+ // Delete from CE table
+ db.delete(CE_TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
+ new String[]{account.name, account.type});
removeAccountFromCacheLocked(accounts, account);
sendAccountsChangedBroadcast(accounts.userId);
@@ -1512,7 +1592,7 @@
try {
UserAccounts accounts = getUserAccounts(userId);
synchronized (accounts.cacheLock) {
- final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
+ final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
db.beginTransaction();
try {
invalidateAuthTokenLocked(accounts, db, accountType, authToken);
@@ -1544,22 +1624,22 @@
return;
}
Cursor cursor = db.rawQuery(
- "SELECT " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID
- + ", " + TABLE_ACCOUNTS + "." + ACCOUNTS_NAME
- + ", " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE
- + " FROM " + TABLE_ACCOUNTS
- + " JOIN " + TABLE_AUTHTOKENS
- + " ON " + TABLE_ACCOUNTS + "." + ACCOUNTS_ID
- + " = " + AUTHTOKENS_ACCOUNTS_ID
- + " WHERE " + AUTHTOKENS_AUTHTOKEN + " = ? AND "
- + TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?",
+ "SELECT " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID
+ + ", " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_NAME
+ + ", " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE
+ + " FROM " + CE_TABLE_ACCOUNTS
+ + " JOIN " + CE_TABLE_AUTHTOKENS
+ + " ON " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_ID
+ + " = " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_ACCOUNTS_ID
+ + " WHERE " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_AUTHTOKEN
+ + " = ? AND " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?",
new String[]{authToken, accountType});
try {
while (cursor.moveToNext()) {
long authTokenId = cursor.getLong(0);
String accountName = cursor.getString(1);
String authTokenType = cursor.getString(2);
- db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ID + "=" + authTokenId, null);
+ db.delete(CE_TABLE_AUTHTOKENS, AUTHTOKENS_ID + "=" + authTokenId, null);
writeAuthTokenIntoCacheLocked(
accounts,
db,
@@ -1585,7 +1665,7 @@
return;
}
cancelNotification(getSigninRequiredNotificationId(accounts, account),
- new UserHandle(accounts.userId));
+ UserHandle.of(accounts.userId));
synchronized (accounts.cacheLock) {
accounts.accountTokenCaches.put(
account, token, tokenType, callerPkg, callerSigDigest, expiryMillis);
@@ -1598,23 +1678,23 @@
return false;
}
cancelNotification(getSigninRequiredNotificationId(accounts, account),
- new UserHandle(accounts.userId));
+ UserHandle.of(accounts.userId));
synchronized (accounts.cacheLock) {
- final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
+ final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
db.beginTransaction();
try {
long accountId = getAccountIdLocked(db, account);
if (accountId < 0) {
return false;
}
- db.delete(TABLE_AUTHTOKENS,
+ db.delete(CE_TABLE_AUTHTOKENS,
AUTHTOKENS_ACCOUNTS_ID + "=" + accountId + " AND " + AUTHTOKENS_TYPE + "=?",
new String[]{type});
ContentValues values = new ContentValues();
values.put(AUTHTOKENS_ACCOUNTS_ID, accountId);
values.put(AUTHTOKENS_TYPE, type);
values.put(AUTHTOKENS_AUTHTOKEN, authToken);
- if (db.insert(TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values) >= 0) {
+ if (db.insert(CE_TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values) >= 0) {
db.setTransactionSuccessful();
writeAuthTokenIntoCacheLocked(accounts, db, account, type, authToken);
return true;
@@ -1714,7 +1794,7 @@
return;
}
synchronized (accounts.cacheLock) {
- final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
+ final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
db.beginTransaction();
try {
final ContentValues values = new ContentValues();
@@ -1722,8 +1802,8 @@
final long accountId = getAccountIdLocked(db, account);
if (accountId >= 0) {
final String[] argsAccountId = {String.valueOf(accountId)};
- db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
- db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId);
+ db.update(CE_TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
+ db.delete(CE_TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId);
accounts.authTokenCache.remove(account);
accounts.accountTokenCaches.remove(account);
db.setTransactionSuccessful();
@@ -1794,44 +1874,57 @@
long identityToken = clearCallingIdentity();
try {
UserAccounts accounts = getUserAccounts(userId);
- setUserdataInternal(accounts, account, key, value);
+ synchronized (accounts.cacheLock) {
+ if (!accountExistsCacheLocked(accounts, account)) {
+ return;
+ }
+ setUserdataInternalLocked(accounts, account, key, value);
+ }
} finally {
restoreCallingIdentity(identityToken);
}
}
- private void setUserdataInternal(UserAccounts accounts, Account account, String key,
+ private boolean accountExistsCacheLocked(UserAccounts accounts, Account account) {
+ if (accounts.accountCache.containsKey(account.type)) {
+ for (Account acc : accounts.accountCache.get(account.type)) {
+ if (acc.name.equals(account.name)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private void setUserdataInternalLocked(UserAccounts accounts, Account account, String key,
String value) {
if (account == null || key == null) {
return;
}
- synchronized (accounts.cacheLock) {
- final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
- db.beginTransaction();
- try {
- long accountId = getAccountIdLocked(db, account);
- if (accountId < 0) {
+ final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
+ db.beginTransaction();
+ try {
+ long accountId = getAccountIdLocked(db, account);
+ if (accountId < 0) {
+ return;
+ }
+ long extrasId = getExtrasIdLocked(db, accountId, key);
+ if (extrasId < 0) {
+ extrasId = insertExtraLocked(db, accountId, key, value);
+ if (extrasId < 0) {
return;
}
- long extrasId = getExtrasIdLocked(db, accountId, key);
- if (extrasId < 0 ) {
- extrasId = insertExtraLocked(db, accountId, key, value);
- if (extrasId < 0) {
- return;
- }
- } else {
- ContentValues values = new ContentValues();
- values.put(EXTRAS_VALUE, value);
- if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) {
- return;
- }
-
+ } else {
+ ContentValues values = new ContentValues();
+ values.put(EXTRAS_VALUE, value);
+ if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) {
+ return;
}
- writeUserDataIntoCacheLocked(accounts, db, account, key, value);
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
}
+ writeUserDataIntoCacheLocked(accounts, db, account, key, value);
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
}
}
@@ -2425,21 +2518,31 @@
userId);
return;
}
-
final int pid = Binder.getCallingPid();
final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
options.putInt(AccountManager.KEY_CALLER_UID, uid);
options.putInt(AccountManager.KEY_CALLER_PID, pid);
+ // Check to see if the Password should be included to the caller.
+ String callerPkg = optionsIn.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
+ boolean isPasswordForwardingAllowed = isPermitted(
+ callerPkg, uid, Manifest.permission.GET_PASSWORD_PRIVILEGED);
+
int usrId = UserHandle.getCallingUserId();
long identityToken = clearCallingIdentity();
try {
UserAccounts accounts = getUserAccounts(usrId);
logRecordWithUid(accounts, DebugDbHelper.ACTION_CALLED_START_ACCOUNT_ADD,
TABLE_ACCOUNTS, uid);
- new StartAccountSession(accounts, response, accountType, expectActivityLaunch,
- null /* accountName */, false /* authDetailsRequired */,
- true /* updateLastAuthenticationTime */) {
+ new StartAccountSession(
+ accounts,
+ response,
+ accountType,
+ expectActivityLaunch,
+ null /* accountName */,
+ false /* authDetailsRequired */,
+ true /* updateLastAuthenticationTime */,
+ isPasswordForwardingAllowed) {
@Override
public void run() throws RemoteException {
mAuthenticator.startAddAccountSession(this, mAccountType, authTokenType,
@@ -2462,12 +2565,21 @@
/** Session that will encrypt the KEY_ACCOUNT_SESSION_BUNDLE in result. */
private abstract class StartAccountSession extends Session {
- public StartAccountSession(UserAccounts accounts, IAccountManagerResponse response,
- String accountType, boolean expectActivityLaunch, String accountName,
- boolean authDetailsRequired, boolean updateLastAuthenticationTime) {
+ private final boolean mIsPasswordForwardingAllowed;
+
+ public StartAccountSession(
+ UserAccounts accounts,
+ IAccountManagerResponse response,
+ String accountType,
+ boolean expectActivityLaunch,
+ String accountName,
+ boolean authDetailsRequired,
+ boolean updateLastAuthenticationTime,
+ boolean isPasswordForwardingAllowed) {
super(accounts, response, accountType, expectActivityLaunch,
true /* stripAuthTokenFromResult */, accountName, authDetailsRequired,
updateLastAuthenticationTime);
+ mIsPasswordForwardingAllowed = isPasswordForwardingAllowed;
}
@Override
@@ -2480,6 +2592,10 @@
checkKeyIntent(
Binder.getCallingUid(),
intent);
+ // Omit passwords if the caller isn't permitted to see them.
+ if (!mIsPasswordForwardingAllowed) {
+ result.remove(AccountManager.KEY_PASSWORD);
+ }
}
IAccountManagerResponse response;
if (mExpectActivityLaunch && result != null
@@ -2826,6 +2942,12 @@
}
int userId = UserHandle.getCallingUserId();
+
+ // Check to see if the Password should be included to the caller.
+ String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
+ boolean isPasswordForwardingAllowed = isPermitted(
+ callerPkg, uid, Manifest.permission.GET_PASSWORD_PRIVILEGED);
+
long identityToken = clearCallingIdentity();
try {
UserAccounts accounts = getUserAccounts(userId);
@@ -2836,7 +2958,8 @@
expectActivityLaunch,
account.name,
false /* authDetailsRequired */,
- true /* updateLastCredentialTime */) {
+ true /* updateLastCredentialTime */,
+ isPasswordForwardingAllowed) {
@Override
public void run() throws RemoteException {
mAuthenticator.startUpdateCredentialsSession(this, account, authTokenType,
@@ -3307,7 +3430,6 @@
long sharedTableAccountId = getAccountIdFromSharedTable(db, account);
final ContentValues values = new ContentValues();
values.put(ACCOUNTS_NAME, newName);
- values.put(ACCOUNTS_PREVIOUS_NAME, account.name);
int r = db.update(
TABLE_SHARED_ACCOUNTS,
values,
@@ -3347,7 +3469,7 @@
public Account[] getSharedAccountsAsUser(int userId) {
userId = handleIncomingUser(userId);
UserAccounts accounts = getUserAccounts(userId);
- ArrayList<Account> accountList = new ArrayList<Account>();
+ ArrayList<Account> accountList = new ArrayList<>();
Cursor cursor = null;
try {
cursor = accounts.openHelper.getReadableDatabase()
@@ -3488,7 +3610,7 @@
}
private long getExtrasIdLocked(SQLiteDatabase db, long accountId, String key) {
- Cursor cursor = db.query(TABLE_EXTRAS, new String[]{EXTRAS_ID},
+ Cursor cursor = db.query(CE_TABLE_EXTRAS, new String[]{EXTRAS_ID},
EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?",
new String[]{key}, null, null, null);
try {
@@ -3729,8 +3851,8 @@
if (accountPresent) {
lastAuthenticatedTime = DatabaseUtils.longForQuery(
mAccounts.openHelper.getReadableDatabase(),
- "select " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
- + " from " +
+ "SELECT " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
+ + " FROM " +
TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND "
+ ACCOUNTS_TYPE + "=?",
new String[] {
@@ -3859,7 +3981,7 @@
Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
}
if (!mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE,
- new UserHandle(mAccounts.userId))) {
+ UserHandle.of(mAccounts.userId))) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed");
}
@@ -3893,15 +4015,16 @@
}
}
- private static String getDatabaseName(int userId) {
+ static String getPreNDatabaseName(int userId) {
File systemDir = Environment.getDataSystemDirectory();
- File databaseFile = new File(Environment.getUserSystemDirectory(userId), DATABASE_NAME);
+ File databaseFile = new File(Environment.getUserSystemDirectory(userId),
+ PRE_N_DATABASE_NAME);
if (userId == 0) {
// Migrate old file, if it exists, to the new location.
// Make sure the new file doesn't already exist. A dummy file could have been
// accidentally created in the old location, causing the new one to become corrupted
// as well.
- File oldFile = new File(systemDir, DATABASE_NAME);
+ File oldFile = new File(systemDir, PRE_N_DATABASE_NAME);
if (oldFile.exists() && !databaseFile.exists()) {
// Check for use directory; create if it doesn't exist, else renameTo will fail
File userDir = Environment.getUserSystemDirectory(userId);
@@ -3918,6 +4041,18 @@
return databaseFile.getPath();
}
+ static String getDeDatabaseName(int userId) {
+ File databaseFile = new File(Environment.getDataSystemDeDirectory(userId),
+ DE_DATABASE_NAME);
+ return databaseFile.getPath();
+ }
+
+ static String getCeDatabaseName(int userId) {
+ File databaseFile = new File(Environment.getDataSystemCeDirectory(userId),
+ CE_DATABASE_NAME);
+ return databaseFile.getPath();
+ }
+
private static class DebugDbHelper{
private DebugDbHelper() {
}
@@ -4050,58 +4185,22 @@
return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
}
- static class DatabaseHelper extends SQLiteOpenHelper {
+ static class PreNDatabaseHelper extends SQLiteOpenHelper {
private final Context mContext;
private final int mUserId;
- public DatabaseHelper(Context context, int userId) {
- super(context, AccountManagerService.getDatabaseName(userId), null, DATABASE_VERSION);
+ public PreNDatabaseHelper(Context context, int userId) {
+ super(context, AccountManagerService.getPreNDatabaseName(userId), null,
+ PRE_N_DATABASE_VERSION);
mContext = context;
mUserId = userId;
}
- /**
- * This call needs to be made while the mCacheLock is held. The way to
- * ensure this is to get the lock any time a method is called ont the DatabaseHelper
- * @param db The database.
- */
@Override
public void onCreate(SQLiteDatabase db) {
- db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
- + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
- + ACCOUNTS_NAME + " TEXT NOT NULL, "
- + ACCOUNTS_TYPE + " TEXT NOT NULL, "
- + ACCOUNTS_PASSWORD + " TEXT, "
- + ACCOUNTS_PREVIOUS_NAME + " TEXT, "
- + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " INTEGER DEFAULT 0, "
- + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
-
- db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " ( "
- + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
- + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, "
- + AUTHTOKENS_TYPE + " TEXT NOT NULL, "
- + AUTHTOKENS_AUTHTOKEN + " TEXT, "
- + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))");
-
- createGrantsTable(db);
-
- db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( "
- + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
- + EXTRAS_ACCOUNTS_ID + " INTEGER, "
- + EXTRAS_KEY + " TEXT NOT NULL, "
- + EXTRAS_VALUE + " TEXT, "
- + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))");
-
- db.execSQL("CREATE TABLE " + TABLE_META + " ( "
- + META_KEY + " TEXT PRIMARY KEY NOT NULL, "
- + META_VALUE + " TEXT)");
-
- createSharedAccountsTable(db);
-
- createAccountsDeletionTrigger(db);
-
- DebugDbHelper.createDebugTable(db);
+ // We use PreNDatabaseHelper only if pre-N db exists
+ throw new IllegalStateException("Legacy database cannot be created - only upgraded!");
}
private void createSharedAccountsTable(SQLiteDatabase db) {
@@ -4161,6 +4260,9 @@
}
}
+ /**
+ * Pre-N database may need an upgrade before splitting
+ */
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
@@ -4222,6 +4324,315 @@
}
}
+ static class DeDatabaseHelper extends SQLiteOpenHelper {
+
+ private final int mUserId;
+ private volatile boolean mCeAttached;
+
+ private DeDatabaseHelper(Context context, int userId) {
+ super(context, getDeDatabaseName(userId), null, DE_DATABASE_VERSION);
+ mUserId = userId;
+ }
+
+ /**
+ * This call needs to be made while the mCacheLock is held. The way to
+ * ensure this is to get the lock any time a method is called ont the DatabaseHelper
+ * @param db The database.
+ */
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ Log.i(TAG, "Creating DE database for user " + mUserId);
+ db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
+ + ACCOUNTS_ID + " INTEGER PRIMARY KEY, "
+ + ACCOUNTS_NAME + " TEXT NOT NULL, "
+ + ACCOUNTS_TYPE + " TEXT NOT NULL, "
+ + ACCOUNTS_PREVIOUS_NAME + " TEXT, "
+ + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " INTEGER DEFAULT 0, "
+ + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
+
+ db.execSQL("CREATE TABLE " + TABLE_META + " ( "
+ + META_KEY + " TEXT PRIMARY KEY NOT NULL, "
+ + META_VALUE + " TEXT)");
+
+ createGrantsTable(db);
+ createSharedAccountsTable(db);
+ createAccountsDeletionTrigger(db);
+ DebugDbHelper.createDebugTable(db);
+ }
+
+ private void createSharedAccountsTable(SQLiteDatabase db) {
+ db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( "
+ + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ + ACCOUNTS_NAME + " TEXT NOT NULL, "
+ + ACCOUNTS_TYPE + " TEXT NOT NULL, "
+ + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
+ }
+
+ private void createAccountsDeletionTrigger(SQLiteDatabase db) {
+ db.execSQL(""
+ + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
+ + " BEGIN"
+ + " DELETE FROM " + TABLE_GRANTS
+ + " WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
+ + " END");
+ }
+
+ private void createGrantsTable(SQLiteDatabase db) {
+ db.execSQL("CREATE TABLE " + TABLE_GRANTS + " ( "
+ + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, "
+ + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL, "
+ + GRANTS_GRANTEE_UID + " INTEGER NOT NULL, "
+ + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE
+ + "," + GRANTS_GRANTEE_UID + "))");
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ Log.i(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
+
+ if (oldVersion != newVersion) {
+ Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
+ }
+ }
+
+ public void attachCeDatabase() {
+ File ceDbFile = new File(getCeDatabaseName(mUserId));
+ SQLiteDatabase db = getWritableDatabase();
+ db.execSQL("ATTACH DATABASE '" + ceDbFile.getPath()+ "' AS ceDb");
+ mCeAttached = true;
+ }
+
+ public boolean isCeDatabaseAttached() {
+ return mCeAttached;
+ }
+
+
+ public SQLiteDatabase getReadableDatabaseUserIsUnlocked() {
+ if(!mCeAttached) {
+ Log.wtf(TAG, "getReadableDatabaseUserIsUnlocked called while user "
+ + mUserId + " is still locked ", new Throwable());
+ }
+ return super.getReadableDatabase();
+ }
+
+ public SQLiteDatabase getWritableDatabaseUserIsUnlocked() {
+ if(!mCeAttached) {
+ Log.wtf(TAG, "getWritableDatabaseUserIsUnlocked called while user " + mUserId
+ + " is still locked ", new Throwable());
+ }
+ return super.getWritableDatabase();
+ }
+
+ @Override
+ public void onOpen(SQLiteDatabase db) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DE_DATABASE_NAME);
+ }
+
+ private void migratePreNDbToDe(File preNDbFile) {
+ Log.i(TAG, "Migrate pre-N database to DE preNDbFile=" + preNDbFile);
+ SQLiteDatabase db = getWritableDatabase();
+ db.execSQL("ATTACH DATABASE '" + preNDbFile.getPath() + "' AS preNDb");
+ db.beginTransaction();
+ // Copy accounts fields
+ db.execSQL("INSERT INTO " + TABLE_ACCOUNTS
+ + "(" + ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ", "
+ + ACCOUNTS_PREVIOUS_NAME + ", " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
+ + ") "
+ + "SELECT " + ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ", "
+ + ACCOUNTS_PREVIOUS_NAME + ", " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
+ + " FROM preNDb." + TABLE_ACCOUNTS);
+ // Copy SHARED_ACCOUNTS
+ db.execSQL("INSERT INTO " + TABLE_SHARED_ACCOUNTS
+ + "(" + SHARED_ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ") " +
+ "SELECT " + SHARED_ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE
+ + " FROM preNDb." + TABLE_SHARED_ACCOUNTS);
+ // Copy DEBUG_TABLE
+ db.execSQL("INSERT INTO " + DebugDbHelper.TABLE_DEBUG
+ + "(" + ACCOUNTS_ID + "," + DebugDbHelper.ACTION_TYPE + ","
+ + DebugDbHelper.TIMESTAMP + "," + DebugDbHelper.CALLER_UID + ","
+ + DebugDbHelper.TABLE_NAME + "," + DebugDbHelper.KEY + ") " +
+ "SELECT " + ACCOUNTS_ID + "," + DebugDbHelper.ACTION_TYPE + ","
+ + DebugDbHelper.TIMESTAMP + "," + DebugDbHelper.CALLER_UID + ","
+ + DebugDbHelper.TABLE_NAME + "," + DebugDbHelper.KEY
+ + " FROM preNDb." + DebugDbHelper.TABLE_DEBUG);
+ // Copy GRANTS
+ db.execSQL("INSERT INTO " + TABLE_GRANTS
+ + "(" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE + ","
+ + GRANTS_GRANTEE_UID + ") " +
+ "SELECT " + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE + ","
+ + GRANTS_GRANTEE_UID + " FROM preNDb." + TABLE_GRANTS);
+ // Copy META
+ db.execSQL("INSERT INTO " + TABLE_META
+ + "(" + META_KEY + "," + META_VALUE + ") "
+ + "SELECT " + META_KEY + "," + META_VALUE + " FROM preNDb." + TABLE_META);
+ db.setTransactionSuccessful();
+ db.endTransaction();
+
+ db.execSQL("DETACH DATABASE preNDb");
+ }
+
+ static DeDatabaseHelper create(Context context, int userId) {
+ File oldDb = new File(getPreNDatabaseName(userId));
+ File newDb = new File(getDeDatabaseName(userId));
+ boolean newDbExists = newDb.exists();
+ DeDatabaseHelper deDatabaseHelper = new DeDatabaseHelper(context, userId);
+ // If the db just created, and there is a legacy db, migrate it
+ if (!newDbExists && oldDb.exists()) {
+ // Migrate legacy db to the latest version - PRE_N_DATABASE_VERSION
+ PreNDatabaseHelper preNDatabaseHelper = new PreNDatabaseHelper(context, userId);
+ // Open the database to force upgrade if required
+ preNDatabaseHelper.getWritableDatabase();
+ preNDatabaseHelper.close();
+ // Move data without SPII to DE
+ deDatabaseHelper.migratePreNDbToDe(oldDb);
+ }
+ return deDatabaseHelper;
+ }
+ }
+
+ static class CeDatabaseHelper extends SQLiteOpenHelper {
+
+ public CeDatabaseHelper(Context context, int userId) {
+ super(context, getCeDatabaseName(userId), null, CE_DATABASE_VERSION);
+ }
+
+ /**
+ * This call needs to be made while the mCacheLock is held.
+ * @param db The database.
+ */
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ Log.i(TAG, "Creating CE database " + getDatabaseName());
+ db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
+ + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ + ACCOUNTS_NAME + " TEXT NOT NULL, "
+ + ACCOUNTS_TYPE + " TEXT NOT NULL, "
+ + ACCOUNTS_PASSWORD + " TEXT, "
+ + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
+
+ db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " ( "
+ + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, "
+ + AUTHTOKENS_TYPE + " TEXT NOT NULL, "
+ + AUTHTOKENS_AUTHTOKEN + " TEXT, "
+ + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))");
+
+ db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( "
+ + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ + EXTRAS_ACCOUNTS_ID + " INTEGER, "
+ + EXTRAS_KEY + " TEXT NOT NULL, "
+ + EXTRAS_VALUE + " TEXT, "
+ + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))");
+
+ createAccountsDeletionTrigger(db);
+ }
+
+ private void createAccountsDeletionTrigger(SQLiteDatabase db) {
+ db.execSQL(""
+ + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
+ + " BEGIN"
+ + " DELETE FROM " + TABLE_AUTHTOKENS
+ + " WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
+ + " DELETE FROM " + TABLE_EXTRAS
+ + " WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
+ + " END");
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ Log.i(TAG, "Upgrade CE from version " + oldVersion + " to version " + newVersion);
+
+ if (oldVersion == 9) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "onUpgrade upgrading to v10");
+ }
+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_META);
+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_SHARED_ACCOUNTS);
+ // Recreate the trigger, since the old one references the table to be removed
+ db.execSQL("DROP TRIGGER IF EXISTS " + TABLE_ACCOUNTS + "Delete");
+ createAccountsDeletionTrigger(db);
+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_GRANTS);
+ db.execSQL("DROP TABLE IF EXISTS " + DebugDbHelper.TABLE_DEBUG);
+ oldVersion ++;
+ }
+
+ if (oldVersion != newVersion) {
+ Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
+ }
+ }
+
+ @Override
+ public void onOpen(SQLiteDatabase db) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + CE_DATABASE_NAME);
+ }
+
+ static String findAccountPasswordByNameAndType(SQLiteDatabase db, String name,
+ String type) {
+ Cursor cursor = db.query(CE_TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD},
+ ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
+ new String[]{name, type}, null, null, null);
+ try {
+ if (cursor.moveToNext()) {
+ return cursor.getString(0);
+ }
+ return null;
+ } finally {
+ cursor.close();
+ }
+ }
+
+ /**
+ * Creates a new {@code CeDatabaseHelper}. If pre-N db file is present at the old location,
+ * it also performs migration to the new CE database.
+ * @param context
+ * @param userId id of the user where the database is located
+ */
+ static CeDatabaseHelper create(Context context, int userId) {
+
+ File oldDatabaseFile = new File(getPreNDatabaseName(userId));
+ File ceDatabaseFile = new File(getCeDatabaseName(userId));
+ boolean newDbExists = ceDatabaseFile.exists();
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "CeDatabaseHelper.create userId=" + userId + " oldDbExists="
+ + oldDatabaseFile.exists() + " newDbExists=" + newDbExists);
+ }
+ boolean removeOldDb = false;
+ if (!newDbExists && oldDatabaseFile.exists()) {
+ removeOldDb = migratePreNDbToCe(oldDatabaseFile, ceDatabaseFile);
+ }
+ // Try to open and upgrade if necessary
+ CeDatabaseHelper ceHelper = new CeDatabaseHelper(context, userId);
+ ceHelper.getWritableDatabase();
+ ceHelper.close();
+ if (removeOldDb) {
+ // TODO STOPSHIP - backup file during testing. Remove file before the release
+ Log.i(TAG, "Migration complete - creating backup of old db " + oldDatabaseFile);
+ renameToBakFile(oldDatabaseFile);
+ }
+ return ceHelper;
+ }
+
+ private static void renameToBakFile(File file) {
+ File bakFile = new File(file.getPath() + ".bak");
+ if (!file.renameTo(bakFile)) {
+ Log.e(TAG, "Cannot move file to " + bakFile);
+ }
+ }
+
+ private static boolean migratePreNDbToCe(File oldDbFile, File ceDbFile) {
+ Log.i(TAG, "Moving pre-N DB " + oldDbFile + " to CE " + ceDbFile);
+ try {
+ FileUtils.copyFileOrThrow(oldDbFile, ceDbFile);
+ } catch (IOException e) {
+ Log.e(TAG, "Cannot copy file to " + ceDbFile + " from " + oldDbFile, e);
+ // Try to remove potentially damaged file if I/O error occurred
+ deleteDbFileWarnIfFailed(ceDbFile);
+ return false;
+ }
+ return true;
+ }
+ }
+
public IBinder onBind(@SuppressWarnings("unused") Intent intent) {
return asBinder();
}
@@ -4666,7 +5077,7 @@
db.endTransaction();
}
cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
- new UserHandle(accounts.userId));
+ UserHandle.of(accounts.userId));
}
}
@@ -4891,7 +5302,7 @@
HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
if (authTokensForAccount == null) {
// need to populate the cache for this account
- final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
+ final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account);
accounts.authTokenCache.put(account, authTokensForAccount);
}
@@ -4899,23 +5310,22 @@
}
}
- protected String readUserDataInternal(UserAccounts accounts, Account account, String key) {
- synchronized (accounts.cacheLock) {
- HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
- if (userDataForAccount == null) {
- // need to populate the cache for this account
- final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
- userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
- accounts.userDataCache.put(account, userDataForAccount);
- }
- return userDataForAccount.get(key);
+ protected String readUserDataInternalLocked(
+ UserAccounts accounts, Account account, String key) {
+ HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
+ if (userDataForAccount == null) {
+ // need to populate the cache for this account
+ final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
+ userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
+ accounts.userDataCache.put(account, userDataForAccount);
}
+ return userDataForAccount.get(key);
}
protected HashMap<String, String> readUserDataForAccountFromDatabaseLocked(
final SQLiteDatabase db, Account account) {
- HashMap<String, String> userDataForAccount = new HashMap<String, String>();
- Cursor cursor = db.query(TABLE_EXTRAS,
+ HashMap<String, String> userDataForAccount = new HashMap<>();
+ Cursor cursor = db.query(CE_TABLE_EXTRAS,
COLUMNS_EXTRAS_KEY_AND_VALUE,
SELECTION_USERDATA_BY_ACCOUNT,
new String[]{account.name, account.type},
@@ -4934,8 +5344,8 @@
protected HashMap<String, String> readAuthTokensForAccountFromDatabaseLocked(
final SQLiteDatabase db, Account account) {
- HashMap<String, String> authTokensForAccount = new HashMap<String, String>();
- Cursor cursor = db.query(TABLE_AUTHTOKENS,
+ HashMap<String, String> authTokensForAccount = new HashMap<>();
+ Cursor cursor = db.query(CE_TABLE_AUTHTOKENS,
COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN,
SELECTION_AUTHTOKENS_BY_ACCOUNT,
new String[]{account.name, account.type},
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index f51fb6c..5d1cb8a 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -43,7 +43,8 @@
import android.util.ArrayMap;
import android.util.ArraySet;
-import com.android.internal.app.ProcessStats;
+import com.android.internal.app.procstats.ProcessStats;
+import com.android.internal.app.procstats.ServiceState;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.TransferPipe;
import com.android.internal.util.FastPrintWriter;
@@ -61,7 +62,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
@@ -343,6 +343,25 @@
return null;
}
+ if (!r.startRequested) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ // Before going further -- if this app is not allowed to run in the
+ // background, then at this point we aren't going to let it period.
+ final int allowed = mAm.checkAllowBackgroundLocked(
+ r.appInfo.uid, r.packageName, callingPid, true);
+ if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
+ Slog.w(TAG, "Background start not allowed: service "
+ + service + " to " + r.name.flattenToShortString()
+ + " from pid=" + callingPid + " uid=" + callingUid
+ + " pkg=" + callingPackage);
+ return null;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
NeededUriGrants neededGrants = mAm.checkGrantUriPermissionFromIntentLocked(
callingUid, r.packageName, service, service.getFlags(), null, r.userId);
@@ -480,7 +499,7 @@
ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
- ProcessStats.ServiceState stracker = r.getTracker();
+ ServiceState stracker = r.getTracker();
if (stracker != null) {
stracker.setStarted(true, mAm.mProcessStats.getMemFactorLocked(), r.lastActivity);
}
@@ -932,7 +951,7 @@
s.lastActivity = SystemClock.uptimeMillis();
if (!s.hasAutoCreateConnections()) {
// This is the first binding, let the tracker know.
- ProcessStats.ServiceState stracker = s.getTracker();
+ ServiceState stracker = s.getTracker();
if (stracker != null) {
stracker.setBound(true, mAm.mProcessStats.getMemFactorLocked(),
s.lastActivity);
@@ -940,7 +959,7 @@
}
}
- mAm.startAssociationLocked(callerApp.uid, callerApp.processName,
+ mAm.startAssociationLocked(callerApp.uid, callerApp.processName, callerApp.curProcState,
s.appInfo.uid, s.name, s.processName);
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
@@ -1274,23 +1293,6 @@
}
r = smap.mServicesByName.get(name);
if (r == null && createIfNeeded) {
- final long token = Binder.clearCallingIdentity();
- try {
- // Before going further -- if this app is not allowed to run in the
- // background, then at this point we aren't going to let it period.
- final int allowed = mAm.checkAllowBackgroundLocked(
- sInfo.applicationInfo.uid, sInfo.packageName, callingPid);
- if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
- Slog.w(TAG, "Background execution not allowed: service "
- + service + " to " + name.flattenToShortString()
- + " from pid=" + callingPid + " uid=" + callingUid
- + " pkg=" + callingPackage);
- return null;
- }
- } finally {
- Binder.restoreCallingIdentity(token);
- }
-
Intent.FilterComparison filter
= new Intent.FilterComparison(service.cloneFilter());
ServiceRestarter res = new ServiceRestarter();
@@ -1365,7 +1367,7 @@
long now = SystemClock.uptimeMillis();
if (r.executeNesting == 0) {
r.executeFg = fg;
- ProcessStats.ServiceState stracker = r.getTracker();
+ ServiceState stracker = r.getTracker();
if (stracker != null) {
stracker.setExecuting(true, mAm.mProcessStats.getMemFactorLocked(), now);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c1ec71c..1587516 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -26,8 +26,8 @@
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.app.ProcessMap;
-import com.android.internal.app.ProcessStats;
import com.android.internal.app.SystemUserHomeActivity;
+import com.android.internal.app.procstats.ProcessStats;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.IResultReceiver;
@@ -40,6 +40,7 @@
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.MemInfoReader;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.ProgressReporter;
import com.android.server.AppOpsService;
import com.android.server.AttributeCache;
import com.android.server.DeviceIdleController;
@@ -55,7 +56,6 @@
import com.android.server.pm.Installer;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.vr.VrManagerInternal;
-import com.android.server.wm.AppTransition;
import com.android.server.wm.WindowManagerService;
import org.xmlpull.v1.XmlPullParser;
@@ -103,7 +103,6 @@
import android.app.ProfilerInfo;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
-import android.app.admin.IDevicePolicyManager;
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
import android.app.backup.IBackupManager;
@@ -167,6 +166,7 @@
import android.os.IBinder;
import android.os.IPermissionController;
import android.os.IProcessInfoService;
+import android.os.IProgressListener;
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
@@ -216,10 +216,6 @@
import android.view.View;
import android.view.WindowManager;
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -364,9 +360,6 @@
public final class ActivityManagerService extends ActivityManagerNative
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
- // File that stores last updated system version and called preboot receivers
- static final String CALLED_PRE_BOOTS_FILENAME = "called_pre_boots.dat";
-
private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityManagerService" : TAG_AM;
private static final String TAG_BACKUP = TAG + POSTFIX_BACKUP;
private static final String TAG_BROADCAST = TAG + POSTFIX_BROADCAST;
@@ -501,8 +494,6 @@
static final int ALLOW_NON_FULL_IN_PROFILE = 1;
static final int ALLOW_FULL_ONLY = 2;
- static final int LAST_PREBOOT_DELIVERED_FILE_VERSION = 10000;
-
// Delay in notifying task stack change listeners (in millis)
static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
@@ -526,6 +517,7 @@
// Determines whether to take full screen screenshots
static final boolean TAKE_FULLSCREEN_SCREENSHOTS = true;
+ public static final float FULLSCREEN_SCREENSHOT_SCALE = 0.6f;
private static native int nativeMigrateToBoost();
private static native int nativeMigrateFromBoost();
@@ -899,6 +891,12 @@
int mNesting;
long mStartTime;
+ // states of the source process when the bind occurred.
+ int mLastState = ActivityManager.MAX_PROCESS_STATE + 1;
+ long mLastStateUptime;
+ long[] mStateTimes = new long[ActivityManager.MAX_PROCESS_STATE
+ - ActivityManager.MIN_PROCESS_STATE+1];
+
Association(int sourceUid, String sourceProcess, int targetUid,
ComponentName targetComponent, String targetProcess) {
mSourceUid = sourceUid;
@@ -1107,22 +1105,20 @@
ComponentName mTopComponent;
String mTopAction = Intent.ACTION_MAIN;
String mTopData;
- boolean mProcessesReady = false;
- boolean mSystemReady = false;
- boolean mBooting = false;
- boolean mCallFinishBooting = false;
- boolean mBootAnimationComplete = false;
- boolean mWaitingUpdate = false;
- boolean mDidUpdate = false;
- boolean mOnBattery = false;
- boolean mLaunchWarningShown = false;
+
+ volatile boolean mProcessesReady = false;
+ volatile boolean mSystemReady = false;
+ volatile boolean mOnBattery = false;
+ volatile int mFactoryTest;
+
+ @GuardedBy("this") boolean mBooting = false;
+ @GuardedBy("this") boolean mCallFinishBooting = false;
+ @GuardedBy("this") boolean mBootAnimationComplete = false;
+ @GuardedBy("this") boolean mLaunchWarningShown = false;
+ @GuardedBy("this") boolean mCheckedForSetup = false;
Context mContext;
- int mFactoryTest;
-
- boolean mCheckedForSetup;
-
/**
* The time at which we will allow normal application switches again,
* after a call to {@link #stopAppSwitches()}.
@@ -1470,12 +1466,16 @@
static final int NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG = 64;
static final int NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG = 65;
static final int NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG = 66;
+ static final int NOTIFY_FORCED_RESIZABLE_MSG = 67;
static final int FIRST_ACTIVITY_STACK_MSG = 100;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
static final int FIRST_COMPAT_MODE_MSG = 300;
static final int FIRST_SUPERVISOR_STACK_MSG = 100;
+ static ServiceThread sKillThread = null;
+ static KillHandler sKillHandler = null;
+
CompatModeDialog mCompatModeDialog;
long mLastMemUsageReportTime = 0;
@@ -1491,14 +1491,38 @@
/** The dimensions of the thumbnails in the Recents UI. */
int mThumbnailWidth;
int mThumbnailHeight;
+ float mFullscreenThumbnailScale;
final ServiceThread mHandlerThread;
final MainHandler mHandler;
final UiHandler mUiHandler;
- final ProcessStartLogger mProcessStartLogger;
PackageManagerInternal mPackageManagerInt;
+ final class KillHandler extends Handler {
+ static final int KILL_PROCESS_GROUP_MSG = 4000;
+
+ public KillHandler(Looper looper) {
+ super(looper, null, true);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case KILL_PROCESS_GROUP_MSG:
+ {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "killProcessGroup");
+ Process.killProcessGroup(msg.arg1 /* uid */, msg.arg2 /* pid */);
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ }
+ break;
+
+ default:
+ super.handleMessage(msg);
+ }
+ }
+ }
+
final class UiHandler extends Handler {
public UiHandler() {
super(com.android.server.UiThread.get().getLooper(), null, true);
@@ -2017,6 +2041,21 @@
}
break;
}
+ case NOTIFY_FORCED_RESIZABLE_MSG: {
+ synchronized (ActivityManagerService.this) {
+ for (int i = mTaskStackListeners.beginBroadcast() - 1; i >= 0; i--) {
+ try {
+ // Make a one-way callback to the listener
+ mTaskStackListeners.getBroadcastItem(i).onActivityForcedResizable(
+ (String) msg.obj, msg.arg1);
+ } catch (RemoteException e){
+ // Handled by the RemoteCallbackList
+ }
+ }
+ mTaskStackListeners.finishBroadcast();
+ }
+ break;
+ }
case NOTIFY_CLEARTEXT_NETWORK_MSG: {
final int uid = msg.arg1;
final byte[] firstPacket = (byte[]) msg.obj;
@@ -2452,7 +2491,13 @@
mHandler = new MainHandler(mHandlerThread.getLooper());
mUiHandler = new UiHandler();
- mProcessStartLogger = new ProcessStartLogger();
+ /* static; one-time init here */
+ if (sKillHandler == null) {
+ sKillThread = new ServiceThread(TAG + ":kill",
+ android.os.Process.THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
+ sKillThread.start();
+ sKillHandler = new KillHandler(sKillThread.getLooper());
+ }
mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
"foreground", BROADCAST_FG_TIMEOUT, false);
@@ -3002,9 +3047,13 @@
}
static void killProcessGroup(int uid, int pid) {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "killProcessGroup");
- Process.killProcessGroup(uid, pid);
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ if (sKillHandler != null) {
+ sKillHandler.sendMessage(
+ sKillHandler.obtainMessage(KillHandler.KILL_PROCESS_GROUP_MSG, uid, pid));
+ } else {
+ Slog.w(TAG, "Asked to kill process group before system bringup!");
+ Process.killProcessGroup(uid, pid);
+ }
}
final void removeLruProcessLocked(ProcessRecord app) {
@@ -3013,7 +3062,7 @@
if (!app.killed) {
Slog.wtfStack(TAG, "Removing process that hasn't been killed: " + app);
Process.killProcessQuiet(app.pid);
- killProcessGroup(app.info.uid, app.pid);
+ killProcessGroup(app.uid, app.pid);
}
if (lrui <= mLruProcessActivityStart) {
mLruProcessActivityStart--;
@@ -3386,7 +3435,7 @@
// clean it up now.
if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG_PROCESSES, "App died: " + app);
checkTime(startTime, "startProcess: bad proc running, killing");
- killProcessGroup(app.info.uid, app.pid);
+ killProcessGroup(app.uid, app.pid);
handleAppDiedLocked(app, true, true);
checkTime(startTime, "startProcess: done killing old proc");
}
@@ -3586,7 +3635,12 @@
app.processName, hostingType,
hostingNameStr != null ? hostingNameStr : "");
- mProcessStartLogger.logIfNeededLocked(app, startResult);
+ try {
+ AppGlobals.getPackageManager().logAppProcessStartIfNeeded(app.processName, app.uid,
+ app.info.seinfo, app.info.sourceDir, startResult.pid);
+ } catch (RemoteException ex) {
+ // Ignore
+ }
if (app.persistent) {
Watchdog.getInstance().processStarted(app.processName, startResult.pid);
@@ -4477,63 +4531,14 @@
}
final long origId = Binder.clearCallingIdentity();
try {
- return startActivityFromRecentsInner(taskId, bOptions);
+ synchronized (this) {
+ return mStackSupervisor.startActivityFromRecentsInner(taskId, bOptions);
+ }
} finally {
Binder.restoreCallingIdentity(origId);
}
}
- final int startActivityFromRecentsInner(int taskId, Bundle bOptions) {
- final TaskRecord task;
- final int callingUid;
- final String callingPackage;
- final Intent intent;
- final int userId;
- synchronized (this) {
- final ActivityOptions activityOptions = (bOptions != null)
- ? new ActivityOptions(bOptions) : null;
- final int launchStackId = (activityOptions != null)
- ? activityOptions.getLaunchStackId() : INVALID_STACK_ID;
-
- if (launchStackId == HOME_STACK_ID) {
- throw new IllegalArgumentException("startActivityFromRecentsInner: Task "
- + taskId + " can't be launch in the home stack.");
- }
- task = mStackSupervisor.anyTaskForIdLocked(taskId, RESTORE_FROM_RECENTS, launchStackId);
- if (task == null) {
- throw new IllegalArgumentException(
- "startActivityFromRecentsInner: Task " + taskId + " not found.");
- }
-
- if (launchStackId != INVALID_STACK_ID) {
- if (launchStackId == DOCKED_STACK_ID && activityOptions != null) {
- mWindowManager.setDockedStackCreateState(
- activityOptions.getDockCreateMode(), null /* initialBounds */);
- }
- if (task.stack.mStackId != launchStackId) {
- mStackSupervisor.moveTaskToStackLocked(
- taskId, launchStackId, ON_TOP, FORCE_FOCUS, "startActivityFromRecents",
- ANIMATE);
- }
- }
-
- // If the user must confirm credentials (e.g. when first launching a work app and the
- // Work Challenge is present) let startActivityInPackage handle the intercepting.
- if (!mUserController.shouldConfirmCredentials(task.userId)
- && task.getRootActivity() != null) {
- moveTaskToFrontLocked(task.taskId, 0, bOptions);
- return ActivityManager.START_TASK_TO_FRONT;
- }
- callingUid = task.mCallingUid;
- callingPackage = task.mCallingPackage;
- intent = task.intent;
- intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
- userId = task.userId;
- }
- return startActivityInPackage(callingUid, callingPackage, intent, null, null, null, 0, 0,
- bOptions, userId, null, task);
- }
-
final int startActivityInPackage(int uid, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo,
String resultWho, int requestCode, int startFlags, Bundle bOptions, int userId,
@@ -5021,7 +5026,7 @@
if (!fromBinderDied) {
Process.killProcessQuiet(pid);
}
- killProcessGroup(app.info.uid, pid);
+ killProcessGroup(app.uid, pid);
app.killed = true;
}
@@ -6057,8 +6062,7 @@
"No more processes in " + old.uidRecord);
enqueueUidChangeLocked(old.uidRecord, -1, UidRecord.CHANGE_GONE);
mActiveUids.remove(uid);
- mBatteryStatsService.noteUidProcessState(uid,
- ActivityManager.PROCESS_STATE_NONEXISTENT);
+ noteUidProcessState(uid, ActivityManager.PROCESS_STATE_NONEXISTENT);
}
old.uidRecord = null;
}
@@ -6083,7 +6087,7 @@
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"Creating new process uid: " + uidRec);
mActiveUids.put(proc.uid, uidRec);
- mBatteryStatsService.noteUidProcessState(uidRec.uid, uidRec.curProcState);
+ noteUidProcessState(uidRec.uid, uidRec.curProcState);
enqueueUidChangeLocked(uidRec, -1, UidRecord.CHANGE_ACTIVE);
}
proc.uidRecord = uidRec;
@@ -6260,6 +6264,7 @@
app.debugging = false;
app.cached = false;
app.killedByAm = false;
+ app.unlocked = mContext.getSystemService(UserManager.class).isUserUnlocked(app.userId);
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
@@ -6577,8 +6582,6 @@
}
}, dumpheapFilter);
- mProcessStartLogger.registerListener(mContext);
-
// Let system services know.
mSystemServiceManager.startBootPhase(SystemService.PHASE_BOOT_COMPLETED);
@@ -7222,8 +7225,17 @@
}
}
+ // NOTE: this is an internal method used by the OnShellCommand implementation only and should
+ // be guarded by permission checking.
+ int getUidState(int uid) {
+ synchronized (this) {
+ UidRecord uidRec = mActiveUids.get(uid);
+ return uidRec == null ? ActivityManager.PROCESS_STATE_NONEXISTENT : uidRec.curProcState;
+ }
+ }
+
@Override
- public boolean inMultiWindow(IBinder token) {
+ public boolean isInMultiWindowMode(IBinder token) {
final long origId = Binder.clearCallingIdentity();
try {
synchronized(this) {
@@ -7240,7 +7252,7 @@
}
@Override
- public boolean inPictureInPicture(IBinder token) {
+ public boolean isInPictureInPictureMode(IBinder token) {
final long origId = Binder.clearCallingIdentity();
try {
synchronized(this) {
@@ -7256,24 +7268,24 @@
}
@Override
- public void enterPictureInPicture(IBinder token) {
+ public void enterPictureInPictureMode(IBinder token) {
final long origId = Binder.clearCallingIdentity();
try {
synchronized(this) {
if (!mSupportsPictureInPicture) {
- throw new IllegalStateException("enterPictureInPicture: "
+ throw new IllegalStateException("enterPictureInPictureMode: "
+ "Device doesn't support picture-in-picture mode.");
}
final ActivityRecord r = ActivityRecord.forTokenLocked(token);
if (r == null) {
- throw new IllegalStateException("enterPictureInPicture: "
+ throw new IllegalStateException("enterPictureInPictureMode: "
+ "Can't find activity for token=" + token);
}
if (!r.supportsPictureInPicture()) {
- throw new IllegalArgumentException("enterPictureInPicture: "
+ throw new IllegalArgumentException("enterPictureInPictureMode: "
+ "Picture-In-Picture not supported for r=" + r);
}
@@ -7282,7 +7294,7 @@
? mDefaultPinnedStackBounds : null;
mStackSupervisor.moveActivityToPinnedStackLocked(
- r, "enterPictureInPicture", bounds);
+ r, "enterPictureInPictureMode", bounds);
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -7575,14 +7587,15 @@
public int getAppStartMode(int uid, String packageName) {
synchronized (this) {
- return checkAllowBackgroundLocked(uid, packageName, -1);
+ return checkAllowBackgroundLocked(uid, packageName, -1, true);
}
}
- int checkAllowBackgroundLocked(int uid, String packageName, int callingPid) {
+ int checkAllowBackgroundLocked(int uid, String packageName, int callingPid,
+ boolean allowWhenForeground) {
UidRecord uidRec = mActiveUids.get(uid);
if (!mLenientBackgroundCheck) {
- if (uidRec == null
+ if (!allowWhenForeground || uidRec == null
|| uidRec.curProcState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) {
if (mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND, uid,
packageName) != AppOpsManager.MODE_ALLOWED) {
@@ -8885,10 +8898,11 @@
continue;
}
}
- if ((flags & ActivityManager.RECENT_INGORE_DOCKED_STACK_TASKS) != 0) {
- if (tr.stack != null && tr.stack.isDockedStack()) {
+ if ((flags & ActivityManager.RECENT_INGORE_DOCKED_STACK_TOP_TASK) != 0) {
+ final ActivityStack stack = tr.stack;
+ if (stack != null && stack.isDockedStack() && stack.topTask() == tr) {
if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
- "Skipping, docked stack task: " + tr);
+ "Skipping, top task in docked stack: " + tr);
continue;
}
}
@@ -9813,7 +9827,8 @@
public void updateLockTaskPackages(int userId, String[] packages) {
final int callingUid = Binder.getCallingUid();
if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
- throw new SecurityException("updateLockTaskPackage called from non-system process");
+ enforceCallingPermission(android.Manifest.permission.UPDATE_LOCK_TASK_PACKAGES,
+ "updateLockTaskPackages()");
}
synchronized (this) {
if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Whitelisting " + userId + ":" +
@@ -10181,7 +10196,8 @@
}
cpr.connections.add(conn);
r.conProviders.add(conn);
- startAssociationLocked(r.uid, r.processName, cpr.uid, cpr.name, cpr.info.processName);
+ startAssociationLocked(r.uid, r.processName, r.curProcState,
+ cpr.uid, cpr.name, cpr.info.processName);
return conn;
}
cpr.addExternalProcessHandleLocked(externalProcessToken);
@@ -10383,7 +10399,7 @@
}
checkTime(startTime, "getContentProviderImpl: after checkContentProviderPermission");
- if (!mProcessesReady && !mDidUpdate && !mWaitingUpdate
+ if (!mProcessesReady
&& !cpi.processName.equals("system")) {
// If this content provider does not run in the system
// process, and the system is not yet ready to run other
@@ -10974,7 +10990,7 @@
final int NA = apps.size();
for (int ia = 0; ia < NA; ia++) {
final ProcessRecord app = apps.valueAt(ia);
- if (app.userId != userId || app.thread == null) continue;
+ if (app.userId != userId || app.thread == null || app.unlocked) continue;
final int NG = app.pkgList.size();
for (int ig = 0; ig < NG; ig++) {
@@ -11422,18 +11438,18 @@
try {
final int currentUserId = mUserController.getCurrentUserIdLocked();
// Get the focused task before launching launcher.
- final int taskId = (mFocusedActivity == null)
- ? -1 : mFocusedActivity.task.taskId;
- startHomeActivityLocked(currentUserId, "notifyLockedProfile");
+
if (mUserController.isLockScreenDisabled(currentUserId)) {
- // If there is no device lock, we first go to launcher and then resume the
- // original task. Work challenge will be shown because we intercepted
- // startActivityFromRecentsInner and the reason why we switch to home stack
- // first is to prevent pressing back button brings user back to the work
- // app.
- if (taskId != -1) {
- startActivityFromRecentsInner(taskId, null);
+
+ // If there is no device lock, we will show the profile's credential page.
+ // startActivityFromRecentsInner is intercepted and will forward user to it.
+ if (mFocusedActivity != null) {
+ mStackSupervisor.startActivityFromRecentsInner(
+ mFocusedActivity.task.taskId, null);
}
+ } else {
+ // Showing launcher to avoid user entering credential twice.
+ startHomeActivityLocked(currentUserId, "notifyLockedProfile");
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -12592,6 +12608,8 @@
com.android.internal.R.dimen.thumbnail_width);
mThumbnailHeight = res.getDimensionPixelSize(
com.android.internal.R.dimen.thumbnail_height);
+ mFullscreenThumbnailScale = res.getFraction(
+ com.android.internal.R.fraction.thumbnail_fullscreen_scale, 1, 1);
mDefaultPinnedStackBounds = Rect.unflattenFromString(res.getString(
com.android.internal.R.string.config_defaultPictureInPictureBounds));
mAppErrors.loadAppsNotReportingCrashesFromConfigLocked(res.getString(
@@ -12604,187 +12622,6 @@
return mSystemReady;
}
- private static File getCalledPreBootReceiversFile() {
- File dataDir = Environment.getDataDirectory();
- File systemDir = new File(dataDir, "system");
- File fname = new File(systemDir, CALLED_PRE_BOOTS_FILENAME);
- return fname;
- }
-
- private static ArrayList<ComponentName> readLastDonePreBootReceivers() {
- ArrayList<ComponentName> lastDoneReceivers = new ArrayList<ComponentName>();
- File file = getCalledPreBootReceiversFile();
- FileInputStream fis = null;
- try {
- fis = new FileInputStream(file);
- DataInputStream dis = new DataInputStream(new BufferedInputStream(fis, 2048));
- int fvers = dis.readInt();
- if (fvers == LAST_PREBOOT_DELIVERED_FILE_VERSION) {
- String vers = dis.readUTF();
- String codename = dis.readUTF();
- String build = dis.readUTF();
- if (android.os.Build.VERSION.RELEASE.equals(vers)
- && android.os.Build.VERSION.CODENAME.equals(codename)
- && android.os.Build.VERSION.INCREMENTAL.equals(build)) {
- int num = dis.readInt();
- while (num > 0) {
- num--;
- String pkg = dis.readUTF();
- String cls = dis.readUTF();
- lastDoneReceivers.add(new ComponentName(pkg, cls));
- }
- }
- }
- } catch (FileNotFoundException e) {
- } catch (IOException e) {
- Slog.w(TAG, "Failure reading last done pre-boot receivers", e);
- } finally {
- if (fis != null) {
- try {
- fis.close();
- } catch (IOException e) {
- }
- }
- }
- return lastDoneReceivers;
- }
-
- private static void writeLastDonePreBootReceivers(ArrayList<ComponentName> list) {
- File file = getCalledPreBootReceiversFile();
- FileOutputStream fos = null;
- DataOutputStream dos = null;
- try {
- fos = new FileOutputStream(file);
- dos = new DataOutputStream(new BufferedOutputStream(fos, 2048));
- dos.writeInt(LAST_PREBOOT_DELIVERED_FILE_VERSION);
- dos.writeUTF(android.os.Build.VERSION.RELEASE);
- dos.writeUTF(android.os.Build.VERSION.CODENAME);
- dos.writeUTF(android.os.Build.VERSION.INCREMENTAL);
- dos.writeInt(list.size());
- for (int i=0; i<list.size(); i++) {
- dos.writeUTF(list.get(i).getPackageName());
- dos.writeUTF(list.get(i).getClassName());
- }
- } catch (IOException e) {
- Slog.w(TAG, "Failure writing last done pre-boot receivers", e);
- file.delete();
- } finally {
- FileUtils.sync(fos);
- if (dos != null) {
- try {
- dos.close();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- }
-
- final class PreBootContinuation extends IIntentReceiver.Stub {
- final Intent intent;
- final Runnable onFinishCallback;
- final ArrayList<ComponentName> doneReceivers;
- final List<ResolveInfo> ris;
- final int[] users;
- int lastRi = -1;
- int curRi = 0;
- int curUser = 0;
-
- PreBootContinuation(Intent _intent, Runnable _onFinishCallback,
- ArrayList<ComponentName> _doneReceivers, List<ResolveInfo> _ris, int[] _users) {
- intent = _intent;
- onFinishCallback = _onFinishCallback;
- doneReceivers = _doneReceivers;
- ris = _ris;
- users = _users;
- }
-
- void go() {
- if (lastRi != curRi) {
- ActivityInfo ai = ris.get(curRi).activityInfo;
- ComponentName comp = new ComponentName(ai.packageName, ai.name);
- intent.setComponent(comp);
- doneReceivers.add(comp);
- lastRi = curRi;
- CharSequence label = ai.loadLabel(mContext.getPackageManager());
- showBootMessage(mContext.getString(R.string.android_preparing_apk, label), false);
- }
- Slog.i(TAG, "Pre-boot of " + intent.getComponent().toShortString()
- + " for user " + users[curUser]);
- EventLogTags.writeAmPreBoot(users[curUser], intent.getComponent().getPackageName());
- broadcastIntentLocked(null, null, intent, null, this,
- 0, null, null, null, AppOpsManager.OP_NONE,
- null, true, false, MY_PID, Process.SYSTEM_UID, users[curUser]);
- }
-
- public void performReceive(Intent intent, int resultCode,
- String data, Bundle extras, boolean ordered,
- boolean sticky, int sendingUser) {
- curUser++;
- if (curUser >= users.length) {
- curUser = 0;
- curRi++;
- if (curRi >= ris.size()) {
- // All done sending broadcasts!
- if (onFinishCallback != null) {
- // The raw IIntentReceiver interface is called
- // with the AM lock held, so redispatch to
- // execute our code without the lock.
- mHandler.post(onFinishCallback);
- }
- return;
- }
- }
- go();
- }
- }
-
- private boolean deliverPreBootCompleted(final Runnable onFinishCallback,
- ArrayList<ComponentName> doneReceivers) {
- Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
- List<ResolveInfo> ris = null;
- try {
- ris = AppGlobals.getPackageManager().queryIntentReceivers(
- intent, null, MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM).getList();
- } catch (RemoteException e) {
- }
- if (ris == null) {
- return false;
- }
- intent.addFlags(Intent.FLAG_RECEIVER_BOOT_UPGRADE | Intent.FLAG_DEBUG_TRIAGED_MISSING);
-
- ArrayList<ComponentName> lastDoneReceivers = readLastDonePreBootReceivers();
- for (int i=0; i<ris.size(); i++) {
- ActivityInfo ai = ris.get(i).activityInfo;
- ComponentName comp = new ComponentName(ai.packageName, ai.name);
- if (lastDoneReceivers.contains(comp)) {
- // We already did the pre boot receiver for this app with the current
- // platform version, so don't do it again...
- ris.remove(i);
- i--;
- // ...however, do keep it as one that has been done, so we don't
- // forget about it when rewriting the file of last done receivers.
- doneReceivers.add(comp);
- }
- }
-
- if (ris.size() <= 0) {
- return false;
- }
-
- // TODO: can we still do this with per user encryption?
- final int[] users = mUserController.getUsers();
- if (users.length <= 0) {
- return false;
- }
-
- PreBootContinuation cont = new PreBootContinuation(intent, onFinishCallback, doneReceivers,
- ris, users);
- cont.go();
- return true;
- }
-
public void systemReady(final Runnable goingCallback) {
synchronized(this) {
if (mSystemReady) {
@@ -12801,33 +12638,7 @@
// Make sure we have the current profile info, since it is needed for security checks.
mUserController.onSystemReady();
-
mRecentTasks.onSystemReadyLocked();
- // Check to see if there are any update receivers to run.
- if (!mDidUpdate) {
- if (mWaitingUpdate) {
- return;
- }
- final ArrayList<ComponentName> doneReceivers = new ArrayList<ComponentName>();
- mWaitingUpdate = deliverPreBootCompleted(new Runnable() {
- public void run() {
- synchronized (ActivityManagerService.this) {
- mDidUpdate = true;
- }
- showBootMessage(mContext.getText(
- R.string.android_upgrading_complete),
- false);
- writeLastDonePreBootReceivers(doneReceivers);
- systemReady(goingCallback);
- }
- }, doneReceivers);
-
- if (mWaitingUpdate) {
- return;
- }
- mDidUpdate = true;
- }
-
mAppOpsService.systemReady();
mSystemReady = true;
}
@@ -13416,10 +13227,11 @@
// Merge several logcat streams, and take the last N lines
InputStreamReader input = null;
try {
- java.lang.Process logcat = new ProcessBuilder("/system/bin/logcat",
- "-v", "time", "-b", "events", "-b", "system", "-b", "main",
- "-b", "crash",
- "-t", String.valueOf(lines)).redirectErrorStream(true).start();
+ java.lang.Process logcat = new ProcessBuilder(
+ "/system/bin/timeout", "-k", "15s", "10s",
+ "/system/bin/logcat", "-v", "time", "-b", "events", "-b", "system",
+ "-b", "main", "-b", "crash", "-t", String.valueOf(lines))
+ .redirectErrorStream(true).start();
try { logcat.getOutputStream().close(); } catch (IOException e) {}
try { logcat.getErrorStream().close(); } catch (IOException e) {}
@@ -13448,6 +13260,7 @@
}
}
+ @Override
public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState() {
enforceNotIsolatedCaller("getProcessesInErrorState");
// assume our apps are happy - lazy create the list
@@ -13524,6 +13337,7 @@
outInfo.processState = app.curProcState;
}
+ @Override
public List<ActivityManager.RunningAppProcessInfo> getRunningAppProcesses() {
enforceNotIsolatedCaller("getRunningAppProcesses");
@@ -13575,6 +13389,7 @@
return runList;
}
+ @Override
public List<ApplicationInfo> getRunningExternalApplications() {
enforceNotIsolatedCaller("getRunningExternalApplications");
List<ActivityManager.RunningAppProcessInfo> runningApps = getRunningAppProcesses();
@@ -13990,10 +13805,27 @@
TimeUtils.formatDuration(dur, pw);
pw.print(" (");
pw.print(ass.mCount);
- pw.println(" times)");
+ pw.print(" times)");
+ pw.print(" ");
+ for (int i=0; i<ass.mStateTimes.length; i++) {
+ long amt = ass.mStateTimes[i];
+ if (ass.mLastState-ActivityManager.MIN_PROCESS_STATE == i) {
+ amt += now - ass.mLastStateUptime;
+ }
+ if (amt != 0) {
+ pw.print(" ");
+ pw.print(ProcessList.makeProcStateString(
+ i + ActivityManager.MIN_PROCESS_STATE));
+ pw.print("=");
+ TimeUtils.formatDuration(amt, pw);
+ if (ass.mLastState-ActivityManager.MIN_PROCESS_STATE == i) {
+ pw.print("*");
+ }
+ }
+ }
+ pw.println();
if (ass.mNesting > 0) {
- pw.print(" ");
- pw.print(" Currently active: ");
+ pw.print(" Currently active: ");
TimeUtils.formatDuration(now - ass.mStartTime, pw);
pw.println();
}
@@ -15625,8 +15457,9 @@
}
for (int oomIndex=0; oomIndex<oomPss.length; oomIndex++) {
- if (oomAdj <= DUMP_MEM_OOM_ADJ[oomIndex]
- || oomIndex == (oomPss.length-1)) {
+ if (oomIndex == (oomPss.length - 1)
+ || (oomAdj >= DUMP_MEM_OOM_ADJ[oomIndex]
+ && oomAdj < DUMP_MEM_OOM_ADJ[oomIndex + 1])) {
oomPss[oomIndex] += myTotalPss;
oomSwapPss[oomIndex] += myTotalSwapPss;
if (oomProcs[oomIndex] == null) {
@@ -18214,6 +18047,9 @@
ActivityRecord starting, boolean initLocale, boolean persistent, int userId) {
int changes = 0;
+ if (mWindowManager != null) {
+ mWindowManager.deferSurfaceLayout();
+ }
if (values != null) {
Configuration newConfig = new Configuration(mConfiguration);
changes = newConfig.updateFrom(values);
@@ -18314,6 +18150,20 @@
null, false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
}
}
+ // Update the configuration with WM first and check if any of the stacks need to be
+ // resized due to the configuration change. If so, resize the stacks now and do any
+ // relaunches if necessary. This way we don't need to relaunch again below in
+ // ensureActivityConfigurationLocked().
+ if (mWindowManager != null) {
+ final int[] resizedStacks = mWindowManager.setNewConfiguration(mConfiguration);
+ if (resizedStacks != null) {
+ for (int stackId : resizedStacks) {
+ final Rect newBounds = mWindowManager.getBoundsForNewConfiguration(stackId);
+ mStackSupervisor.resizeStackLocked(
+ stackId, newBounds, null, null, false, false);
+ }
+ }
+ }
}
boolean kept = true;
@@ -18335,11 +18185,9 @@
!PRESERVE_WINDOWS);
}
}
-
- if (values != null && mWindowManager != null) {
- mWindowManager.setNewConfiguration(mConfiguration);
+ if (mWindowManager != null) {
+ mWindowManager.continueSurfaceLayout();
}
-
return kept;
}
@@ -18431,8 +18279,8 @@
return null;
}
- Association startAssociationLocked(int sourceUid, String sourceProcess, int targetUid,
- ComponentName targetComponent, String targetProcess) {
+ Association startAssociationLocked(int sourceUid, String sourceProcess, int sourceState,
+ int targetUid, ComponentName targetComponent, String targetProcess) {
if (!mTrackingAssociations) {
return null;
}
@@ -18461,7 +18309,8 @@
ass.mCount++;
ass.mNesting++;
if (ass.mNesting == 1) {
- ass.mStartTime = SystemClock.uptimeMillis();
+ ass.mStartTime = ass.mLastStateUptime = SystemClock.uptimeMillis();
+ ass.mLastState = sourceState;
}
return ass;
}
@@ -18490,7 +18339,39 @@
}
ass.mNesting--;
if (ass.mNesting == 0) {
- ass.mTime += SystemClock.uptimeMillis() - ass.mStartTime;
+ long uptime = SystemClock.uptimeMillis();
+ ass.mTime += uptime - ass.mStartTime;
+ ass.mStateTimes[ass.mLastState-ActivityManager.MIN_PROCESS_STATE]
+ += uptime - ass.mLastStateUptime;
+ ass.mLastState = ActivityManager.MAX_PROCESS_STATE + 2;
+ }
+ }
+
+ private void noteUidProcessState(final int uid, final int state) {
+ mBatteryStatsService.noteUidProcessState(uid, state);
+ if (mTrackingAssociations) {
+ for (int i1=0, N1=mAssociations.size(); i1<N1; i1++) {
+ ArrayMap<ComponentName, SparseArray<ArrayMap<String, Association>>> targetComponents
+ = mAssociations.valueAt(i1);
+ for (int i2=0, N2=targetComponents.size(); i2<N2; i2++) {
+ SparseArray<ArrayMap<String, Association>> sourceUids
+ = targetComponents.valueAt(i2);
+ ArrayMap<String, Association> sourceProcesses = sourceUids.get(uid);
+ if (sourceProcesses != null) {
+ for (int i4=0, N4=sourceProcesses.size(); i4<N4; i4++) {
+ Association ass = sourceProcesses.valueAt(i4);
+ if (ass.mNesting >= 1) {
+ // currently associated
+ long uptime = SystemClock.uptimeMillis();
+ ass.mStateTimes[ass.mLastState-ActivityManager.MIN_PROCESS_STATE]
+ += uptime - ass.mLastStateUptime;
+ ass.mLastState = state;
+ ass.mLastStateUptime = uptime;
+ }
+ }
+ }
+ }
+ }
}
}
@@ -18607,9 +18488,14 @@
for (int j = 0; j < activitiesSize; j++) {
final ActivityRecord r = app.activities.get(j);
if (r.app != app) {
- Slog.w(TAG, "Wtf, activity " + r + " in proc activity list not using proc "
- + app + "?!? Using " + r.app + " instead.");
- continue;
+ Log.wtf(TAG, "Found activity " + r + " in proc activity list using " + r.app
+ + " instead of expected " + app);
+ if (r.app == null || (r.app.uid == app.uid)) {
+ // Only fix things up when they look sane
+ r.app = app;
+ } else {
+ continue;
+ }
}
if (r.visible) {
// App has a visible activity; only upgrade adjustment.
@@ -20364,7 +20250,7 @@
}
uidRec.setProcState = uidRec.curProcState;
enqueueUidChangeLocked(uidRec, -1, uidChange);
- mBatteryStatsService.noteUidProcessState(uidRec.uid, uidRec.curProcState);
+ noteUidProcessState(uidRec.uid, uidRec.curProcState);
}
}
@@ -20755,8 +20641,8 @@
}
@Override
- public boolean unlockUser(int userId, byte[] token, byte[] secret) {
- return mUserController.unlockUser(userId, token, secret);
+ public boolean unlockUser(int userId, byte[] token, byte[] secret, IProgressListener listener) {
+ return mUserController.unlockUser(userId, token, secret, new ProgressReporter(0, listener));
}
@Override
@@ -21031,6 +20917,20 @@
mStackSupervisor.mActivityMetricsLogger.notifyTransitionStarting(reason);
}
}
+
+ @Override
+ public void notifyAppTransitionFinished() {
+ synchronized (ActivityManagerService.this) {
+ mStackSupervisor.notifyAppTransitionDone();
+ }
+ }
+
+ @Override
+ public void notifyAppTransitionCancelled() {
+ synchronized (ActivityManagerService.this) {
+ mStackSupervisor.notifyAppTransitionDone();
+ }
+ }
}
private final class SleepTokenImpl extends SleepToken {
@@ -21119,7 +21019,9 @@
// Will bring task to front if it already has a root activity.
final long origId = Binder.clearCallingIdentity();
try {
- startActivityFromRecentsInner(mTaskId, null);
+ synchronized (this) {
+ mStackSupervisor.startActivityFromRecentsInner(mTaskId, null);
+ }
} 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 0253976..9be6b43 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -16,12 +16,12 @@
package com.android.server.am;
+import android.app.ActivityManager;
import android.app.IActivityManager;
import android.os.RemoteException;
import android.os.ShellCommand;
import android.os.UserHandle;
-
-import com.android.internal.util.ArrayUtils;
+import android.util.DebugUtils;
import java.io.PrintWriter;
@@ -64,6 +64,8 @@
return runIsUserStopped(pw);
case "lenient-background-check":
return runLenientBackgroundCheck(pw);
+ case "get-uid-state":
+ return getUidState(pw);
default:
return handleDefaultCommands(cmd);
}
@@ -170,6 +172,17 @@
return 0;
}
+ int getUidState(PrintWriter pw) throws RemoteException {
+ mInternal.enforceCallingPermission(android.Manifest.permission.DUMP,
+ "getUidState()");
+ int state = mInternal.getUidState(Integer.parseInt(getNextArgRequired()));
+ pw.print(state);
+ pw.print(" (");
+ pw.printf(DebugUtils.valueToString(ActivityManager.class, "PROCESS_STATE_", state));
+ pw.println(")");
+ return 0;
+ }
+
@Override
public void onHelp() {
PrintWriter pw = getOutPrintWriter();
@@ -212,7 +225,7 @@
pw.println(" kill [--user <USER_ID> | all | current] <PACKAGE>");
pw.println(" Kill all processes associated with the given application.");
pw.println(" kill-all");
- pw.println(" Kill all processes that are safe to kill (cached, etc)");
+ pw.println(" Kill all processes that are safe to kill (cached, etc).");
pw.println(" write");
pw.println(" Write all pending state to storage.");
pw.println(" track-associations");
@@ -220,9 +233,11 @@
pw.println(" untrack-associations");
pw.println(" Disable and clear association tracking.");
pw.println(" is-user-stopped <USER_ID>");
- pw.println(" returns whether <USER_ID> has been stopped or not");
+ pw.println(" Returns whether <USER_ID> has been stopped or not.");
pw.println(" lenient-background-check [<true|false>]");
- pw.println(" optionally controls lenient background check mode, returns current mode.");
+ pw.println(" Optionally controls lenient background check mode, returns current mode.");
+ pw.println(" get-uid-state <UID>");
+ pw.println(" Gets the process state of an app given its <UID>.");
}
}
}
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 5219827..48f87b6 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -23,7 +23,6 @@
import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SAVED_STATE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_THUMBNAILS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STATES;
@@ -452,24 +451,24 @@
}
}
- void scheduleMultiWindowChanged() {
+ void scheduleMultiWindowModeChanged() {
if (task == null || task.stack == null || app == null || app.thread == null) {
return;
}
try {
// An activity is considered to be in multi-window mode if its task isn't fullscreen.
- app.thread.scheduleMultiWindowChanged(appToken, !task.mFullscreen);
+ app.thread.scheduleMultiWindowModeChanged(appToken, !task.mFullscreen);
} catch (Exception e) {
// If process died, I don't care.
}
}
- void schedulePictureInPictureChanged() {
+ void schedulePictureInPictureModeChanged() {
if (task == null || task.stack == null || app == null || app.thread == null) {
return;
}
try {
- app.thread.schedulePictureInPictureChanged(
+ app.thread.schedulePictureInPictureModeChanged(
appToken, task.stack.mStackId == PINNED_STACK_ID);
} catch (Exception e) {
// If process died, no one cares.
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 26eaa8a..4ec1f61 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -258,6 +258,12 @@
// Current bounds of the stack or null if fullscreen.
Rect mBounds = null;
+ boolean mUpdateBoundsDeferred;
+ boolean mUpdateBoundsDeferredCalled;
+ final Rect mDeferredBounds = new Rect();
+ final Rect mDeferredTaskBounds = new Rect();
+ final Rect mDeferredTaskInsetBounds = new Rect();
+
long mLaunchStartTime = 0;
long mFullyDrawnStartTime = 0;
@@ -427,6 +433,57 @@
mActivityContainer.mActivityDisplay.mDisplay.getSize(out);
}
+ /**
+ * Defers updating the bounds of the stack. If the stack was resized/repositioned while
+ * deferring, the bounds will update in {@link #continueUpdateBounds()}.
+ */
+ void deferUpdateBounds() {
+ if (!mUpdateBoundsDeferred) {
+ mUpdateBoundsDeferred = true;
+ mUpdateBoundsDeferredCalled = false;
+ }
+ }
+
+ /**
+ * Continues updating bounds after updates have been deferred. If there was a resize attempt
+ * between {@link #deferUpdateBounds()} and {@link #continueUpdateBounds()}, the stack will
+ * be resized to that bounds.
+ */
+ void continueUpdateBounds() {
+ final boolean wasDeferred = mUpdateBoundsDeferred;
+ mUpdateBoundsDeferred = false;
+ if (wasDeferred && mUpdateBoundsDeferredCalled) {
+ mStackSupervisor.resizeStackUncheckedLocked(this,
+ mDeferredBounds.isEmpty() ? null : mDeferredBounds,
+ mDeferredTaskBounds.isEmpty() ? null : mDeferredTaskBounds,
+ mDeferredTaskInsetBounds.isEmpty() ? null : mDeferredTaskInsetBounds);
+ }
+ }
+
+ boolean updateBoundsAllowed(Rect bounds, Rect tempTaskBounds,
+ Rect tempTaskInsetBounds) {
+ if (!mUpdateBoundsDeferred) {
+ return true;
+ }
+ if (bounds != null) {
+ mDeferredBounds.set(bounds);
+ } else {
+ mDeferredBounds.setEmpty();
+ }
+ if (tempTaskBounds != null) {
+ mDeferredTaskBounds.set(tempTaskBounds);
+ } else {
+ mDeferredTaskBounds.setEmpty();
+ }
+ if (tempTaskInsetBounds != null) {
+ mDeferredTaskInsetBounds.set(tempTaskInsetBounds);
+ } else {
+ mDeferredTaskInsetBounds.setEmpty();
+ }
+ mUpdateBoundsDeferredCalled = true;
+ return false;
+ }
+
void setBounds(Rect bounds) {
mBounds = mFullscreen ? null : new Rect(bounds);
if (mTaskPositioner != null) {
@@ -929,7 +986,7 @@
// use within SystemUI while keeping memory usage low.
if (ActivityManagerService.TAKE_FULLSCREEN_SCREENSHOTS) {
w = h = -1;
- scale = 0.5f;
+ scale = mService.mFullscreenThumbnailScale;
}
return mWindowManager.screenshotApplications(who.appToken, Display.DEFAULT_DISPLAY,
w, h, scale);
@@ -1814,7 +1871,6 @@
// appropriate for it.
mStackSupervisor.mStoppingActivities.remove(r);
mStackSupervisor.mGoingToSleepActivities.remove(r);
- mStackSupervisor.mWaitingVisibleActivities.remove(r);
} catch (Exception e) {
// Just skip on any failure; we'll make it
// visible when it next restarts.
@@ -1980,7 +2036,9 @@
if (next == null) {
// There are no more activities!
final String reason = "noMoreActivities";
- if (!mFullscreen && adjustFocusToNextFocusableStackLocked(reason)) {
+ final int returnTaskType = prevTask == null || !prevTask.isOverHomeStack()
+ ? HOME_ACTIVITY_TYPE : prevTask.getTaskToReturnTo();
+ if (!mFullscreen && adjustFocusToNextFocusableStackLocked(returnTaskType, reason)) {
// Try to move focus to the next visible stack with a running activity if this
// stack is not covering the entire screen.
return mStackSupervisor.resumeFocusedStackTopActivityLocked(
@@ -1993,8 +2051,6 @@
"resumeTopActivityLocked: No more activities go home");
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
// Only resume home if on home display
- final int returnTaskType = prevTask == null || !prevTask.isOverHomeStack() ?
- HOME_ACTIVITY_TYPE : prevTask.getTaskToReturnTo();
return isOnHomeDisplay() &&
mStackSupervisor.resumeHomeStackTask(returnTaskType, prev, reason);
}
@@ -3004,16 +3060,18 @@
} else {
final TaskRecord task = r.task;
if (r.frontOfTask && task == topTask() && task.isOverHomeStack()) {
+ final int taskToReturnTo = task.getTaskToReturnTo();
+
// For non-fullscreen stack, we want to move the focus to the next visible
// stack to prevent the home screen from moving to the top and obscuring
// other visible stacks.
- if (!mFullscreen && adjustFocusToNextFocusableStackLocked(myReason)) {
+ if (!mFullscreen
+ && adjustFocusToNextFocusableStackLocked(taskToReturnTo, myReason)) {
return;
}
// Move the home stack to the top if this stack is fullscreen or there is no
// other visible stack.
- if (mStackSupervisor.moveHomeStackTaskToTop(
- task.getTaskToReturnTo(), myReason)) {
+ if (mStackSupervisor.moveHomeStackTaskToTop(taskToReturnTo, myReason)) {
// Activity focus was already adjusted. Nothing else to do...
return;
}
@@ -3024,13 +3082,21 @@
mService.setFocusedActivityLocked(mStackSupervisor.topRunningActivityLocked(), myReason);
}
- private boolean adjustFocusToNextFocusableStackLocked(String reason) {
+ private boolean adjustFocusToNextFocusableStackLocked(int taskToReturnTo, String reason) {
final ActivityStack stack = getNextFocusableStackLocked();
final String myReason = reason + " adjustFocusToNextFocusableStack";
if (stack == null) {
return false;
}
- return mService.setFocusedActivityLocked(stack.topRunningActivityLocked(), myReason);
+
+ final ActivityRecord top = stack.topRunningActivityLocked();
+
+ if (stack.isHomeStack() && (top == null || !top.visible)) {
+ // If we will be focusing on the home stack next and its current top activity isn't
+ // visible, then use the task return to value to determine the home task to display next.
+ return mStackSupervisor.moveHomeStackTaskToTop(taskToReturnTo, reason);
+ }
+ return mService.setFocusedActivityLocked(top, myReason);
}
final void stopActivityLocked(ActivityRecord r) {
@@ -4508,6 +4574,8 @@
}
r.configChangeFlags = 0;
+ r.deferRelaunchUntilPaused = false;
+ r.preserveWindowOnDeferredRelaunch = false;
}
boolean willActivityBeVisibleLocked(IBinder token) {
@@ -4721,6 +4789,8 @@
" Task id #" + task.taskId + "\n" +
" mFullscreen=" + task.mFullscreen + "\n" +
" mBounds=" + task.mBounds + "\n" +
+ " mMinimalWidth=" + task.mMinimalWidth + "\n" +
+ " mMinimalHeight=" + task.mMinimalHeight + "\n" +
" mLastNonFullscreenBounds=" + task.mLastNonFullscreenBounds);
if (printed) {
header = null;
@@ -4843,7 +4913,9 @@
// We only need to adjust focused stack if this stack is in focus.
if (isOnHomeDisplay() && mStackSupervisor.isFocusedStack(this)) {
String myReason = reason + " leftTaskHistoryEmpty";
- if (mFullscreen || !adjustFocusToNextFocusableStackLocked(myReason)) {
+ if (mFullscreen
+ || !adjustFocusToNextFocusableStackLocked(
+ task.getTaskToReturnTo(), myReason)) {
mStackSupervisor.moveHomeStackToFront(myReason);
}
}
@@ -4867,18 +4939,18 @@
// add the task to stack first, mTaskPositioner might need the stack association
addTask(task, toTop, "createTaskRecord");
final boolean isLockscreenShown = mService.mLockScreenShown == LOCK_SCREEN_SHOWN;
- if (!layoutTaskInStack(task, info.layout) && mBounds != null && task.isResizeable()
+ if (!layoutTaskInStack(task, info.windowLayout) && mBounds != null && task.isResizeable()
&& !isLockscreenShown) {
task.updateOverrideConfiguration(mBounds);
}
return task;
}
- boolean layoutTaskInStack(TaskRecord task, ActivityInfo.Layout layout) {
+ boolean layoutTaskInStack(TaskRecord task, ActivityInfo.WindowLayout windowLayout) {
if (mTaskPositioner == null) {
return false;
}
- mTaskPositioner.updateDefaultBounds(task, mTaskHistory, layout);
+ mTaskPositioner.updateDefaultBounds(task, mTaskHistory, windowLayout);
return true;
}
@@ -4916,7 +4988,7 @@
private void postAddTask(TaskRecord task, ActivityStack prevStack) {
if (prevStack != null) {
- mStackSupervisor.scheduleReportPictureInPictureChangedIfNeeded(task, prevStack);
+ mStackSupervisor.scheduleReportPictureInPictureModeChangedIfNeeded(task, prevStack);
} else if (task.voiceSession != null) {
try {
task.voiceSession.taskStarted(task.intent, task.taskId);
@@ -4975,7 +5047,7 @@
r.setTask(task, null);
task.addActivityToTop(r);
setAppTask(r, task);
- mStackSupervisor.scheduleReportPictureInPictureChangedIfNeeded(task, prevStack);
+ mStackSupervisor.scheduleReportPictureInPictureModeChangedIfNeeded(task, prevStack);
moveToFrontAndResumeStateIfNeeded(r, wasFocused, wasResumed, "moveActivityToStack");
if (wasResumed) {
prevStack.mResumedActivity = null;
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 7def1bd..7b2a370 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -52,6 +52,7 @@
import android.hardware.input.InputManager;
import android.hardware.input.InputManagerInternal;
import android.os.Binder;
+import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
@@ -102,6 +103,7 @@
import java.util.Set;
import static android.Manifest.permission.START_ANY_ACTIVITY;
+import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
@@ -120,7 +122,6 @@
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
@@ -151,6 +152,7 @@
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityManagerService.ANIMATE;
import static com.android.server.am.ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG;
+import static com.android.server.am.ActivityManagerService.NOTIFY_FORCED_RESIZABLE_MSG;
import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
@@ -167,6 +169,7 @@
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
+import static com.android.server.wm.AppTransition.TRANSIT_DOCK_TASK_FROM_RECENTS;
public final class ActivityStackSupervisor implements DisplayListener {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStackSupervisor" : TAG_AM;
@@ -423,6 +426,11 @@
boolean mAppVisibilitiesChangedSinceLastPause;
/**
+ * Set of tasks that are in resizing mode during an app transition to fill the "void".
+ */
+ private final ArraySet<Integer> mResizingTasksDuringAnimation = new ArraySet<>();
+
+ /**
* Description of a request to start a new activity, which has been held
* due to app switches being disabled.
*/
@@ -1304,7 +1312,7 @@
boolean checkStartAnyActivityPermission(Intent intent, ActivityInfo aInfo,
String resultWho, int requestCode, int callingPid, int callingUid,
String callingPackage, boolean ignoreTargetSecurity, ProcessRecord callerApp,
- ActivityRecord resultRecord, ActivityStack resultStack) {
+ ActivityRecord resultRecord, ActivityStack resultStack, ActivityOptions options) {
final int startAnyPerm = mService.checkPermission(START_ANY_ACTIVITY, callingPid,
callingUid);
if (startAnyPerm == PERMISSION_GRANTED) {
@@ -1358,6 +1366,19 @@
Slog.w(TAG, message);
return false;
}
+ if (options != null && options.getLaunchTaskId() != -1) {
+ final int startInTaskPerm = mService.checkPermission(START_TASKS_FROM_RECENTS,
+ callingPid, callingUid);
+ if (startInTaskPerm != PERMISSION_GRANTED) {
+ final String msg = "Permission Denial: starting " + intent.toString()
+ + " from " + callerApp + " (pid=" + callingPid
+ + ", uid=" + callingUid + ") with launchTaskId="
+ + options.getLaunchTaskId();
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ }
+
return true;
}
@@ -1776,7 +1797,7 @@
// WM resizeTask must be done after the task is moved to the correct stack,
// because Task's setBounds() also updates dim layer's bounds, but that has
// dependency on the stack.
- mWindowManager.resizeTask(task.taskId, bounds, task.mOverrideConfig,
+ mWindowManager.resizeTask(task.taskId, task.mBounds, task.mOverrideConfig,
false /* relayout */, false /* forced */);
}
}
@@ -1788,6 +1809,8 @@
if (DEBUG_STACK) Slog.d(TAG_STACK,
"findTaskToMoveToFront: moved to front of stack=" + task.stack);
+
+ showNonResizeableDockToastIfNeeded(task, INVALID_STACK_ID, task.stack.mStackId);
}
boolean canUseActivityOptionsLaunchBounds(ActivityOptions options, int launchStackId) {
@@ -1924,14 +1947,45 @@
}
}
- private void resizeStackUncheckedLocked(ActivityStack stack, Rect bounds, Rect tempTaskBounds,
+ void deferUpdateBounds(int stackId) {
+ final ActivityStack stack = getStack(stackId);
+ if (stack != null) {
+ stack.deferUpdateBounds();
+ }
+ }
+
+ void continueUpdateBounds(int stackId) {
+ final ActivityStack stack = getStack(stackId);
+ if (stack != null) {
+ stack.continueUpdateBounds();
+ }
+ }
+
+ void notifyAppTransitionDone() {
+ continueUpdateBounds(HOME_STACK_ID);
+ for (int i = mResizingTasksDuringAnimation.size() - 1; i >= 0; i--) {
+ final int taskId = mResizingTasksDuringAnimation.valueAt(i);
+ if (anyTaskForIdLocked(taskId) != null) {
+ mWindowManager.setTaskDockedResizing(taskId, false);
+ }
+ }
+ mResizingTasksDuringAnimation.clear();
+ }
+
+ void resizeStackUncheckedLocked(ActivityStack stack, Rect bounds, Rect tempTaskBounds,
Rect tempTaskInsetBounds) {
bounds = TaskRecord.validateBounds(bounds);
+ if (!stack.updateBoundsAllowed(bounds, tempTaskBounds, tempTaskInsetBounds)) {
+ return;
+ }
+
mTmpBounds.clear();
mTmpConfigs.clear();
mTmpInsetBounds.clear();
final ArrayList<TaskRecord> tasks = stack.getAllTasks();
+ final Rect taskBounds = tempTaskBounds != null ? tempTaskBounds : bounds;
+ final Rect insetBounds = tempTaskInsetBounds != null ? tempTaskInsetBounds : taskBounds;
for (int i = tasks.size() - 1; i >= 0; i--) {
final TaskRecord task = tasks.get(i);
if (task.isResizeable()) {
@@ -1943,9 +1997,7 @@
fitWithinBounds(tempRect2, bounds);
task.updateOverrideConfiguration(tempRect2);
} else {
- task.updateOverrideConfiguration(
- tempTaskBounds != null ? tempTaskBounds : bounds,
- tempTaskInsetBounds != null ? tempTaskInsetBounds : bounds);
+ task.updateOverrideConfiguration(taskBounds, insetBounds);
}
}
@@ -1991,8 +2043,10 @@
resizeStackUncheckedLocked(stack, dockedBounds, tempDockedTaskBounds,
tempDockedTaskInsetBounds);
- if (stack.mFullscreen) {
- // The dock stack went fullscreen which is kinda like dismissing it.
+ // TODO: Checking for isAttached might not be needed as if the user passes in null
+ // dockedBounds then they want the docked stack to be dismissed.
+ if (stack.mFullscreen || (dockedBounds == null && !stack.isAttached())) {
+ // The dock stack either was dismissed or went fullscreen, which is kinda the same.
// In this case we make all other static stacks fullscreen and move all
// docked stack tasks to the fullscreen stack.
for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
@@ -2020,13 +2074,10 @@
mWindowManager.getStackDockedModeBounds(
HOME_STACK_ID, tempRect, true /* ignoreVisibility */);
for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
- if (StackId.isResizeableByDockedStack(i)) {
- ActivityStack otherStack = getStack(i);
- if (otherStack != null) {
- resizeStackLocked(i, tempRect, tempOtherTaskBounds,
- tempOtherTaskInsetBounds, preserveWindows,
- true /* allowResizeInDockedMode */);
- }
+ if (StackId.isResizeableByDockedStack(i) && getStack(i) != null) {
+ resizeStackLocked(i, tempRect, tempOtherTaskBounds,
+ tempOtherTaskInsetBounds, preserveWindows,
+ true /* allowResizeInDockedMode */);
}
}
}
@@ -2686,12 +2737,21 @@
// Called when WindowManager has finished animating the launchingBehind activity to the back.
void handleLaunchTaskBehindCompleteLocked(ActivityRecord r) {
- r.mLaunchTaskBehind = false;
final TaskRecord task = r.task;
- task.setLastThumbnailLocked(task.stack.screenshotActivitiesLocked(r));
+ final ActivityStack stack = task.stack;
+
+ r.mLaunchTaskBehind = false;
+ task.setLastThumbnailLocked(stack.screenshotActivitiesLocked(r));
mRecentTasks.addLocked(task);
mService.notifyTaskStackChangedLocked();
mWindowManager.setAppVisibility(r.appToken, false);
+
+ // When launching tasks behind, update the last active time of the top task after the new
+ // task has been shown briefly
+ final ActivityRecord top = stack.topActivity();
+ if (top != null) {
+ top.task.touchActiveTime();
+ }
}
void scheduleLaunchTaskBehindComplete(IBinder token) {
@@ -3309,10 +3369,14 @@
return;
}
- if (!task.canGoInDockedStack() || task.mResizeMode == RESIZE_MODE_FORCE_RESIZEABLE) {
- // Display warning toast if we tried to put a non-dockable task in the docked stack or
- // the task was forced to be resizable by the system.
+ if (!task.canGoInDockedStack()) {
+ // Display a warning toast that we tried to put a non-dockable task in the docked stack.
mWindowManager.scheduleShowNonResizeableDockToast(task.taskId);
+ } else if (task.mResizeMode == RESIZE_MODE_FORCE_RESIZEABLE) {
+ String packageName = task.getTopActivity() != null
+ ? task.getTopActivity().appInfo.packageName : null;
+ mService.mHandler.obtainMessage(NOTIFY_FORCED_RESIZABLE_MSG, task.taskId, 0,
+ packageName).sendToTarget();
}
}
@@ -3467,7 +3531,7 @@
mActivityMetricsLogger.logWindowState();
}
- void scheduleReportMultiWindowChanged(TaskRecord task) {
+ void scheduleReportMultiWindowModeChanged(TaskRecord task) {
for (int i = task.mActivities.size() - 1; i >= 0; i--) {
final ActivityRecord r = task.mActivities.get(i);
if (r.app != null && r.app.thread != null) {
@@ -3480,7 +3544,7 @@
}
}
- void scheduleReportPictureInPictureChangedIfNeeded(TaskRecord task, ActivityStack prevStack) {
+ void scheduleReportPictureInPictureModeChangedIfNeeded(TaskRecord task, ActivityStack prevStack) {
final ActivityStack stack = task.stack;
if (prevStack == null || prevStack == stack
|| (prevStack.mStackId != PINNED_STACK_ID && stack.mStackId != PINNED_STACK_ID)) {
@@ -3518,7 +3582,7 @@
synchronized (mService) {
for (int i = mMultiWindowModeChangedActivities.size() - 1; i >= 0; i--) {
final ActivityRecord r = mMultiWindowModeChangedActivities.remove(i);
- r.scheduleMultiWindowChanged();
+ r.scheduleMultiWindowModeChanged();
}
}
} break;
@@ -3526,7 +3590,7 @@
synchronized (mService) {
for (int i = mPipModeChangedActivities.size() - 1; i >= 0; i--) {
final ActivityRecord r = mPipModeChangedActivities.remove(i);
- r.schedulePictureInPictureChanged();
+ r.schedulePictureInPictureModeChanged();
}
}
} break;
@@ -4129,4 +4193,80 @@
throw new IllegalStateException("Failed to find a stack behind stack=" + stack
+ " in=" + stacks);
}
+
+ /**
+ * Puts a task into resizing mode during the next app transition.
+ *
+ * @param taskId the id of the task to put into resizing mode
+ */
+ private void setResizingDuringAnimation(int taskId) {
+ mResizingTasksDuringAnimation.add(taskId);
+ mWindowManager.setTaskDockedResizing(taskId, true);
+ }
+
+ final int startActivityFromRecentsInner(int taskId, Bundle bOptions) {
+ final TaskRecord task;
+ final int callingUid;
+ final String callingPackage;
+ final Intent intent;
+ final int userId;
+ final ActivityOptions activityOptions = (bOptions != null)
+ ? new ActivityOptions(bOptions) : null;
+ final int launchStackId = (activityOptions != null)
+ ? activityOptions.getLaunchStackId() : INVALID_STACK_ID;
+
+ if (launchStackId == HOME_STACK_ID) {
+ throw new IllegalArgumentException("startActivityFromRecentsInner: Task "
+ + taskId + " can't be launch in the home stack.");
+ }
+ task = anyTaskForIdLocked(taskId, RESTORE_FROM_RECENTS, launchStackId);
+ if (task == null) {
+ throw new IllegalArgumentException(
+ "startActivityFromRecentsInner: Task " + taskId + " not found.");
+ }
+
+ if (launchStackId != INVALID_STACK_ID) {
+ if (launchStackId == DOCKED_STACK_ID) {
+ mWindowManager.setDockedStackCreateState(
+ activityOptions.getDockCreateMode(), null /* initialBounds */);
+
+ // Defer updating the stack in which recents is until the app transition is done, to
+ // not run into issues where we still need to draw the task in recents but the
+ // docked stack is already created.
+ deferUpdateBounds(HOME_STACK_ID);
+ mWindowManager.prepareAppTransition(TRANSIT_DOCK_TASK_FROM_RECENTS, false);
+ }
+ if (task.stack.mStackId != launchStackId) {
+ moveTaskToStackLocked(
+ taskId, launchStackId, ON_TOP, FORCE_FOCUS, "startActivityFromRecents",
+ ANIMATE);
+ }
+ }
+
+ // If the user must confirm credentials (e.g. when first launching a work app and the
+ // Work Challenge is present) let startActivityInPackage handle the intercepting.
+ if (!mService.mUserController.shouldConfirmCredentials(task.userId)
+ && task.getRootActivity() != null) {
+ mService.moveTaskToFrontLocked(task.taskId, 0, bOptions);
+
+ // If we are launching the task in the docked stack, put it into resizing mode so
+ // the window renders full-screen with the background filling the void. Also only
+ // call this at the end to make sure that tasks exists on the window manager side.
+ if (launchStackId == DOCKED_STACK_ID) {
+ setResizingDuringAnimation(taskId);
+ }
+ return ActivityManager.START_TASK_TO_FRONT;
+ }
+ callingUid = task.mCallingUid;
+ callingPackage = task.mCallingPackage;
+ intent = task.intent;
+ intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
+ userId = task.userId;
+ int result = mService.startActivityInPackage(callingUid, callingPackage, intent, null,
+ null, null, 0, 0, bOptions, userId, null, task);
+ if (launchStackId == DOCKED_STACK_ID) {
+ setResizingDuringAnimation(task.taskId);
+ }
+ return result;
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
index 13d90e3..76dfd01 100644
--- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
@@ -26,6 +26,7 @@
import static android.content.Intent.EXTRA_TASK_ID;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
import android.app.KeyguardManager;
@@ -192,14 +193,14 @@
Binder.getCallingUid(), userId, null, null, 0, new Intent[]{ intent },
new String[]{ resolvedType },
FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT | FLAG_IMMUTABLE, null);
- final int flags = intent.getFlags();
final KeyguardManager km = (KeyguardManager) mService.mContext
.getSystemService(KEYGUARD_SERVICE);
final Intent newIntent = km.createConfirmDeviceCredentialIntent(null, null, userId);
if (newIntent == null) {
return null;
}
- newIntent.setFlags(flags | FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ newIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS |
+ FLAG_ACTIVITY_TASK_ON_HOME);
newIntent.putExtra(EXTRA_PACKAGE_NAME, aInfo.packageName);
newIntent.putExtra(EXTRA_INTENT, new IntentSender(target));
return newIntent;
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index af69c93..3bbc4521 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -362,7 +362,7 @@
boolean abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho,
requestCode, callingPid, callingUid, callingPackage, ignoreTargetSecurity, callerApp,
- resultRecord, resultStack);
+ resultRecord, resultStack, options);
abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
callingPid, resolvedType, aInfo.applicationInfo);
@@ -547,12 +547,18 @@
}
if (startedActivityStackId == DOCKED_STACK_ID && prevFocusedStackId == HOME_STACK_ID) {
- // We launch an activity while being in home stack, which means either launcher or
- // recents into docked stack. We don't want the launched activity to be alone in a
- // docked stack, so we want to immediately launch recents too.
- if (DEBUG_RECENTS) Slog.d(TAG, "Scheduling recents launch.");
- mWindowManager.showRecentApps();
- return;
+ final ActivityStack homeStack = mSupervisor.getStack(HOME_STACK_ID);
+ final ActivityRecord topActivityHomeStack = homeStack != null
+ ? homeStack.topRunningActivityLocked() : null;
+ if (topActivityHomeStack == null
+ || topActivityHomeStack.mActivityType != RECENTS_ACTIVITY_TYPE) {
+ // We launch an activity while being in home stack, which means either launcher or
+ // recents into docked stack. We don't want the launched activity to be alone in a
+ // docked stack, so we want to immediately launch recents too.
+ if (DEBUG_RECENTS) Slog.d(TAG, "Scheduling recents launch.");
+ mWindowManager.showRecentApps();
+ return;
+ }
}
if (startedActivityStackId == PINNED_STACK_ID
@@ -879,6 +885,9 @@
ActivityRecord intentActivity = getReusableIntentActivity();
+ final int preferredLaunchStackId =
+ (mOptions != null) ? mOptions.getLaunchStackId() : INVALID_STACK_ID;
+
if (intentActivity != null) {
// When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused but
// still needs to be a lock task mode violation since the task gets cleared out and
@@ -977,6 +986,8 @@
}
top.deliverNewIntentLocked(
mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage);
+ mSupervisor.showNonResizeableDockToastIfNeeded(mStartActivity.task,
+ preferredLaunchStackId, topStack.mStackId);
return START_DELIVERED_TO_TOP;
}
@@ -1063,8 +1074,6 @@
}
mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);
- final int preferredLaunchStackId =
- (mOptions != null) ? mOptions.getLaunchStackId() : INVALID_STACK_ID;
mSupervisor.showNonResizeableDockToastIfNeeded(
mStartActivity.task, preferredLaunchStackId, mTargetStack.mStackId);
@@ -1297,7 +1306,10 @@
// same component, then instead of launching bring that one to the front.
putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
ActivityRecord intentActivity = null;
- if (putIntoExistingTask) {
+ if (mOptions != null && mOptions.getLaunchTaskId() != -1) {
+ final TaskRecord task = mSupervisor.anyTaskForIdLocked(mOptions.getLaunchTaskId());
+ intentActivity = task != null ? task.getTopActivity() : null;
+ } else if (putIntoExistingTask) {
// See if there is a task to bring to the front. If this is a SINGLE_INSTANCE
// activity, there can be one and only one instance of it in the history, and it is
// always in its own unique task, so we do a special search.
@@ -1349,6 +1361,15 @@
intentActivity.task, mNoAnimation, mOptions,
mStartActivity.appTimeTracker, "bringingFoundTaskToFront");
mMovedToFront = true;
+ } else if ((launchStack.mStackId == DOCKED_STACK_ID
+ || launchStack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID)
+ && (mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
+ // If we want to launch adjacent and mTargetStack is not the computed
+ // launch stack - move task to top of computed stack.
+ mSupervisor.moveTaskToStackLocked(intentActivity.task.taskId,
+ launchStack.mStackId, ON_TOP, FORCE_FOCUS, "launchToSide",
+ ANIMATE);
+ mMovedToFront = true;
}
mOptions = null;
}
@@ -1756,26 +1777,41 @@
if (!launchToSideAllowed || (launchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) == 0) {
return null;
}
+ // Otherwise handle adjacent launch.
// The parent activity doesn't want to launch the activity on top of itself, but
// instead tries to put it onto other side in side-by-side mode.
final ActivityStack parentStack = task != null ? task.stack
: r.mInitialActivityContainer != null ? r.mInitialActivityContainer.mStack
: mSupervisor.mFocusedStack;
- if (parentStack != null && parentStack.mStackId == DOCKED_STACK_ID) {
- // If parent was in docked stack, the natural place to launch another activity
- // will be fullscreen, so it can appear alongside the docked window.
- return mSupervisor.getStack(FULLSCREEN_WORKSPACE_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
+
+ if (parentStack != mSupervisor.mFocusedStack) {
+ // If task's parent stack is not focused - use it during adjacent launch.
+ return parentStack;
} else {
- // If the parent is not in the docked stack, we check if there is docked window
- // and if yes, we will launch into that stack. If not, we just put the new
- // activity into parent's stack, because we can't find a better place.
- final ActivityStack stack = mSupervisor.getStack(DOCKED_STACK_ID);
- if (stack != null && stack.getStackVisibilityLocked(r) == STACK_INVISIBLE) {
- // There is a docked stack, but it isn't visible, so we can't launch into that.
- return null;
+ if (mSupervisor.mFocusedStack != null && task == mSupervisor.mFocusedStack.topTask()) {
+ // If task is already on top of focused stack - use it. We don't want to move the
+ // existing focused task to adjacent stack, just deliver new intent in this case.
+ return mSupervisor.mFocusedStack;
+ }
+
+ if (parentStack != null && parentStack.mStackId == DOCKED_STACK_ID) {
+ // If parent was in docked stack, the natural place to launch another activity
+ // will be fullscreen, so it can appear alongside the docked window.
+ return mSupervisor.getStack(FULLSCREEN_WORKSPACE_STACK_ID, CREATE_IF_NEEDED,
+ ON_TOP);
} else {
- return stack;
+ // If the parent is not in the docked stack, we check if there is docked window
+ // and if yes, we will launch into that stack. If not, we just put the new
+ // activity into parent's stack, because we can't find a better place.
+ final ActivityStack dockedStack = mSupervisor.getStack(DOCKED_STACK_ID);
+ if (dockedStack != null
+ && dockedStack.getStackVisibilityLocked(r) == STACK_INVISIBLE) {
+ // There is a docked stack, but it isn't visible, so we can't launch into that.
+ return null;
+ } else {
+ return dockedStack;
+ }
}
}
}
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 6cd7561..68bd2fd 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -724,9 +724,7 @@
final boolean crashSilenced = mAppsNotReportingCrashes != null &&
mAppsNotReportingCrashes.contains(proc.info.packageName);
if (mService.canShowErrorDialogs() && !crashSilenced) {
- Dialog d = new AppErrorDialog(mContext, mService, data);
- d.show();
- proc.crashDialog = d;
+ proc.crashDialog = new AppErrorDialog(mContext, mService, data);
} else {
// The device is asleep, so just pretend that the user
// saw a crash dialog and hit "force quit".
@@ -735,6 +733,10 @@
}
}
}
+ // If we've created a crash dialog, show it without the lock held
+ if(data.proc.crashDialog != null) {
+ data.proc.crashDialog.show();
+ }
}
void stopReportingCrashesLocked(ProcessRecord proc) {
@@ -924,6 +926,7 @@
}
void handleShowAnrUi(Message msg) {
+ Dialog d = null;
synchronized (mService) {
HashMap<String, Object> data = (HashMap<String, Object>) msg.obj;
ProcessRecord proc = (ProcessRecord)data.get("app");
@@ -944,10 +947,9 @@
null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
if (mService.canShowErrorDialogs()) {
- Dialog d = new AppNotRespondingDialog(mService,
+ d = new AppNotRespondingDialog(mService,
mContext, proc, (ActivityRecord)data.get("activity"),
msg.arg1 != 0);
- d.show();
proc.anrDialog = d;
} else {
MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_ANR,
@@ -956,6 +958,10 @@
mService.killAppAtUsersRequest(proc, null);
}
}
+ // If we've created a crash dialog, show it without the lock held
+ if (d != null) {
+ d.show();
+ }
}
/**
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 71a1f97..3d42047 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1170,7 +1170,8 @@
}
if (useCheckinFormat) {
- List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(0);
+ List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(
+ PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.MATCH_ALL);
if (isRealCheckin) {
// For a real checkin, first we want to prefer to use the last complete checkin
// file if there is one.
@@ -1223,7 +1224,8 @@
// WiFi keeps an accumulated total of stats, unlike Bluetooth.
// Keep the last WiFi stats so we can compute a delta.
@GuardedBy("mExternalStatsLock")
- private WifiActivityEnergyInfo mLastInfo = new WifiActivityEnergyInfo(0, 0, 0, 0, 0, 0);
+ private WifiActivityEnergyInfo mLastInfo =
+ new WifiActivityEnergyInfo(0, 0, 0, new long[]{0}, 0, 0, 0);
@GuardedBy("mExternalStatsLock")
private WifiActivityEnergyInfo pullWifiEnergyInfoLocked() {
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 45e3a76..37c7765f 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -50,7 +50,6 @@
import android.util.Slog;
import android.util.TimeUtils;
import com.android.server.DeviceIdleController;
-import com.android.server.LocalServices;
import static com.android.server.am.ActivityManagerDebugConfig.*;
@@ -563,7 +562,7 @@
}
if (!skip) {
final int allowed = mService.checkAllowBackgroundLocked(filter.receiverList.uid,
- filter.packageName, -1);
+ filter.packageName, -1, true);
if (allowed == ActivityManager.APP_START_MODE_DISABLED) {
Slog.w(TAG, "Background execution not allowed: receiving "
+ r.intent
@@ -1102,21 +1101,21 @@
if (!skip) {
final int allowed = mService.checkAllowBackgroundLocked(
- info.activityInfo.applicationInfo.uid, info.activityInfo.packageName, -1);
+ info.activityInfo.applicationInfo.uid, info.activityInfo.packageName, -1,
+ false);
if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
// We won't allow this receiver to be launched if the app has been
- // completely disabled from launches, or it is delayed and the broadcast
- // was not explicitly sent to it and this would result in a new process
- // for it being created.
+ // completely disabled from launches, or it was not explicitly sent
+ // to it and the app is in a state that should not receive it
+ // (depending on how checkAllowBackgroundLocked has determined that).
if (allowed == ActivityManager.APP_START_MODE_DISABLED) {
Slog.w(TAG, "Background execution disabled: receiving "
+ r.intent + " to "
+ component.flattenToShortString());
skip = true;
- }
- if (((r.intent.getFlags()&Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND) != 0)
+ } else if (((r.intent.getFlags()&Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND) != 0)
|| (r.intent.getComponent() == null
- && r.intent.getPackage() == null && app == null
+ && r.intent.getPackage() == null
&& ((r.intent.getFlags()
& Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) == 0))) {
Slog.w(TAG, "Background execution not allowed: receiving "
diff --git a/services/core/java/com/android/server/am/HealthStatsBatteryStatsWriter.java b/services/core/java/com/android/server/am/HealthStatsBatteryStatsWriter.java
index 39c6ce6..9fb51c1 100644
--- a/services/core/java/com/android/server/am/HealthStatsBatteryStatsWriter.java
+++ b/services/core/java/com/android/server/am/HealthStatsBatteryStatsWriter.java
@@ -122,7 +122,8 @@
// Battery Stats stores the GPS sensors with a bogus key in this API. Pull it out
// as a separate metric here so as to not expose that in the API.
if (sensorId == BatteryStats.Uid.Sensor.GPS) {
- addTimer(uidWriter, UidHealthStats.TIMER_GPS_SENSOR, sensors.valueAt(i).getSensorTime());
+ addTimer(uidWriter, UidHealthStats.TIMER_GPS_SENSOR,
+ sensors.valueAt(i).getSensorTime());
} else {
addTimers(uidWriter, UidHealthStats.TIMERS_SENSORS, Integer.toString(sensorId),
sensors.valueAt(i).getSensorTime());
@@ -131,7 +132,7 @@
// STATS_PIDS
pids = uid.getPidStats();
- N = sensors.size();
+ N = pids.size();
for (int i=0; i<N; i++) {
final HealthStatsWriter writer = new HealthStatsWriter(PidHealthStats.CONSTANTS);
writePid(writer, pids.valueAt(i));
@@ -241,7 +242,8 @@
addTimer(uidWriter, UidHealthStats.TIMER_CAMERA, uid.getCameraTurnedOnTimer());
// TIMER_FOREGROUND_ACTIVITY
- addTimer(uidWriter, UidHealthStats.TIMER_FOREGROUND_ACTIVITY, uid.getForegroundActivityTimer());
+ addTimer(uidWriter, UidHealthStats.TIMER_FOREGROUND_ACTIVITY,
+ uid.getForegroundActivityTimer());
// TIMER_BLUETOOTH_SCAN
addTimer(uidWriter, UidHealthStats.TIMER_BLUETOOTH_SCAN, uid.getBluetoothScanTimer());
diff --git a/services/core/java/com/android/server/am/LaunchingTaskPositioner.java b/services/core/java/com/android/server/am/LaunchingTaskPositioner.java
index 4ba1d0d..d652341 100644
--- a/services/core/java/com/android/server/am/LaunchingTaskPositioner.java
+++ b/services/core/java/com/android/server/am/LaunchingTaskPositioner.java
@@ -109,22 +109,22 @@
*
* @param task Task for which we want to find bounds that won't collide with other.
* @param tasks Existing tasks with which we don't want to collide.
- * @param layout Optional information from the client about how it would like to be sized
+ * @param windowLayout Optional information from the client about how it would like to be sized
* and positioned.
*/
void updateDefaultBounds(TaskRecord task, ArrayList<TaskRecord> tasks,
- @Nullable ActivityInfo.Layout layout) {
+ @Nullable ActivityInfo.WindowLayout windowLayout) {
if (!mDefaultStartBoundsConfigurationSet) {
return;
}
- if (layout == null) {
+ if (windowLayout == null) {
positionCenter(task, tasks, mDefaultFreeformWidth, mDefaultFreeformHeight);
return;
}
- int width = getFinalWidth(layout);
- int height = getFinalHeight(layout);
- int verticalGravity = layout.gravity & Gravity.VERTICAL_GRAVITY_MASK;
- int horizontalGravity = layout.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
+ int width = getFinalWidth(windowLayout);
+ int height = getFinalHeight(windowLayout);
+ int verticalGravity = windowLayout.gravity & Gravity.VERTICAL_GRAVITY_MASK;
+ int horizontalGravity = windowLayout.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
if (verticalGravity == Gravity.TOP) {
if (horizontalGravity == Gravity.RIGHT) {
positionTopRight(task, tasks, width, height);
@@ -140,30 +140,30 @@
} else {
// Some fancy gravity setting that we don't support yet. We just put the activity in the
// center.
- Slog.w(TAG, "Received unsupported gravity: " + layout.gravity
+ Slog.w(TAG, "Received unsupported gravity: " + windowLayout.gravity
+ ", positioning in the center instead.");
positionCenter(task, tasks, width, height);
}
}
- private int getFinalWidth(ActivityInfo.Layout layout) {
+ private int getFinalWidth(ActivityInfo.WindowLayout windowLayout) {
int width = mDefaultFreeformWidth;
- if (layout.width > 0) {
- width = layout.width;
+ if (windowLayout.width > 0) {
+ width = windowLayout.width;
}
- if (layout.widthFraction > 0) {
- width = (int) (mAvailableRect.width() * layout.widthFraction);
+ if (windowLayout.widthFraction > 0) {
+ width = (int) (mAvailableRect.width() * windowLayout.widthFraction);
}
return width;
}
- private int getFinalHeight(ActivityInfo.Layout layout) {
+ private int getFinalHeight(ActivityInfo.WindowLayout windowLayout) {
int height = mDefaultFreeformHeight;
- if (layout.height > 0) {
- height = layout.height;
+ if (windowLayout.height > 0) {
+ height = windowLayout.height;
}
- if (layout.heightFraction > 0) {
- height = (int) (mAvailableRect.height() * layout.heightFraction);
+ if (windowLayout.heightFraction > 0) {
+ height = (int) (mAvailableRect.height() * windowLayout.heightFraction);
}
return height;
}
diff --git a/services/core/java/com/android/server/am/PreBootBroadcaster.java b/services/core/java/com/android/server/am/PreBootBroadcaster.java
new file mode 100644
index 0000000..1825c88
--- /dev/null
+++ b/services/core/java/com/android/server/am/PreBootBroadcaster.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.am;
+
+import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
+
+import android.app.AppOpsManager;
+import android.content.ComponentName;
+import android.content.IIntentReceiver;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.util.ProgressReporter;
+
+import java.util.List;
+
+/**
+ * Simple broadcaster that sends {@link Intent#ACTION_PRE_BOOT_COMPLETED} to all
+ * system apps that register for it. Override {@link #onFinished()} to handle
+ * when all broadcasts are finished.
+ */
+public abstract class PreBootBroadcaster extends IIntentReceiver.Stub {
+ private static final String TAG = "PreBootBroadcaster";
+
+ private final ActivityManagerService mService;
+ private final int mUserId;
+ private final ProgressReporter mProgress;
+
+ private final Intent mIntent;
+ private final List<ResolveInfo> mTargets;
+
+ private int mIndex = 0;
+
+ public PreBootBroadcaster(ActivityManagerService service, int userId,
+ ProgressReporter progress) {
+ mService = service;
+ mUserId = userId;
+ mProgress = progress;
+
+ mIntent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
+ mIntent.addFlags(Intent.FLAG_RECEIVER_BOOT_UPGRADE | Intent.FLAG_DEBUG_TRIAGED_MISSING);
+
+ mTargets = mService.mContext.getPackageManager().queryBroadcastReceiversAsUser(mIntent,
+ MATCH_SYSTEM_ONLY, UserHandle.of(userId));
+ }
+
+ public void sendNext() {
+ if (mIndex >= mTargets.size()) {
+ onFinished();
+ return;
+ }
+
+ final ResolveInfo ri = mTargets.get(mIndex++);
+ final ComponentName componentName = ri.activityInfo.getComponentName();
+
+ final CharSequence label = ri.activityInfo.loadLabel(mService.mContext.getPackageManager());
+ mProgress.setProgress(mIndex, mTargets.size(),
+ mService.mContext.getString(R.string.android_preparing_apk, label));
+
+ Slog.i(TAG, "Pre-boot of " + componentName.toShortString() + " for user " + mUserId);
+ EventLogTags.writeAmPreBoot(mUserId, componentName.getPackageName());
+
+ mIntent.setComponent(componentName);
+ mService.broadcastIntentLocked(null, null, mIntent, null, this, 0, null, null, null,
+ AppOpsManager.OP_NONE, null, true, false, ActivityManagerService.MY_PID,
+ Process.SYSTEM_UID, mUserId);
+ }
+
+ @Override
+ public void performReceive(Intent intent, int resultCode, String data, Bundle extras,
+ boolean ordered, boolean sticky, int sendingUser) {
+ sendNext();
+ }
+
+ public abstract void onFinished();
+}
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index b4aa4cf..93d4060 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -24,7 +24,8 @@
import android.util.DebugUtils;
import android.util.EventLog;
import android.util.Slog;
-import com.android.internal.app.ProcessStats;
+import com.android.internal.app.procstats.ProcessStats;
+import com.android.internal.app.procstats.ProcessState;
import com.android.internal.os.BatteryStatsImpl;
import android.app.ActivityManager;
@@ -69,7 +70,7 @@
IApplicationThread thread; // the actual proc... may be null only if
// 'persistent' is true (in which case we
// are in the process of launching the app)
- ProcessStats.ProcessState baseProcessTracker;
+ ProcessState baseProcessTracker;
BatteryStatsImpl.Uid.Proc curProcBatteryStats;
int pid; // The process of this application; 0 if none
int[] gids; // The gids this process was launched with
@@ -116,6 +117,7 @@
boolean killed; // True once we know the process has been killed
boolean procStateChanged; // Keep track of whether we changed 'setAdj'.
boolean reportedInteraction;// Whether we have told usage stats about it being an interaction
+ boolean unlocked; // True when proc was started in user unlocked state
long interactionEventTime; // The time we sent the last interaction event
long fgInteractionTime; // When we became foreground for interaction purposes
String waitingToKill; // Process is waiting to be killed when in the bg, and reason
@@ -443,7 +445,7 @@
public void makeActive(IApplicationThread _thread, ProcessStatsService tracker) {
if (thread == null) {
- final ProcessStats.ProcessState origBase = baseProcessTracker;
+ final ProcessState origBase = baseProcessTracker;
if (origBase != null) {
origBase.setState(ProcessStats.STATE_NOTHING,
tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), pkgList);
@@ -469,7 +471,7 @@
public void makeInactive(ProcessStatsService tracker) {
thread = null;
- final ProcessStats.ProcessState origBase = baseProcessTracker;
+ final ProcessState origBase = baseProcessTracker;
if (origBase != null) {
if (origBase != null) {
origBase.setState(ProcessStats.STATE_NOTHING,
@@ -558,7 +560,7 @@
}
EventLog.writeEvent(EventLogTags.AM_KILL, userId, pid, processName, setAdj, reason);
Process.killProcessQuiet(pid);
- Process.killProcessGroup(info.uid, pid);
+ ActivityManagerService.killProcessGroup(uid, pid);
if (!persistent) {
killed = true;
killedByAm = true;
@@ -695,7 +697,7 @@
}
pkgList.clear();
- ProcessStats.ProcessState ps = tracker.getProcessStateLocked(
+ ProcessState ps = tracker.getProcessStateLocked(
info.packageName, uid, info.versionCode, processName);
ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder(
info.versionCode);
diff --git a/services/core/java/com/android/server/am/ProcessStartLogger.java b/services/core/java/com/android/server/am/ProcessStartLogger.java
deleted file mode 100644
index 39fbeb5..0000000
--- a/services/core/java/com/android/server/am/ProcessStartLogger.java
+++ /dev/null
@@ -1,151 +0,0 @@
-package com.android.server.am;
-
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-
-import android.app.AppGlobals;
-import android.app.admin.SecurityLog;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.Process.ProcessStartResult;
-import android.util.Slog;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.HashMap;
-
-/**
- * A class that logs process start information (including APK hash) to the security log.
- */
-class ProcessStartLogger {
- private static final String CLASS_NAME = "ProcessStartLogger";
- private static final String TAG = TAG_WITH_CLASS_NAME ? CLASS_NAME : TAG_AM;
-
- final HandlerThread mHandlerProcessLoggingThread;
- Handler mHandlerProcessLogging;
- // Should only access in mHandlerProcessLoggingThread
- final HashMap<String, String> mProcessLoggingApkHashes;
-
- ProcessStartLogger() {
- mHandlerProcessLoggingThread = new HandlerThread(CLASS_NAME,
- Process.THREAD_PRIORITY_BACKGROUND);
- mProcessLoggingApkHashes = new HashMap();
- }
-
- void logIfNeededLocked(ProcessRecord app, ProcessStartResult startResult) {
- if (!SecurityLog.isLoggingEnabled()) {
- return;
- }
- if (!mHandlerProcessLoggingThread.isAlive()) {
- mHandlerProcessLoggingThread.start();
- mHandlerProcessLogging = new Handler(mHandlerProcessLoggingThread.getLooper());
- }
- mHandlerProcessLogging.post(new ProcessLoggingRunnable(app, startResult,
- System.currentTimeMillis()));
- }
-
- void registerListener(Context context) {
- IntentFilter packageChangedFilter = new IntentFilter();
- packageChangedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
- packageChangedFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
- context.registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)
- || Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())) {
- int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
- getSendingUserId());
- String packageName = intent.getData().getSchemeSpecificPart();
- try {
- ApplicationInfo info = AppGlobals.getPackageManager().getApplicationInfo(
- packageName, 0, userHandle);
- invaildateCache(info.sourceDir);
- } catch (RemoteException e) {
- }
- }
- }
- }, packageChangedFilter);
- }
-
- private void invaildateCache(final String apkPath) {
- if (mHandlerProcessLogging != null) {
- mHandlerProcessLogging.post(new Runnable() {
- @Override
- public void run() {
- mProcessLoggingApkHashes.remove(apkPath);
- }
- });
- }
- }
-
- private class ProcessLoggingRunnable implements Runnable {
-
- private final ProcessRecord app;
- private final Process.ProcessStartResult startResult;
- private final long startTimestamp;
-
- public ProcessLoggingRunnable(ProcessRecord app, Process.ProcessStartResult startResult,
- long startTimestamp){
- this.app = app;
- this.startResult = startResult;
- this.startTimestamp = startTimestamp;
- }
-
- @Override
- public void run() {
- String apkHash = computeStringHashOfApk(app);
- SecurityLog.writeEvent(SecurityLog.TAG_APP_PROCESS_START,
- app.processName,
- startTimestamp,
- app.uid,
- startResult.pid,
- app.info.seinfo,
- apkHash);
- }
-
- private String computeStringHashOfApk(ProcessRecord app){
- final String apkFile = app.info.sourceDir;
- if(apkFile == null) {
- return "No APK";
- }
- String apkHash = mProcessLoggingApkHashes.get(apkFile);
- if (apkHash == null) {
- try {
- byte[] hash = computeHashOfApkFile(apkFile);
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < hash.length; i++) {
- sb.append(String.format("%02x", hash[i]));
- }
- apkHash = sb.toString();
- mProcessLoggingApkHashes.put(apkFile, apkHash);
- } catch (IOException | NoSuchAlgorithmException e) {
- Slog.w(TAG, "computeStringHashOfApk() failed", e);
- }
- }
- return apkHash != null ? apkHash : "Failed to count APK hash";
- }
-
- private byte[] computeHashOfApkFile(String packageArchiveLocation)
- throws IOException, NoSuchAlgorithmException {
- MessageDigest md = MessageDigest.getInstance("SHA-256");
- FileInputStream input = new FileInputStream(new File(packageArchiveLocation));
- byte[] buffer = new byte[65536];
- int size;
- while((size = input.read(buffer)) > 0) {
- md.update(buffer, 0, size);
- }
- input.close();
- return md.digest();
- }
- }
-}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index 9634dff..8d2b1c2 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -28,8 +28,11 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
-import com.android.internal.app.IProcessStats;
-import com.android.internal.app.ProcessStats;
+import com.android.internal.app.procstats.DumpUtils;
+import com.android.internal.app.procstats.IProcessStats;
+import com.android.internal.app.procstats.ProcessState;
+import com.android.internal.app.procstats.ProcessStats;
+import com.android.internal.app.procstats.ServiceState;
import com.android.internal.os.BackgroundThread;
import java.io.File;
@@ -107,12 +110,12 @@
}
}
- public ProcessStats.ProcessState getProcessStateLocked(String packageName,
+ public ProcessState getProcessStateLocked(String packageName,
int uid, int versionCode, String processName) {
return mProcessStats.getProcessStateLocked(packageName, uid, versionCode, processName);
}
- public ProcessStats.ServiceState getServiceStateLocked(String packageName, int uid,
+ public ServiceState getServiceStateLocked(String packageName, int uid,
int versionCode, String processName, String className) {
return mProcessStats.getServiceStateLocked(packageName, uid, versionCode, processName,
className);
@@ -143,22 +146,10 @@
final SparseArray<ProcessStats.PackageState> vers = uids.valueAt(iuid);
for (int iver=vers.size()-1; iver>=0; iver--) {
final ProcessStats.PackageState pkg = vers.valueAt(iver);
- final ArrayMap<String, ProcessStats.ServiceState> services = pkg.mServices;
+ final ArrayMap<String, ServiceState> services = pkg.mServices;
for (int isvc=services.size()-1; isvc>=0; isvc--) {
- final ProcessStats.ServiceState service = services.valueAt(isvc);
- if (service.isRestarting()) {
- service.setRestarting(true, memFactor, now);
- } else if (service.isInUse()) {
- if (service.mStartedState != ProcessStats.STATE_NOTHING) {
- service.setStarted(true, memFactor, now);
- }
- if (service.mBoundState != ProcessStats.STATE_NOTHING) {
- service.setBound(true, memFactor, now);
- }
- if (service.mExecState != ProcessStats.STATE_NOTHING) {
- service.setExecuting(true, memFactor, now);
- }
- }
+ final ServiceState service = services.valueAt(isvc);
+ service.setMemFactor(memFactor, now);
}
}
}
@@ -294,12 +285,11 @@
if (stats.mReadError != null) {
Slog.w(TAG, "Ignoring existing stats; " + stats.mReadError);
if (DEBUG) {
- ArrayMap<String, SparseArray<ProcessStats.ProcessState>> procMap
- = stats.mProcesses.getMap();
+ ArrayMap<String, SparseArray<ProcessState>> procMap = stats.mProcesses.getMap();
final int NPROC = procMap.size();
for (int ip=0; ip<NPROC; ip++) {
Slog.w(TAG, "Process: " + procMap.keyAt(ip));
- SparseArray<ProcessStats.ProcessState> uids = procMap.valueAt(ip);
+ SparseArray<ProcessState> uids = procMap.valueAt(ip);
final int NUID = uids.size();
for (int iu=0; iu<NUID; iu++) {
Slog.w(TAG, " Uid " + uids.keyAt(iu) + ": " + uids.valueAt(iu));
@@ -387,13 +377,13 @@
boolean dumpFilteredProcessesCsvLocked(PrintWriter pw, String header,
boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates,
boolean sepProcStates, int[] procStates, long now, String reqPackage) {
- ArrayList<ProcessStats.ProcessState> procs = mProcessStats.collectProcessesLocked(
+ ArrayList<ProcessState> procs = mProcessStats.collectProcessesLocked(
screenStates, memStates, procStates, procStates, now, reqPackage, false);
if (procs.size() > 0) {
if (header != null) {
pw.println(header);
}
- ProcessStats.dumpProcessListCsv(pw, procs, sepScreenStates, screenStates,
+ DumpUtils.dumpProcessListCsv(pw, procs, sepScreenStates, screenStates,
sepMemStates, memStates, sepProcStates, procStates, now);
return true;
}
@@ -668,8 +658,8 @@
}
boolean[] sep = new boolean[1];
String[] error = new String[1];
- csvScreenStats = parseStateList(ProcessStats.ADJ_SCREEN_NAMES_CSV, ProcessStats.ADJ_SCREEN_MOD,
- args[i], sep, error);
+ csvScreenStats = parseStateList(DumpUtils.ADJ_SCREEN_NAMES_CSV,
+ ProcessStats.ADJ_SCREEN_MOD, args[i], sep, error);
if (csvScreenStats == null) {
pw.println("Error in \"" + args[i] + "\": " + error[0]);
dumpHelp(pw);
@@ -685,7 +675,8 @@
}
boolean[] sep = new boolean[1];
String[] error = new String[1];
- csvMemStats = parseStateList(ProcessStats.ADJ_MEM_NAMES_CSV, 1, args[i], sep, error);
+ csvMemStats = parseStateList(DumpUtils.ADJ_MEM_NAMES_CSV, 1, args[i],
+ sep, error);
if (csvMemStats == null) {
pw.println("Error in \"" + args[i] + "\": " + error[0]);
dumpHelp(pw);
@@ -701,7 +692,8 @@
}
boolean[] sep = new boolean[1];
String[] error = new String[1];
- csvProcStats = parseStateList(ProcessStats.STATE_NAMES_CSV, 1, args[i], sep, error);
+ csvProcStats = parseStateList(DumpUtils.STATE_NAMES_CSV, 1, args[i],
+ sep, error);
if (csvProcStats == null) {
pw.println("Error in \"" + args[i] + "\": " + error[0]);
dumpHelp(pw);
@@ -839,19 +831,19 @@
if (!csvSepScreenStats) {
for (int i=0; i<csvScreenStats.length; i++) {
pw.print(" ");
- ProcessStats.printScreenLabelCsv(pw, csvScreenStats[i]);
+ DumpUtils.printScreenLabelCsv(pw, csvScreenStats[i]);
}
}
if (!csvSepMemStats) {
for (int i=0; i<csvMemStats.length; i++) {
pw.print(" ");
- ProcessStats.printMemLabelCsv(pw, csvMemStats[i]);
+ DumpUtils.printMemLabelCsv(pw, csvMemStats[i]);
}
}
if (!csvSepProcStats) {
for (int i=0; i<csvProcStats.length; i++) {
pw.print(" ");
- pw.print(ProcessStats.STATE_NAMES_CSV[csvProcStats[i]]);
+ pw.print(DumpUtils.STATE_NAMES_CSV[csvProcStats[i]]);
}
}
pw.println();
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 87cb40e..5075c3a 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -16,7 +16,7 @@
package com.android.server.am;
-import com.android.internal.app.ProcessStats;
+import com.android.internal.app.procstats.ServiceState;
import com.android.internal.os.BatteryStatsImpl;
import com.android.server.LocalServices;
import com.android.server.notification.NotificationManagerInternal;
@@ -88,8 +88,8 @@
ProcessRecord app; // where this service is running or null.
ProcessRecord isolatedProc; // keep track of isolated process, if requested
- ProcessStats.ServiceState tracker; // tracking service execution, may be null
- ProcessStats.ServiceState restartTracker; // tracking service restart
+ ServiceState tracker; // tracking service execution, may be null
+ ServiceState restartTracker; // tracking service restart
boolean delayed; // are we waiting to start this service in the background?
boolean isForeground; // is service currently in foreground mode?
int foregroundId; // Notification ID of last foreground req.
@@ -326,7 +326,7 @@
createdFromFg = callerIsFg;
}
- public ProcessStats.ServiceState getTracker() {
+ public ServiceState getTracker() {
if (tracker != null) {
return tracker;
}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 4eae45c..e32d1d1 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -72,8 +72,6 @@
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
import static android.content.pm.ActivityInfo.RESIZE_MODE_CROP_WINDOWS;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ADD_REMOVE;
@@ -128,10 +126,14 @@
private static final String ATTR_RESIZE_MODE = "resize_mode";
private static final String ATTR_PRIVILEGED = "privileged";
private static final String ATTR_NON_FULLSCREEN_BOUNDS = "non_fullscreen_bounds";
+ private static final String ATTR_MINIMAL_WIDTH = "minimal_width";
+ private static final String ATTR_MINIMAL_HEIGHT = "minimal_height";
+
private static final String TASK_THUMBNAIL_SUFFIX = "_task_thumbnail";
static final int INVALID_TASK_ID = -1;
+ static final int INVALID_MINIMAL_SIZE = -1;
final int taskId; // Unique identifier for this task.
String affinity; // The affinity name for this task, or null; may change identity.
@@ -254,9 +256,10 @@
// The information is persisted and used to determine the appropriate stack to launch the
// task into on restore.
Rect mLastNonFullscreenBounds = null;
- // Minimal size for width/height of this task when it's resizeable. -1 means it should use the
- // default minimal size.
- final int mMinimalSize;
+ // Minimal width and height of this task when it's resizeable. -1 means it should use the
+ // default minimal width/height.
+ int mMinimalWidth;
+ int mMinimalHeight;
// Ranking (from top) of this task among all visible tasks. (-1 means it's not visible)
// This number will be assigned when we evaluate OOM scores for all visible tasks.
@@ -281,7 +284,8 @@
mCallingUid = info.applicationInfo.uid;
mCallingPackage = info.packageName;
setIntent(_intent, info);
- mMinimalSize = info != null && info.layout != null ? info.layout.minimalSize : -1;
+ setMinDimensions(info);
+ touchActiveTime();
}
TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent,
@@ -301,6 +305,7 @@
mCallingUid = info.applicationInfo.uid;
mCallingPackage = info.packageName;
setIntent(_intent, info);
+ setMinDimensions(info);
taskType = ActivityRecord.APPLICATION_ACTIVITY_TYPE;
isPersistable = true;
@@ -311,7 +316,7 @@
taskType = APPLICATION_ACTIVITY_TYPE;
mTaskToReturnTo = HOME_ACTIVITY_TYPE;
lastTaskDescription = _taskDescription;
- mMinimalSize = info != null && info.layout != null ? info.layout.minimalSize : -1;
+ touchActiveTime();
}
private TaskRecord(ActivityManagerService service, int _taskId, Intent _intent,
@@ -324,7 +329,7 @@
TaskThumbnailInfo lastThumbnailInfo, int taskAffiliation, int prevTaskId,
int nextTaskId, int taskAffiliationColor, int callingUid, String callingPackage,
int resizeMode, boolean privileged, boolean _realActivitySuspended,
- boolean userSetupComplete) {
+ boolean userSetupComplete, int minimalWidth, int minimalHeight) {
mService = service;
mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
TaskPersister.IMAGE_EXTENSION;
@@ -364,8 +369,8 @@
mCallingPackage = callingPackage;
mResizeMode = resizeMode;
mPrivileged = privileged;
- ActivityInfo info = (mActivities.size() > 0) ? mActivities.get(0).info : null;
- mMinimalSize = info != null && info.layout != null ? info.layout.minimalSize : -1;
+ mMinimalWidth = minimalWidth;
+ mMinimalHeight = minimalHeight;
}
void touchActiveTime() {
@@ -471,6 +476,17 @@
setLockTaskAuth();
}
+ /** Sets the original minimal width and height. */
+ private void setMinDimensions(ActivityInfo info) {
+ if (info != null && info.windowLayout != null) {
+ mMinimalWidth = info.windowLayout.minimalWidth;
+ mMinimalHeight = info.windowLayout.minimalHeight;
+ } else {
+ mMinimalWidth = INVALID_MINIMAL_SIZE;
+ mMinimalHeight = INVALID_MINIMAL_SIZE;
+ }
+ }
+
/**
* Return true if the input activity has the same intent resolution as the intent this task
* record is based on (normally the root activity intent).
@@ -1136,6 +1152,8 @@
out.attribute(
null, ATTR_NON_FULLSCREEN_BOUNDS, mLastNonFullscreenBounds.flattenToString());
}
+ out.attribute(null, ATTR_MINIMAL_WIDTH, String.valueOf(mMinimalWidth));
+ out.attribute(null, ATTR_MINIMAL_HEIGHT, String.valueOf(mMinimalHeight));
if (affinityIntent != null) {
out.startTag(null, TAG_AFFINITYINTENT);
@@ -1200,6 +1218,8 @@
int resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
boolean privileged = false;
Rect bounds = null;
+ int minimalWidth = INVALID_MINIMAL_SIZE;
+ int minimalHeight = INVALID_MINIMAL_SIZE;
for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) {
final String attrName = in.getAttributeName(attrNdx);
@@ -1267,6 +1287,10 @@
privileged = Boolean.valueOf(attrValue);
} else if (ATTR_NON_FULLSCREEN_BOUNDS.equals(attrName)) {
bounds = Rect.unflattenFromString(attrValue);
+ } else if (ATTR_MINIMAL_WIDTH.equals(attrName)) {
+ minimalWidth = Integer.valueOf(attrValue);
+ } else if (ATTR_MINIMAL_HEIGHT.equals(attrName)) {
+ minimalHeight = Integer.valueOf(attrValue);
} else {
Slog.w(TAG, "TaskRecord: Unknown attribute=" + attrName);
}
@@ -1327,7 +1351,7 @@
activities, firstActiveTime, lastActiveTime, lastTimeOnTop, neverRelinquishIdentity,
taskDescription, thumbnailInfo, taskAffiliation, prevTaskId, nextTaskId,
taskAffiliationColor, callingUid, callingPackage, resizeMode, privileged,
- realActivitySuspended, userSetupComplete);
+ realActivitySuspended, userSetupComplete, minimalWidth, minimalHeight);
task.updateOverrideConfiguration(bounds);
for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
@@ -1342,35 +1366,41 @@
if (bounds == null) {
return;
}
- int minimalSize = mMinimalSize;
+ int minimalWidth = mMinimalWidth;
+ int minimalHeight = mMinimalHeight;
// If the task has no requested minimal size, we'd like to enforce a minimal size
// so that the user can not render the task too small to manipulate. We don't need
// to do this for the pinned stack as the bounds are controlled by the system.
- if (minimalSize == -1 && stack.mStackId != PINNED_STACK_ID) {
- minimalSize = mService.mStackSupervisor.mDefaultMinimalSizeOfResizeableTask;
+ if (stack.mStackId != PINNED_STACK_ID) {
+ if (minimalWidth == INVALID_MINIMAL_SIZE) {
+ minimalWidth = mService.mStackSupervisor.mDefaultMinimalSizeOfResizeableTask;
+ }
+ if (minimalHeight == INVALID_MINIMAL_SIZE) {
+ minimalHeight = mService.mStackSupervisor.mDefaultMinimalSizeOfResizeableTask;
+ }
}
- final boolean adjustWidth = minimalSize > bounds.width();
- final boolean adjustHeight = minimalSize > bounds.height();
+ final boolean adjustWidth = minimalWidth > bounds.width();
+ final boolean adjustHeight = minimalHeight > bounds.height();
if (!(adjustWidth || adjustHeight)) {
return;
}
if (adjustWidth) {
if (mBounds != null && bounds.right == mBounds.right) {
- bounds.left = bounds.right - minimalSize;
+ bounds.left = bounds.right - minimalWidth;
} else {
// Either left bounds match, or neither match, or the previous bounds were
// fullscreen and we default to keeping left.
- bounds.right = bounds.left + minimalSize;
+ bounds.right = bounds.left + minimalWidth;
}
}
if (adjustHeight) {
if (mBounds != null && bounds.bottom == mBounds.bottom) {
- bounds.top = bounds.bottom - minimalSize;
+ bounds.top = bounds.bottom - minimalHeight;
} else {
// Either top bounds match, or neither match, or the previous bounds were
// fullscreen and we default to keeping top.
- bounds.bottom = bounds.top + minimalSize;
+ bounds.bottom = bounds.top + minimalHeight;
}
}
}
@@ -1421,7 +1451,7 @@
}
if (mFullscreen != oldFullscreen) {
- mService.mStackSupervisor.scheduleReportMultiWindowChanged(this);
+ mService.mStackSupervisor.scheduleReportMultiWindowModeChanged(this);
}
return !mOverrideConfig.equals(oldConfig) ? mOverrideConfig : null;
@@ -1502,6 +1532,9 @@
Rect updateOverrideConfigurationFromLaunchBounds() {
final Rect bounds = validateBounds(getLaunchBounds());
updateOverrideConfiguration(bounds);
+ if (bounds != null) {
+ bounds.set(mBounds);
+ }
return bounds;
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 5baba52..6e890d5 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -24,6 +24,7 @@
import static android.app.ActivityManager.USER_OP_SUCCESS;
import static android.content.Context.KEYGUARD_SERVICE;
import static android.os.Process.SYSTEM_UID;
+
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -37,6 +38,10 @@
import static com.android.server.am.ActivityManagerService.SYSTEM_USER_START_MSG;
import static com.android.server.am.ActivityManagerService.SYSTEM_USER_UNLOCK_MSG;
import static com.android.server.am.ActivityManagerService.USER_SWITCH_TIMEOUT_MSG;
+import static com.android.server.am.UserState.STATE_BOOTING;
+import static com.android.server.am.UserState.STATE_RUNNING_LOCKED;
+import static com.android.server.am.UserState.STATE_RUNNING_UNLOCKED;
+import static com.android.server.am.UserState.STATE_RUNNING_UNLOCKING;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
@@ -53,6 +58,7 @@
import android.content.pm.UserInfo;
import android.os.BatteryStats;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
@@ -77,6 +83,7 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.ProgressReporter;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.LocalServices;
import com.android.server.pm.UserManagerService;
@@ -86,6 +93,7 @@
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
/**
@@ -219,9 +227,7 @@
// consistent developer events. We step into RUNNING_LOCKED here,
// but we might immediately step into RUNNING below if the user
// storage is already unlocked.
- if (uss.state == UserState.STATE_BOOTING) {
- uss.setState(UserState.STATE_RUNNING_LOCKED);
-
+ if (uss.setState(STATE_BOOTING, STATE_RUNNING_LOCKED)) {
Intent intent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED, null);
intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
@@ -236,11 +242,10 @@
}
/**
- * Consider stepping from {@link UserState#STATE_RUNNING_LOCKED} into
- * {@link UserState#STATE_RUNNING}, which only occurs if the user storage is
- * actually unlocked.
+ * Step from {@link UserState#STATE_RUNNING_LOCKED} to
+ * {@link UserState#STATE_RUNNING_UNLOCKING}.
*/
- void finishUserUnlock(UserState uss) {
+ void finishUserUnlocking(final UserState uss, final ProgressReporter progress) {
final int userId = uss.mHandle.getIdentifier();
synchronized (mService) {
// Bail if we ended up with a stale user
@@ -249,14 +254,58 @@
// Only keep marching forward if user is actually unlocked
if (!isUserKeyUnlocked(userId)) return;
- if (uss.state == UserState.STATE_RUNNING_LOCKED) {
- uss.setState(UserState.STATE_RUNNING);
-
- // Give user manager a chance to prepare app storage
+ if (uss.setState(STATE_RUNNING_LOCKED, STATE_RUNNING_UNLOCKING)) {
+ // Prepare app storage before we go any further
+ progress.setProgress(5, mService.mContext.getString(R.string.android_start_title));
mUserManager.onBeforeUnlockUser(userId);
+ progress.setProgress(20);
+ // Dispatch unlocked to system services
mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0));
+ // Send PRE_BOOT broadcasts if fingerprint changed
+ final UserInfo info = getUserInfo(userId);
+ if (!Objects.equals(info.lastLoggedInFingerprint, Build.FINGERPRINT)) {
+ progress.startSegment(80);
+ new PreBootBroadcaster(mService, userId, progress) {
+ @Override
+ public void onFinished() {
+ finishUserUnlocked(uss, progress);
+ }
+ }.sendNext();
+ } else {
+ finishUserUnlocked(uss, progress);
+ }
+ }
+ }
+ }
+
+ /**
+ * Step from {@link UserState#STATE_RUNNING_UNLOCKING} to
+ * {@link UserState#STATE_RUNNING_UNLOCKED}.
+ */
+ void finishUserUnlocked(UserState uss, ProgressReporter progress) {
+ try {
+ finishUserUnlockedInternal(uss);
+ } finally {
+ progress.finish();
+ }
+ }
+
+ void finishUserUnlockedInternal(UserState uss) {
+ final int userId = uss.mHandle.getIdentifier();
+ synchronized (mService) {
+ // Bail if we ended up with a stale user
+ if (mStartedUsers.get(uss.mHandle.getIdentifier()) != uss) return;
+
+ // Only keep marching forward if user is actually unlocked
+ if (!isUserKeyUnlocked(userId)) return;
+
+ if (uss.setState(STATE_RUNNING_UNLOCKING, STATE_RUNNING_UNLOCKED)) {
+ // Remember that we logged in
+ mUserManager.onUserLoggedIn(userId);
+
+ // Dispatch unlocked to external apps
final Intent unlockedIntent = new Intent(Intent.ACTION_USER_UNLOCKED);
unlockedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
unlockedIntent.addFlags(
@@ -385,35 +434,17 @@
stoppingIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
stoppingIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
stoppingIntent.putExtra(Intent.EXTRA_SHUTDOWN_USERSPACE_ONLY, true);
- final Intent shutdownIntent = new Intent(Intent.ACTION_SHUTDOWN);
- // This is the result receiver for the final shutdown broadcast.
- final IIntentReceiver shutdownReceiver = new IIntentReceiver.Stub() {
- @Override
- public void performReceive(Intent intent, int resultCode, String data,
- Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
- finishUserStop(uss);
- }
- };
// This is the result receiver for the initial stopping broadcast.
final IIntentReceiver stoppingReceiver = new IIntentReceiver.Stub() {
@Override
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
- // On to the next.
- synchronized (mService) {
- if (uss.state != UserState.STATE_STOPPING) {
- // Whoops, we are being started back up. Abort, abort!
- return;
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ finishUserStopping(userId, uss);
}
- uss.setState(UserState.STATE_SHUTDOWN);
- }
- mService.mBatteryStatsService.noteEvent(
- BatteryStats.HistoryItem.EVENT_USER_RUNNING_FINISH,
- Integer.toString(userId), userId);
- mService.mSystemServiceManager.stopUser(userId);
- mService.broadcastIntentLocked(null, null, shutdownIntent,
- null, shutdownReceiver, 0, null, null, null, AppOpsManager.OP_NONE,
- null, true, false, MY_PID, SYSTEM_UID, userId);
+ });
}
};
// Kick things off.
@@ -427,7 +458,45 @@
}
}
- void finishUserStop(UserState uss) {
+ void finishUserStopping(final int userId, final UserState uss) {
+ // On to the next.
+ final Intent shutdownIntent = new Intent(Intent.ACTION_SHUTDOWN);
+ // This is the result receiver for the final shutdown broadcast.
+ final IIntentReceiver shutdownReceiver = new IIntentReceiver.Stub() {
+ @Override
+ public void performReceive(Intent intent, int resultCode, String data,
+ Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ finishUserStopped(uss);
+ }
+ });
+ }
+ };
+
+ synchronized (mService) {
+ if (uss.state != UserState.STATE_STOPPING) {
+ // Whoops, we are being started back up. Abort, abort!
+ return;
+ }
+ uss.setState(UserState.STATE_SHUTDOWN);
+ }
+
+ mService.mBatteryStatsService.noteEvent(
+ BatteryStats.HistoryItem.EVENT_USER_RUNNING_FINISH,
+ Integer.toString(userId), userId);
+ mService.mSystemServiceManager.stopUser(userId);
+
+ synchronized (mService) {
+ mService.broadcastIntentLocked(null, null, shutdownIntent,
+ null, shutdownReceiver, 0, null, null, null,
+ AppOpsManager.OP_NONE,
+ null, true, false, MY_PID, SYSTEM_UID, userId);
+ }
+ }
+
+ void finishUserStopped(UserState uss) {
final int userId = uss.mHandle.getIdentifier();
boolean stopped;
ArrayList<IStopUserCallback> callbacks;
@@ -716,10 +785,17 @@
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mService.broadcastIntentLocked(null, null, intent, null,
new IIntentReceiver.Stub() {
+ @Override
public void performReceive(Intent intent, int resultCode,
String data, Bundle extras, boolean ordered,
boolean sticky, int sendingUser) {
- onUserInitialized(uss, foreground, oldUserId, userId);
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ onUserInitialized(uss, foreground,
+ oldUserId, userId);
+ }
+ });
}
}, 0, null, null, null, AppOpsManager.OP_NONE,
null, true, false, MY_PID, SYSTEM_UID, userId);
@@ -769,7 +845,7 @@
return result;
}
- boolean unlockUser(final int userId, byte[] token, byte[] secret) {
+ boolean unlockUser(final int userId, byte[] token, byte[] secret, ProgressReporter progress) {
if (mService.checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
!= PackageManager.PERMISSION_GRANTED) {
String msg = "Permission Denial: unlockUser() from pid="
@@ -782,7 +858,7 @@
final long binderToken = Binder.clearCallingIdentity();
try {
- return unlockUserCleared(userId, token, secret);
+ return unlockUserCleared(userId, token, secret, progress);
} finally {
Binder.restoreCallingIdentity(binderToken);
}
@@ -796,14 +872,20 @@
*/
boolean maybeUnlockUser(final int userId) {
// Try unlocking storage using empty token
- return unlockUserCleared(userId, null, null);
+ return unlockUserCleared(userId, null, null, ProgressReporter.NO_OP);
}
- boolean unlockUserCleared(final int userId, byte[] token, byte[] secret) {
+ boolean unlockUserCleared(final int userId, byte[] token, byte[] secret,
+ ProgressReporter progress) {
synchronized (mService) {
// Bail if already running unlocked
final UserState uss = mStartedUsers.get(userId);
- if (uss.state == UserState.STATE_RUNNING) return true;
+ switch (uss.state) {
+ case STATE_RUNNING_UNLOCKING:
+ case STATE_RUNNING_UNLOCKED:
+ progress.finish();
+ return true;
+ }
}
if (!isUserKeyUnlocked(userId)) {
@@ -813,13 +895,14 @@
mountService.unlockUserKey(userId, userInfo.serialNumber, token, secret);
} catch (RemoteException | RuntimeException e) {
Slog.w(TAG, "Failed to unlock: " + e.getMessage());
+ progress.finish();
return false;
}
}
synchronized (mService) {
final UserState uss = mStartedUsers.get(userId);
- finishUserUnlock(uss);
+ finishUserUnlocking(uss, progress);
}
return true;
@@ -971,7 +1054,6 @@
mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
EventLogTags.writeAmSwitchUser(newUserId);
- getUserManager().onUserForeground(newUserId);
sendUserSwitchBroadcastsLocked(oldUserId, newUserId);
}
@@ -1219,7 +1301,8 @@
unlocked = false;
break;
- case UserState.STATE_RUNNING:
+ case UserState.STATE_RUNNING_UNLOCKING:
+ case UserState.STATE_RUNNING_UNLOCKED:
unlocked = true;
break;
}
diff --git a/services/core/java/com/android/server/am/UserState.java b/services/core/java/com/android/server/am/UserState.java
index 7b18a17..6e2342b 100644
--- a/services/core/java/com/android/server/am/UserState.java
+++ b/services/core/java/com/android/server/am/UserState.java
@@ -33,14 +33,16 @@
// User is first coming up.
public final static int STATE_BOOTING = 0;
- // User is in the locked running state.
+ // User is in the locked state.
public final static int STATE_RUNNING_LOCKED = 1;
- // User is in the normal running state.
- public final static int STATE_RUNNING = 2;
+ // User is in the unlocking state.
+ public final static int STATE_RUNNING_UNLOCKING = 2;
+ // User is in the running state.
+ public final static int STATE_RUNNING_UNLOCKED = 3;
// User is in the initial process of being stopped.
- public final static int STATE_STOPPING = 3;
+ public final static int STATE_STOPPING = 4;
// User is in the final phase of stopping, sending Intent.ACTION_SHUTDOWN.
- public final static int STATE_SHUTDOWN = 4;
+ public final static int STATE_SHUTDOWN = 5;
public final UserHandle mHandle;
public final ArrayList<IStopUserCallback> mStopCallbacks
@@ -61,6 +63,17 @@
mHandle = handle;
}
+ public boolean setState(int oldState, int newState) {
+ if (state == oldState) {
+ setState(newState);
+ return true;
+ } else {
+ Slog.w(TAG, "Expected user " + mHandle.getIdentifier() + " in state "
+ + stateToString(oldState) + " but was in state " + stateToString(state));
+ return false;
+ }
+ }
+
public void setState(int newState) {
if (DEBUG_MU) {
Slog.i(TAG, "User " + mHandle.getIdentifier() + " state changed from "
@@ -74,7 +87,8 @@
switch (state) {
case STATE_BOOTING: return "BOOTING";
case STATE_RUNNING_LOCKED: return "RUNNING_LOCKED";
- case STATE_RUNNING: return "RUNNING";
+ case STATE_RUNNING_UNLOCKING: return "RUNNING_UNLOCKING";
+ case STATE_RUNNING_UNLOCKED: return "RUNNING_UNLOCKED";
case STATE_STOPPING: return "STOPPING";
case STATE_SHUTDOWN: return "SHUTDOWN";
default: return Integer.toString(state);
@@ -84,7 +98,6 @@
void dump(String prefix, PrintWriter pw) {
pw.print(prefix);
pw.print("state="); pw.print(stateToString(state));
- pw.print(" lastState="); pw.print(stateToString(lastState));
if (switching) pw.print(" SWITCHING");
if (initializing) pw.print(" INITIALIZING");
pw.println();
diff --git a/services/core/java/com/android/server/backup/BackupUtils.java b/services/core/java/com/android/server/backup/BackupUtils.java
new file mode 100644
index 0000000..e5d564d
--- /dev/null
+++ b/services/core/java/com/android/server/backup/BackupUtils.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.Signature;
+import android.util.Slog;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class BackupUtils {
+ private static final String TAG = "BackupUtils";
+
+ private static final boolean DEBUG = false; // STOPSHIP if true
+
+ public static boolean signaturesMatch(ArrayList<byte[]> storedSigHashes, PackageInfo target) {
+ if (target == null) {
+ return false;
+ }
+
+ // If the target resides on the system partition, we allow it to restore
+ // data from the like-named package in a restore set even if the signatures
+ // do not match. (Unlike general applications, those flashed to the system
+ // partition will be signed with the device's platform certificate, so on
+ // different phones the same system app will have different signatures.)
+ if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ if (DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check");
+ return true;
+ }
+
+ // Allow unsigned apps, but not signed on one device and unsigned on the other
+ // !!! TODO: is this the right policy?
+ Signature[] deviceSigs = target.signatures;
+ if (DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigHashes
+ + " device=" + deviceSigs);
+ if ((storedSigHashes == null || storedSigHashes.size() == 0)
+ && (deviceSigs == null || deviceSigs.length == 0)) {
+ return true;
+ }
+ if (storedSigHashes == null || deviceSigs == null) {
+ return false;
+ }
+
+ // !!! TODO: this demands that every stored signature match one
+ // that is present on device, and does not demand the converse.
+ // Is this this right policy?
+ final int nStored = storedSigHashes.size();
+ final int nDevice = deviceSigs.length;
+
+ // hash each on-device signature
+ ArrayList<byte[]> deviceHashes = new ArrayList<byte[]>(nDevice);
+ for (int i = 0; i < nDevice; i++) {
+ deviceHashes.add(hashSignature(deviceSigs[i]));
+ }
+
+ // now ensure that each stored sig (hash) matches an on-device sig (hash)
+ for (int n = 0; n < nStored; n++) {
+ boolean match = false;
+ final byte[] storedHash = storedSigHashes.get(n);
+ for (int i = 0; i < nDevice; i++) {
+ if (Arrays.equals(storedHash, deviceHashes.get(i))) {
+ match = true;
+ break;
+ }
+ }
+ // match is false when no on-device sig matched one of the stored ones
+ if (!match) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public static byte[] hashSignature(byte[] signature) {
+ try {
+ MessageDigest digest = MessageDigest.getInstance("SHA-256");
+ digest.update(signature);
+ return digest.digest();
+ } catch (NoSuchAlgorithmException e) {
+ Slog.w(TAG, "No SHA-256 algorithm found!");
+ }
+ return null;
+ }
+
+ public static byte[] hashSignature(Signature signature) {
+ return hashSignature(signature.toByteArray());
+ }
+
+ public static ArrayList<byte[]> hashSignatureArray(Signature[] sigs) {
+ if (sigs == null) {
+ return null;
+ }
+
+ ArrayList<byte[]> hashes = new ArrayList<>(sigs.length);
+ for (Signature s : sigs) {
+ hashes.add(hashSignature(s));
+ }
+ return hashes;
+ }
+
+ public static ArrayList<byte[]> hashSignatureArray(List<byte[]> sigs) {
+ if (sigs == null) {
+ return null;
+ }
+
+ ArrayList<byte[]> hashes = new ArrayList<>(sigs.size());
+ for (byte[] s : sigs) {
+ hashes.add(hashSignature(s));
+ }
+ return hashes;
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/ApfFilter.java b/services/core/java/com/android/server/connectivity/ApfFilter.java
deleted file mode 100644
index 824db65..0000000
--- a/services/core/java/com/android/server/connectivity/ApfFilter.java
+++ /dev/null
@@ -1,650 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.connectivity;
-
-import static android.system.OsConstants.*;
-
-import android.net.NetworkUtils;
-import android.net.apf.ApfGenerator;
-import android.net.apf.ApfGenerator.IllegalInstructionException;
-import android.net.apf.ApfGenerator.Register;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.system.PacketSocketAddress;
-import android.util.Log;
-import android.util.Pair;
-
-import com.android.internal.util.HexDump;
-import com.android.internal.util.IndentingPrintWriter;
-import com.android.server.ConnectivityService;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.lang.Thread;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.NetworkInterface;
-import java.net.SocketException;
-import java.net.UnknownHostException;
-import java.nio.ByteBuffer;
-import java.nio.BufferUnderflowException;
-import java.util.ArrayList;
-import java.util.Arrays;
-
-import libcore.io.IoBridge;
-
-/**
- * For networks that support packet filtering via APF programs, {@code ApfFilter}
- * listens for IPv6 ICMPv6 router advertisements (RAs) and generates APF programs to
- * filter out redundant duplicate ones.
- *
- * @hide
- */
-public class ApfFilter {
- // Thread to listen for RAs.
- private class ReceiveThread extends Thread {
- private final byte[] mPacket = new byte[1514];
- private final FileDescriptor mSocket;
- private volatile boolean mStopped;
-
- public ReceiveThread(FileDescriptor socket) {
- mSocket = socket;
- }
-
- public void halt() {
- mStopped = true;
- try {
- // Interrupts the read() call the thread is blocked in.
- IoBridge.closeAndSignalBlockedThreads(mSocket);
- } catch (IOException ignored) {}
- }
-
- @Override
- public void run() {
- log("begin monitoring");
- while (!mStopped) {
- try {
- int length = Os.read(mSocket, mPacket, 0, mPacket.length);
- processRa(mPacket, length);
- } catch (IOException|ErrnoException e) {
- if (!mStopped) {
- Log.e(TAG, "Read error", e);
- }
- }
- }
- }
- }
-
- private static final String TAG = "ApfFilter";
- private static final boolean DBG = true;
- private static final boolean VDBG = false;
-
- private final ConnectivityService mConnectivityService;
- private final NetworkAgentInfo mNai;
- private ReceiveThread mReceiveThread;
- private String mIfaceName;
- private long mUniqueCounter;
-
- private ApfFilter(ConnectivityService connectivityService, NetworkAgentInfo nai) {
- mConnectivityService = connectivityService;
- mNai = nai;
- maybeStartFilter();
- }
-
- private void log(String s) {
- Log.d(TAG, "(" + mNai.network.netId + "): " + s);
- }
-
- private long getUniqueNumber() {
- return mUniqueCounter++;
- }
-
- /**
- * Attempt to start listening for RAs and, if RAs are received, generating and installing
- * filters to ignore useless RAs.
- */
- private void maybeStartFilter() {
- mIfaceName = mNai.linkProperties.getInterfaceName();
- if (mIfaceName == null) return;
- FileDescriptor socket;
- try {
- socket = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IPV6);
- PacketSocketAddress addr = new PacketSocketAddress((short) ETH_P_IPV6,
- NetworkInterface.getByName(mIfaceName).getIndex());
- Os.bind(socket, addr);
- NetworkUtils.attachRaFilter(socket, mNai.networkMisc.apfPacketFormat);
- } catch(SocketException|ErrnoException e) {
- Log.e(TAG, "Error filtering raw socket", e);
- return;
- }
- mReceiveThread = new ReceiveThread(socket);
- mReceiveThread.start();
- }
-
- /**
- * mNai's LinkProperties may have changed, take appropriate action.
- */
- public void updateFilter() {
- // If we're not listening for RAs, try starting.
- if (mReceiveThread == null) {
- maybeStartFilter();
- // If interface name has changed, restart.
- } else if (!mIfaceName.equals(mNai.linkProperties.getInterfaceName())) {
- shutdown();
- maybeStartFilter();
- }
- }
-
- // Returns seconds since Unix Epoch.
- private static long curTime() {
- return System.currentTimeMillis() / 1000L;
- }
-
- // A class to hold information about an RA.
- private class Ra {
- private static final int ETH_HEADER_LEN = 14;
-
- private static final int IPV6_HEADER_LEN = 40;
- private static final int IPV6_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 8;
- private static final int IPV6_DST_ADDR_OFFSET = ETH_HEADER_LEN + 24;
-
- // From RFC4861:
- private static final int ICMP6_RA_HEADER_LEN = 16;
- private static final int ICMP6_RA_CHECKSUM_OFFSET =
- ETH_HEADER_LEN + IPV6_HEADER_LEN + 2;
- private static final int ICMP6_RA_CHECKSUM_LEN = 2;
- private static final int ICMP6_RA_OPTION_OFFSET =
- ETH_HEADER_LEN + IPV6_HEADER_LEN + ICMP6_RA_HEADER_LEN;
- private static final int ICMP6_RA_ROUTER_LIFETIME_OFFSET =
- ETH_HEADER_LEN + IPV6_HEADER_LEN + 6;
- private static final int ICMP6_RA_ROUTER_LIFETIME_LEN = 2;
- // Prefix information option.
- private static final int ICMP6_PREFIX_OPTION_TYPE = 3;
- private static final int ICMP6_PREFIX_OPTION_LEN = 32;
- private static final int ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET = 4;
- private static final int ICMP6_PREFIX_OPTION_VALID_LIFETIME_LEN = 4;
- private static final int ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET = 8;
- private static final int ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_LEN = 4;
-
- // From RFC6106: Recursive DNS Server option
- private static final int ICMP6_RDNSS_OPTION_TYPE = 25;
- // From RFC6106: DNS Search List option
- private static final int ICMP6_DNSSL_OPTION_TYPE = 31;
-
- // From RFC4191: Route Information option
- private static final int ICMP6_ROUTE_INFO_OPTION_TYPE = 24;
- // Above three options all have the same format:
- private static final int ICMP6_4_BYTE_LIFETIME_OFFSET = 4;
- private static final int ICMP6_4_BYTE_LIFETIME_LEN = 4;
-
- private final ByteBuffer mPacket;
- // List of binary ranges that include the whole packet except the lifetimes.
- // Pairs consist of offset and length.
- private final ArrayList<Pair<Integer, Integer>> mNonLifetimes =
- new ArrayList<Pair<Integer, Integer>>();
- // Minimum lifetime in packet
- long mMinLifetime;
- // When the packet was last captured, in seconds since Unix Epoch
- long mLastSeen;
-
- // For debugging only. Offsets into the packet where PIOs are.
- private final ArrayList<Integer> mPrefixOptionOffsets;
- // For debugging only. How many times this RA was seen.
- int seenCount = 0;
-
- // For debugging only. Returns the hex representation of the last matching packet.
- String getLastMatchingPacket() {
- return HexDump.toHexString(mPacket.array(), 0, mPacket.capacity(), false /* lowercase */);
- }
-
- private String IPv6AddresstoString(int pos) {
- try {
- byte[] array = mPacket.array();
- // Can't just call copyOfRange() and see if it throws, because if it reads past the
- // end it pads with zeros instead of throwing.
- if (pos < 0 || pos + 16 > array.length || pos + 16 < pos) {
- return "???";
- }
- byte[] addressBytes = Arrays.copyOfRange(array, pos, pos + 16);
- InetAddress address = (Inet6Address) InetAddress.getByAddress(addressBytes);
- return address.getHostAddress();
- } catch (UnsupportedOperationException e) {
- // array() failed. Cannot happen, mPacket is array-backed and read-write.
- return "???";
- } catch (ClassCastException | UnknownHostException e) {
- // Cannot happen.
- return "???";
- }
- }
-
- // Can't be static because it's in a non-static inner class.
- // TODO: Make this final once RA is its own class.
- private int uint8(byte b) {
- return b & 0xff;
- }
-
- private int uint16(short s) {
- return s & 0xffff;
- }
-
- private long uint32(int s) {
- return s & 0xffffffff;
- }
-
- public String toString() {
- try {
- StringBuffer sb = new StringBuffer();
- sb.append(String.format("RA %s -> %s %d ",
- IPv6AddresstoString(IPV6_SRC_ADDR_OFFSET),
- IPv6AddresstoString(IPV6_DST_ADDR_OFFSET),
- uint16(mPacket.getShort(ICMP6_RA_ROUTER_LIFETIME_OFFSET))));
- for (int i: mPrefixOptionOffsets) {
- String prefix = IPv6AddresstoString(i + 16);
- int length = uint8(mPacket.get(i + 2));
- long valid = mPacket.getInt(i + 4);
- long preferred = mPacket.getInt(i + 8);
- sb.append(String.format("%s/%d %d/%d ", prefix, length, valid, preferred));
- }
- return sb.toString();
- } catch (BufferUnderflowException | IndexOutOfBoundsException e) {
- return "<Malformed RA>";
- }
- }
-
- /**
- * Add a binary range of the packet that does not include a lifetime to mNonLifetimes.
- * Assumes mPacket.position() is as far as we've parsed the packet.
- * @param lastNonLifetimeStart offset within packet of where the last binary range of
- * data not including a lifetime.
- * @param lifetimeOffset offset from mPacket.position() to the next lifetime data.
- * @param lifetimeLength length of the next lifetime data.
- * @return offset within packet of where the next binary range of data not including
- * a lifetime. This can be passed into the next invocation of this function
- * via {@code lastNonLifetimeStart}.
- */
- private int addNonLifetime(int lastNonLifetimeStart, int lifetimeOffset,
- int lifetimeLength) {
- lifetimeOffset += mPacket.position();
- mNonLifetimes.add(new Pair<Integer, Integer>(lastNonLifetimeStart,
- lifetimeOffset - lastNonLifetimeStart));
- return lifetimeOffset + lifetimeLength;
- }
-
- // Note that this parses RA and may throw IllegalArgumentException (from
- // Buffer.position(int) ) or IndexOutOfBoundsException (from ByteBuffer.get(int) ) if
- // parsing encounters something non-compliant with specifications.
- Ra(byte[] packet, int length) {
- mPacket = ByteBuffer.allocate(length).put(ByteBuffer.wrap(packet, 0, length));
- mPacket.clear();
- mLastSeen = curTime();
-
- // Ignore the checksum.
- int lastNonLifetimeStart = addNonLifetime(0,
- ICMP6_RA_CHECKSUM_OFFSET,
- ICMP6_RA_CHECKSUM_LEN);
-
- // Parse router lifetime
- lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
- ICMP6_RA_ROUTER_LIFETIME_OFFSET,
- ICMP6_RA_ROUTER_LIFETIME_LEN);
-
- // Parse ICMP6 options
- mPrefixOptionOffsets = new ArrayList<>();
- mPacket.position(ICMP6_RA_OPTION_OFFSET);
- while (mPacket.hasRemaining()) {
- int optionType = ((int)mPacket.get(mPacket.position())) & 0xff;
- int optionLength = (((int)mPacket.get(mPacket.position() + 1)) & 0xff) * 8;
- switch (optionType) {
- case ICMP6_PREFIX_OPTION_TYPE:
- // Parse valid lifetime
- lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
- ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET,
- ICMP6_PREFIX_OPTION_VALID_LIFETIME_LEN);
- // Parse preferred lifetime
- lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
- ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET,
- ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_LEN);
- mPrefixOptionOffsets.add(mPacket.position());
- break;
- // These three options have the same lifetime offset and size, so process
- // together:
- case ICMP6_ROUTE_INFO_OPTION_TYPE:
- case ICMP6_RDNSS_OPTION_TYPE:
- case ICMP6_DNSSL_OPTION_TYPE:
- // Parse lifetime
- lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
- ICMP6_4_BYTE_LIFETIME_OFFSET,
- ICMP6_4_BYTE_LIFETIME_LEN);
- break;
- default:
- // RFC4861 section 4.2 dictates we ignore unknown options for fowards
- // compatibility.
- break;
- }
- mPacket.position(mPacket.position() + optionLength);
- }
- // Mark non-lifetime bytes since last lifetime.
- addNonLifetime(lastNonLifetimeStart, 0, 0);
- mMinLifetime = minLifetime(packet, length);
- }
-
- // Ignoring lifetimes (which may change) does {@code packet} match this RA?
- boolean matches(byte[] packet, int length) {
- if (length != mPacket.limit()) return false;
- ByteBuffer a = ByteBuffer.wrap(packet);
- ByteBuffer b = mPacket;
- for (Pair<Integer, Integer> nonLifetime : mNonLifetimes) {
- a.clear();
- b.clear();
- a.position(nonLifetime.first);
- b.position(nonLifetime.first);
- a.limit(nonLifetime.first + nonLifetime.second);
- b.limit(nonLifetime.first + nonLifetime.second);
- if (a.compareTo(b) != 0) return false;
- }
- return true;
- }
-
- // What is the minimum of all lifetimes within {@code packet} in seconds?
- // Precondition: matches(packet, length) already returned true.
- long minLifetime(byte[] packet, int length) {
- long minLifetime = Long.MAX_VALUE;
- // Wrap packet in ByteBuffer so we can read big-endian values easily
- ByteBuffer byteBuffer = ByteBuffer.wrap(packet);
- for (int i = 0; (i + 1) < mNonLifetimes.size(); i++) {
- int offset = mNonLifetimes.get(i).first + mNonLifetimes.get(i).second;
-
- // The checksum is in mNonLifetimes, but it's not a lifetime.
- if (offset == ICMP6_RA_CHECKSUM_OFFSET) {
- continue;
- }
-
- int lifetimeLength = mNonLifetimes.get(i+1).first - offset;
- long val;
- switch (lifetimeLength) {
- case 2: val = byteBuffer.getShort(offset); break;
- case 4: val = byteBuffer.getInt(offset); break;
- default: throw new IllegalStateException("bogus lifetime size " + length);
- }
- // Mask to size, converting signed to unsigned
- val &= (1L << (lifetimeLength * 8)) - 1;
- minLifetime = Math.min(minLifetime, val);
- }
- return minLifetime;
- }
-
- // How many seconds does this RA's have to live, taking into account the fact
- // that we might have seen it a while ago.
- long currentLifetime() {
- return mMinLifetime - (curTime() - mLastSeen);
- }
-
- boolean isExpired() {
- return currentLifetime() < 0;
- }
-
- // Append a filter for this RA to {@code gen}. Jump to DROP_LABEL if it should be dropped.
- // Jump to the next filter if packet doesn't match this RA.
- long generateFilter(ApfGenerator gen) throws IllegalInstructionException {
- String nextFilterLabel = "Ra" + getUniqueNumber();
- // Skip if packet is not the right size
- gen.addLoadFromMemory(Register.R0, gen.PACKET_SIZE_MEMORY_SLOT);
- gen.addJumpIfR0NotEquals(mPacket.limit(), nextFilterLabel);
- int filterLifetime = (int)(currentLifetime() / FRACTION_OF_LIFETIME_TO_FILTER);
- // Skip filter if expired
- gen.addLoadFromMemory(Register.R0, gen.FILTER_AGE_MEMORY_SLOT);
- gen.addJumpIfR0GreaterThan(filterLifetime, nextFilterLabel);
- for (int i = 0; i < mNonLifetimes.size(); i++) {
- // Generate code to match the packet bytes
- Pair<Integer, Integer> nonLifetime = mNonLifetimes.get(i);
- // Don't generate JNEBS instruction for 0 bytes as it always fails the
- // ASSERT_FORWARD_IN_PROGRAM(pc + cmp_imm - 1) check where cmp_imm is
- // the number of bytes to compare. nonLifetime is zero between the
- // valid and preferred lifetimes in the prefix option.
- if (nonLifetime.second != 0) {
- gen.addLoadImmediate(Register.R0, nonLifetime.first);
- gen.addJumpIfBytesNotEqual(Register.R0,
- Arrays.copyOfRange(mPacket.array(), nonLifetime.first,
- nonLifetime.first + nonLifetime.second),
- nextFilterLabel);
- }
- // Generate code to test the lifetimes haven't gone down too far
- if ((i + 1) < mNonLifetimes.size()) {
- Pair<Integer, Integer> nextNonLifetime = mNonLifetimes.get(i + 1);
- int offset = nonLifetime.first + nonLifetime.second;
- // Skip the checksum.
- if (offset == ICMP6_RA_CHECKSUM_OFFSET) {
- continue;
- }
- int length = nextNonLifetime.first - offset;
- switch (length) {
- case 4: gen.addLoad32(Register.R0, offset); break;
- case 2: gen.addLoad16(Register.R0, offset); break;
- default: throw new IllegalStateException("bogus lifetime size " + length);
- }
- gen.addJumpIfR0LessThan(filterLifetime, nextFilterLabel);
- }
- }
- gen.addJump(gen.DROP_LABEL);
- gen.defineLabel(nextFilterLabel);
- return filterLifetime;
- }
- }
-
- // Maximum number of RAs to filter for.
- private static final int MAX_RAS = 10;
- private ArrayList<Ra> mRas = new ArrayList<Ra>();
-
- // There is always some marginal benefit to updating the installed APF program when an RA is
- // seen because we can extend the program's lifetime slightly, but there is some cost to
- // updating the program, so don't bother unless the program is going to expire soon. This
- // constant defines "soon" in seconds.
- private static final long MAX_PROGRAM_LIFETIME_WORTH_REFRESHING = 30;
- // We don't want to filter an RA for it's whole lifetime as it'll be expired by the time we ever
- // see a refresh. Using half the lifetime might be a good idea except for the fact that
- // packets may be dropped, so let's use 6.
- private static final int FRACTION_OF_LIFETIME_TO_FILTER = 6;
-
- // When did we last install a filter program? In seconds since Unix Epoch.
- private long mLastTimeInstalledProgram;
- // How long should the last installed filter program live for? In seconds.
- private long mLastInstalledProgramMinLifetime;
-
- // For debugging only. The length in bytes of the last program.
- private byte[] mLastInstalledProgram;
-
- private void installNewProgram() {
- if (mRas.size() == 0) return;
- final byte[] program;
- long programMinLifetime = Long.MAX_VALUE;
- try {
- ApfGenerator gen = new ApfGenerator();
- // This is guaranteed to return true because of the check in maybeInstall.
- gen.setApfVersion(mNai.networkMisc.apfVersionSupported);
- // Step 1: Determine how many RA filters we can fit in the program.
- int ras = 0;
- for (Ra ra : mRas) {
- if (ra.isExpired()) continue;
- ra.generateFilter(gen);
- if (gen.programLengthOverEstimate() > mNai.networkMisc.maximumApfProgramSize) {
- // We went too far. Use prior number of RAs in "ras".
- break;
- } else {
- // Yay! this RA filter fits, increment "ras".
- ras++;
- }
- }
- // Step 2: Generate RA filters
- gen = new ApfGenerator();
- // This is guaranteed to return true because of the check in maybeInstall.
- gen.setApfVersion(mNai.networkMisc.apfVersionSupported);
- for (Ra ra : mRas) {
- if (ras-- == 0) break;
- if (ra.isExpired()) continue;
- programMinLifetime = Math.min(programMinLifetime, ra.generateFilter(gen));
- }
- // Execution will reach the end of the program if no filters match, which will pass the
- // packet to the AP.
- program = gen.generate();
- } catch (IllegalInstructionException e) {
- Log.e(TAG, "Program failed to generate: ", e);
- return;
- }
- mLastTimeInstalledProgram = curTime();
- mLastInstalledProgramMinLifetime = programMinLifetime;
- mLastInstalledProgram = program;
- if (VDBG) {
- hexDump("Installing filter: ", program, program.length);
- } else {
- Log.d(TAG, "Installing filter length=" + program.length);
- }
- mConnectivityService.pushApfProgramToNetwork(mNai, program);
- }
-
- // Install a new filter program if the last installed one will die soon.
- private void maybeInstallNewProgram() {
- if (mRas.size() == 0) return;
- // If the current program doesn't expire for a while, don't bother updating.
- long expiry = mLastTimeInstalledProgram + mLastInstalledProgramMinLifetime;
- if (expiry < curTime() + MAX_PROGRAM_LIFETIME_WORTH_REFRESHING) {
- installNewProgram();
- }
- }
-
- private void hexDump(String msg, byte[] packet, int length) {
- log(msg + HexDump.toHexString(packet, 0, length, false /* lowercase */));
- }
-
- private void processRa(byte[] packet, int length) {
- if (VDBG) hexDump("Read packet = ", packet, length);
-
- // Have we seen this RA before?
- for (int i = 0; i < mRas.size(); i++) {
- Ra ra = mRas.get(i);
- if (ra.matches(packet, length)) {
- if (VDBG) log("matched RA " + ra);
- // Update lifetimes.
- ra.mLastSeen = curTime();
- ra.mMinLifetime = ra.minLifetime(packet, length);
- ra.seenCount++;
-
- // Keep mRas in LRU order so as to prioritize generating filters for recently seen
- // RAs. LRU prioritizes this because RA filters are generated in order from mRas
- // until the filter program exceeds the maximum filter program size allowed by the
- // chipset, so RAs appearing earlier in mRas are more likely to make it into the
- // filter program.
- // TODO: consider sorting the RAs in order of increasing expiry time as well.
- // Swap to front of array.
- mRas.add(0, mRas.remove(i));
-
- maybeInstallNewProgram();
- return;
- }
- }
- // Purge expired RAs.
- for (int i = 0; i < mRas.size();) {
- if (mRas.get(i).isExpired()) {
- log("Expired RA " + mRas.get(i));
- mRas.remove(i);
- } else {
- i++;
- }
- }
- // TODO: figure out how to proceed when we've received more then MAX_RAS RAs.
- if (mRas.size() >= MAX_RAS) return;
- try {
- Ra ra = new Ra(packet, length);
- log("Adding " + ra);
- mRas.add(ra);
- } catch (Exception e) {
- Log.e(TAG, "Error parsing RA: " + e);
- return;
- }
- installNewProgram();
- }
-
- /**
- * Install an {@link ApfFilter} on {@code nai} if {@code nai} supports packet
- * filtering using APF programs.
- */
- public static void maybeInstall(ConnectivityService connectivityService, NetworkAgentInfo nai) {
- if (nai.networkMisc == null) return;
- if (nai.networkMisc.apfVersionSupported == 0) return;
- if (nai.networkMisc.maximumApfProgramSize < 512) {
- Log.e(TAG, "Unacceptably small APF limit: " + nai.networkMisc.maximumApfProgramSize);
- return;
- }
- // For now only support generating programs for Ethernet frames. If this restriction is
- // lifted:
- // 1. the program generator will need its offsets adjusted.
- // 2. the packet filter attached to our packet socket will need its offset adjusted.
- if (nai.networkMisc.apfPacketFormat != ARPHRD_ETHER) return;
- if (!new ApfGenerator().setApfVersion(nai.networkMisc.apfVersionSupported)) {
- Log.e(TAG, "Unsupported APF version: " + nai.networkMisc.apfVersionSupported);
- return;
- }
- nai.apfFilter = new ApfFilter(connectivityService, nai);
- }
-
- public void shutdown() {
- if (mReceiveThread != null) {
- log("shutting down");
- mReceiveThread.halt(); // Also closes socket.
- mReceiveThread = null;
- }
- }
-
- public void dump(IndentingPrintWriter pw) {
- pw.println("APF version: " + mNai.networkMisc.apfVersionSupported);
- pw.println("Max program size: " + mNai.networkMisc.maximumApfProgramSize);
- pw.println("Receive thread: " + (mReceiveThread != null ? "RUNNING" : "STOPPED"));
- if (mLastTimeInstalledProgram == 0) {
- pw.println("No program installed.");
- return;
- }
-
- pw.println(String.format(
- "Last program length %d, installed %ds ago, lifetime %d",
- mLastInstalledProgram.length, curTime() - mLastTimeInstalledProgram,
- mLastInstalledProgramMinLifetime));
-
- pw.println("RA filters:");
- pw.increaseIndent();
- for (Ra ra: mRas) {
- pw.println(ra);
- pw.increaseIndent();
- pw.println(String.format(
- "Seen: %d, last %ds ago", ra.seenCount, curTime() - ra.mLastSeen));
- if (DBG) {
- pw.println("Last match:");
- pw.increaseIndent();
- pw.println(ra.getLastMatchingPacket());
- pw.decreaseIndent();
- }
- pw.decreaseIndent();
- }
-
- if (DBG) {
- pw.println("Last program:");
- pw.increaseIndent();
- pw.println(HexDump.toHexString(mLastInstalledProgram, false /* lowercase */));
- pw.decreaseIndent();
- }
-
- pw.decreaseIndent();
- }
-}
diff --git a/services/core/java/com/android/server/connectivity/MetricsLoggerService.java b/services/core/java/com/android/server/connectivity/MetricsLoggerService.java
index f6dc9b9..f91db78 100644
--- a/services/core/java/com/android/server/connectivity/MetricsLoggerService.java
+++ b/services/core/java/com/android/server/connectivity/MetricsLoggerService.java
@@ -18,18 +18,21 @@
import com.android.server.SystemService;
+import android.app.PendingIntent;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.net.ConnectivityMetricsEvent;
import android.net.ConnectivityMetricsLogger;
import android.net.IConnectivityMetricsLogger;
-import android.net.IConnectivityMetricsLoggerSubscriber;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.ArrayMap;
+import android.os.Binder;
+import android.os.Parcel;
+import android.text.format.DateUtils;
import android.util.Log;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayDeque;
import java.util.ArrayList;
-import java.util.List;
/** {@hide} */
public class MetricsLoggerService extends SystemService {
@@ -43,134 +46,307 @@
@Override
public void onStart() {
+ resetThrottlingCounters(System.currentTimeMillis());
}
@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
- Log.d(TAG, "onBootPhase: PHASE_SYSTEM_SERVICES_READY");
+ if (DBG) Log.d(TAG, "onBootPhase: PHASE_SYSTEM_SERVICES_READY");
publishBinderService(ConnectivityMetricsLogger.CONNECTIVITY_METRICS_LOGGER_SERVICE,
mBinder);
}
}
- private final int MAX_NUMBER_OF_EVENTS = 100;
- private final int MAX_TIME_OFFSET = 15*60*1000; // 15 minutes
- private final List<ConnectivityMetricsEvent> mEvents = new ArrayList<>();
- private long mLastSentEventTimeMillis = System.currentTimeMillis();
+ // TODO: read from system property
+ private final int MAX_NUMBER_OF_EVENTS = 1000;
- private final void enforceConnectivityInternalPermission() {
- getContext().enforceCallingPermission(
+ // TODO: read from system property
+ private final int EVENTS_NOTIFICATION_THRESHOLD = 300;
+
+ // TODO: read from system property
+ private final int THROTTLING_TIME_INTERVAL_MILLIS = 60 * 60 * 1000; // 1 hour
+
+ // TODO: read from system property
+ private final int THROTTLING_MAX_NUMBER_OF_MESSAGES_PER_COMPONENT = 1000;
+
+ private int mEventCounter = 0;
+
+ /**
+ * Reference of the last event in the list of cached events.
+ *
+ * When client of this service retrieves events by calling getEvents, it is passing
+ * ConnectivityMetricsEvent.Reference object. After getEvents returns, that object will
+ * contain this reference. The client can save it and use next time it calls getEvents.
+ * This way only new events will be returned.
+ */
+ private long mLastEventReference = 0;
+
+ private final int mThrottlingCounters[] =
+ new int[ConnectivityMetricsLogger.NUMBER_OF_COMPONENTS];
+
+ private long mThrottlingIntervalBoundaryMillis;
+
+ private final ArrayDeque<ConnectivityMetricsEvent> mEvents = new ArrayDeque<>();
+
+ private void enforceConnectivityInternalPermission() {
+ getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.CONNECTIVITY_INTERNAL,
"MetricsLoggerService");
}
+ private void enforceDumpPermission() {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.DUMP,
+ "MetricsLoggerService");
+ }
+
+ private void resetThrottlingCounters(long currentTimeMillis) {
+ for (int i = 0; i < mThrottlingCounters.length; i++) {
+ mThrottlingCounters[i] = 0;
+ }
+ mThrottlingIntervalBoundaryMillis =
+ currentTimeMillis + THROTTLING_TIME_INTERVAL_MILLIS;
+ }
+
+ private void addEvent(ConnectivityMetricsEvent e) {
+ if (VDBG) {
+ Log.v(TAG, "writeEvent(" + e.toString() + ")");
+ }
+
+ while (mEvents.size() >= MAX_NUMBER_OF_EVENTS) {
+ mEvents.removeFirst();
+ }
+
+ mEvents.addLast(e);
+ }
+
/**
* Implementation of the IConnectivityMetricsLogger interface.
*/
private final IConnectivityMetricsLogger.Stub mBinder = new IConnectivityMetricsLogger.Stub() {
- private final ArrayMap<IConnectivityMetricsLoggerSubscriber,
- IBinder.DeathRecipient> mSubscribers = new ArrayMap<>();
+ private final ArrayList<PendingIntent> mPendingIntents = new ArrayList<>();
-
- private ConnectivityMetricsEvent[] prepareEventsToSendIfReady() {
- ConnectivityMetricsEvent[] eventsToSend = null;
- final long currentTimeMillis = System.currentTimeMillis();
- final long timeOffset = currentTimeMillis - mLastSentEventTimeMillis;
- if (timeOffset >= MAX_TIME_OFFSET
- || timeOffset < 0 // system time has changed
- || mEvents.size() >= MAX_NUMBER_OF_EVENTS) {
- // batch events
- mLastSentEventTimeMillis = currentTimeMillis;
- eventsToSend = new ConnectivityMetricsEvent[mEvents.size()];
- mEvents.toArray(eventsToSend);
- mEvents.clear();
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump ConnectivityMetricsLoggerService " +
+ "from from pid=" + Binder.getCallingPid() + ", uid=" +
+ Binder.getCallingUid());
+ return;
}
- return eventsToSend;
- }
- private void maybeSendEventsToSubscribers(ConnectivityMetricsEvent[] eventsToSend) {
- if (eventsToSend == null || eventsToSend.length == 0) return;
- synchronized (mSubscribers) {
- for (IConnectivityMetricsLoggerSubscriber s : mSubscribers.keySet()) {
- try {
- s.onEvents(eventsToSend);
- } catch (RemoteException ex) {
- Log.e(TAG, "RemoteException " + ex);
- }
- }
- }
- }
+ boolean dumpSerializedSize = false;
+ boolean dumpEvents = false;
+ for (String arg : args) {
+ switch (arg) {
+ case "--events":
+ dumpEvents = true;
+ break;
- public void logEvent(ConnectivityMetricsEvent event) {
- ConnectivityMetricsEvent[] events = new ConnectivityMetricsEvent[]{event};
- logEvents(events);
- }
+ case "--size":
+ dumpSerializedSize = true;
+ break;
- public void logEvents(ConnectivityMetricsEvent[] events) {
- enforceConnectivityInternalPermission();
- ConnectivityMetricsEvent[] eventsToSend;
-
- if (VDBG) {
- for (ConnectivityMetricsEvent e : events) {
- Log.v(TAG, "writeEvent(" + e.toString() + ")");
+ case "--all":
+ dumpEvents = true;
+ dumpSerializedSize = true;
+ break;
}
}
synchronized (mEvents) {
- for (ConnectivityMetricsEvent e : events) {
- mEvents.add(e);
+ pw.println("Number of events: " + mEvents.size());
+ pw.println("Time span: " +
+ DateUtils.formatElapsedTime(
+ (System.currentTimeMillis() - mEvents.peekFirst().timestamp)
+ / 1000));
+
+ if (dumpSerializedSize) {
+ long dataSize = 0;
+ Parcel p = Parcel.obtain();
+ for (ConnectivityMetricsEvent e : mEvents) {
+ dataSize += 16; // timestamp and 2 stamps
+
+ p.writeParcelable(e.data, 0);
+ }
+ dataSize += p.dataSize();
+ p.recycle();
+ pw.println("Serialized data size: " + dataSize);
}
- eventsToSend = prepareEventsToSendIfReady();
+ if (dumpEvents) {
+ pw.println();
+ pw.println("Events:");
+ for (ConnectivityMetricsEvent e : mEvents) {
+ pw.println(e.toString());
+ }
+ }
}
- maybeSendEventsToSubscribers(eventsToSend);
+ if (!mPendingIntents.isEmpty()) {
+ pw.println();
+ pw.println("Pending intents:");
+ for (PendingIntent pi : mPendingIntents) {
+ pw.println(pi.toString());
+ }
+ }
}
- public boolean subscribe(IConnectivityMetricsLoggerSubscriber subscriber) {
- enforceConnectivityInternalPermission();
- if (VDBG) Log.v(TAG, "subscribe");
+ public long logEvent(ConnectivityMetricsEvent event) {
+ ConnectivityMetricsEvent[] events = new ConnectivityMetricsEvent[]{event};
+ return logEvents(events);
+ }
- synchronized (mSubscribers) {
- if (mSubscribers.containsKey(subscriber)) {
- Log.e(TAG, "subscriber is already subscribed");
- return false;
+ /**
+ * @param events
+ *
+ * Note: All events must belong to the same component.
+ *
+ * @return 0 on success
+ * <0 if error happened
+ * >0 timestamp after which new events will be accepted
+ */
+ public long logEvents(ConnectivityMetricsEvent[] events) {
+ enforceConnectivityInternalPermission();
+
+ if (events == null || events.length == 0) {
+ Log.wtf(TAG, "No events passed to logEvents()");
+ return -1;
+ }
+
+ int componentTag = events[0].componentTag;
+ if (componentTag < 0 ||
+ componentTag >= ConnectivityMetricsLogger.NUMBER_OF_COMPONENTS) {
+ Log.wtf(TAG, "Unexpected tag: " + componentTag);
+ return -1;
+ }
+
+ synchronized (mThrottlingCounters) {
+ long currentTimeMillis = System.currentTimeMillis();
+ if (currentTimeMillis > mThrottlingIntervalBoundaryMillis) {
+ resetThrottlingCounters(currentTimeMillis);
}
- final IConnectivityMetricsLoggerSubscriber s = subscriber;
- IBinder.DeathRecipient dr = new IBinder.DeathRecipient() {
- @Override
- public void binderDied() {
- if (VDBG) Log.v(TAG, "subscriber died");
- synchronized (mSubscribers) {
- mSubscribers.remove(s);
+
+ mThrottlingCounters[componentTag] += events.length;
+
+ if (mThrottlingCounters[componentTag] >
+ THROTTLING_MAX_NUMBER_OF_MESSAGES_PER_COMPONENT) {
+ Log.w(TAG, "Too many events from #" + componentTag +
+ ". Block until " + mThrottlingIntervalBoundaryMillis);
+
+ return mThrottlingIntervalBoundaryMillis;
+ }
+ }
+
+ boolean sendPendingIntents = false;
+
+ synchronized (mEvents) {
+ for (ConnectivityMetricsEvent e : events) {
+ if (e.componentTag != componentTag) {
+ Log.wtf(TAG, "Unexpected tag: " + e.componentTag);
+ return -1;
+ }
+
+ addEvent(e);
+ }
+
+ mLastEventReference += events.length;
+
+ mEventCounter += events.length;
+ if (mEventCounter >= EVENTS_NOTIFICATION_THRESHOLD) {
+ mEventCounter = 0;
+ sendPendingIntents = true;
+ }
+ }
+
+ if (sendPendingIntents) {
+ synchronized (mPendingIntents) {
+ for (PendingIntent pi : mPendingIntents) {
+ if (VDBG) Log.v(TAG, "Send pending intent");
+ try {
+ pi.send(getContext(), 0, null, null, null);
+ } catch (PendingIntent.CanceledException e) {
+ Log.e(TAG, "Pending intent canceled: " + pi);
+ mPendingIntents.remove(pi);
}
}
- };
-
- try {
- subscriber.asBinder().linkToDeath(dr, 0);
- mSubscribers.put(subscriber, dr);
- } catch (RemoteException e) {
- Log.e(TAG, "subscribe failed: " + e);
- return false;
}
}
+ return 0;
+ }
+
+ /**
+ * Retrieve events
+ *
+ * @param reference of the last event previously returned. The function will return
+ * events following it.
+ * If 0 then all events will be returned.
+ * After the function call it will contain reference of the
+ * last returned event.
+ * @return events
+ */
+ public ConnectivityMetricsEvent[] getEvents(ConnectivityMetricsEvent.Reference reference) {
+ enforceDumpPermission();
+ long ref = reference.value;
+ if (VDBG) Log.v(TAG, "getEvents(" + ref + ")");
+
+ ConnectivityMetricsEvent[] result;
+ synchronized (mEvents) {
+ if (ref > mLastEventReference) {
+ Log.e(TAG, "Invalid reference");
+ reference.value = mLastEventReference;
+ return null;
+ }
+ if (ref < mLastEventReference - mEvents.size()) {
+ ref = mLastEventReference - mEvents.size();
+ }
+
+ int numEventsToSkip =
+ mEvents.size() // Total number of events
+ - (int)(mLastEventReference - ref); // Number of events to return
+
+ result = new ConnectivityMetricsEvent[mEvents.size() - numEventsToSkip];
+ int i = 0;
+ for (ConnectivityMetricsEvent e : mEvents) {
+ if (numEventsToSkip > 0) {
+ numEventsToSkip--;
+ } else {
+ result[i++] = e;
+ }
+ }
+ }
+
+ reference.value = mLastEventReference;
+
+ return result;
+ }
+
+ public boolean register(PendingIntent newEventsIntent) {
+ enforceDumpPermission();
+ if (VDBG) Log.v(TAG, "register(" + newEventsIntent + ")");
+
+ synchronized (mPendingIntents) {
+ if (mPendingIntents.remove(newEventsIntent)) {
+ Log.w(TAG, "Replacing registered pending intent");
+ }
+ mPendingIntents.add(newEventsIntent);
+ }
+
return true;
}
- public void unsubscribe(IConnectivityMetricsLoggerSubscriber subscriber) {
- enforceConnectivityInternalPermission();
- if (VDBG) Log.v(TAG, "unsubscribe");
- synchronized (mSubscribers) {
- IBinder.DeathRecipient dr = mSubscribers.remove(subscriber);
- if (dr == null) {
- Log.e(TAG, "subscriber is not subscribed");
- return;
+ public void unregister(PendingIntent newEventsIntent) {
+ enforceDumpPermission();
+ if (VDBG) Log.v(TAG, "unregister(" + newEventsIntent + ")");
+
+ synchronized (mPendingIntents) {
+ if (!mPendingIntents.remove(newEventsIntent)) {
+ Log.e(TAG, "Pending intent is not registered");
}
- subscriber.asBinder().unlinkToDeath(dr, 0);
}
}
};
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index b4c71c1..c5d38cb 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -32,7 +32,6 @@
import com.android.internal.util.AsyncChannel;
import com.android.server.ConnectivityService;
import com.android.server.connectivity.NetworkMonitor;
-import com.android.server.connectivity.ApfFilter;
import java.util.ArrayList;
import java.util.Comparator;
@@ -164,8 +163,6 @@
// Used by ConnectivityService to keep track of 464xlat.
public Nat464Xlat clatd;
- public ApfFilter apfFilter;
-
public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info,
LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler,
NetworkMisc misc, NetworkRequest defaultRequest, ConnectivityService connService) {
@@ -178,7 +175,6 @@
currentScore = score;
networkMonitor = connService.createNetworkMonitor(context, handler, this, defaultRequest);
networkMisc = misc;
- apfFilter.maybeInstall(connService, this);
}
/**
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index bce7733..bbb162e 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -34,6 +34,8 @@
import android.net.ProxyInfo;
import android.net.TrafficStats;
import android.net.Uri;
+import android.net.metrics.CaptivePortalCheckResultEvent;
+import android.net.metrics.CaptivePortalStateChangeEvent;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Handler;
@@ -297,9 +299,13 @@
transitionTo(mLingeringState);
return HANDLED;
case CMD_NETWORK_CONNECTED:
+ CaptivePortalStateChangeEvent.logEvent(
+ CaptivePortalStateChangeEvent.NETWORK_MONITOR_CONNECTED);
transitionTo(mEvaluatingState);
return HANDLED;
case CMD_NETWORK_DISCONNECTED:
+ CaptivePortalStateChangeEvent.logEvent(
+ CaptivePortalStateChangeEvent.NETWORK_MONITOR_DISCONNECTED);
if (mLaunchCaptivePortalAppBroadcastReceiver != null) {
mContext.unregisterReceiver(mLaunchCaptivePortalAppBroadcastReceiver);
mLaunchCaptivePortalAppBroadcastReceiver = null;
@@ -349,6 +355,8 @@
private class ValidatedState extends State {
@Override
public void enter() {
+ CaptivePortalStateChangeEvent.logEvent(
+ CaptivePortalStateChangeEvent.NETWORK_MONITOR_VALIDATED);
mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
NETWORK_TEST_RESULT_VALID, 0, mNetworkAgentInfo));
}
@@ -457,6 +465,8 @@
// will be unresponsive. isCaptivePortal() could be executed on another Thread
// if this is found to cause problems.
int httpResponseCode = isCaptivePortal();
+ CaptivePortalCheckResultEvent.logEvent(mNetworkAgentInfo.network.netId,
+ httpResponseCode);
if (httpResponseCode == 204) {
transitionTo(mValidatedState);
} else if (httpResponseCode >= 200 && httpResponseCode <= 399) {
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index da9c48a..2cba93fd 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -32,6 +32,7 @@
import android.content.res.Resources;
import android.hardware.usb.UsbManager;
import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
import android.net.INetworkStatsService;
import android.net.InterfaceConfiguration;
import android.net.LinkAddress;
@@ -40,6 +41,7 @@
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkRequest;
+import android.net.NetworkState;
import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.net.wifi.WifiManager;
@@ -88,7 +90,7 @@
*/
public class Tethering extends BaseNetworkObserver {
- private Context mContext;
+ private final Context mContext;
private final static String TAG = "Tethering";
private final static boolean DBG = false;
private final static boolean VDBG = false;
@@ -100,7 +102,7 @@
private Collection<Integer> mUpstreamIfaceTypes;
// used to synchronize public access to members
- private Object mPublicSync;
+ private final Object mPublicSync;
private static final Integer MOBILE_TYPE = new Integer(ConnectivityManager.TYPE_MOBILE);
private static final Integer HIPRI_TYPE = new Integer(ConnectivityManager.TYPE_MOBILE_HIPRI);
@@ -112,7 +114,7 @@
private final INetworkManagementService mNMService;
private final INetworkStatsService mStatsService;
- private Looper mLooper;
+ private final Looper mLooper;
private HashMap<String, TetherInterfaceSM> mIfaces; // all tethered/tetherable ifaces
@@ -143,7 +145,9 @@
private static final String DNS_DEFAULT_SERVER1 = "8.8.8.8";
private static final String DNS_DEFAULT_SERVER2 = "8.8.4.4";
- private StateMachine mTetherMasterSM;
+ private final StateMachine mTetherMasterSM;
+ private final UpstreamNetworkMonitor mUpstreamNetworkMonitor;
+ private String mCurrentUpstreamIface;
private Notification.Builder mTetheredNotificationBuilder;
private int mLastNotificationId;
@@ -167,6 +171,8 @@
mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper);
mTetherMasterSM.start();
+ mUpstreamNetworkMonitor = new UpstreamNetworkMonitor();
+
mStateReceiver = new StateReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(UsbManager.ACTION_USB_STATE);
@@ -505,7 +511,7 @@
};
// The following is necessary to avoid unmarshalling issues when sending the receiver
- // across proccesses.
+ // across processes.
Parcel parcel = Parcel.obtain();
rr.writeToParcel(parcel,0);
parcel.setDataPosition(0);
@@ -559,6 +565,7 @@
}
}
}
+
public int tether(String iface) {
if (DBG) Log.d(TAG, "Tethering " + iface);
TetherInterfaceSM sm = null;
@@ -1204,6 +1211,11 @@
Log.e(TAG, "Error Tethering: " + e.toString());
setLastError(ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR);
+ try {
+ mNMService.untetherInterface(mIfaceName);
+ } catch (Exception ee) {
+ Log.e(TAG, "Error untethering after failure!" + ee.toString());
+ }
transitionTo(mInitialState);
return;
}
@@ -1371,15 +1383,115 @@
}
+ /**
+ * A NetworkCallback class that relays information of interest to the
+ * tethering master state machine thread for subsequent processing.
+ */
+ class UpstreamNetworkCallback extends NetworkCallback {
+ @Override
+ public void onLinkPropertiesChanged(Network network, LinkProperties newLp) {
+ mTetherMasterSM.sendMessage(
+ TetherMasterSM.EVENT_UPSTREAM_LINKPROPERTIES_CHANGED,
+ new NetworkState(null, newLp, null, network, null, null));
+ }
+
+ @Override
+ public void onLost(Network network) {
+ mTetherMasterSM.sendMessage(TetherMasterSM.EVENT_UPSTREAM_LOST, network);
+ }
+ }
+
+ /**
+ * A class to centralize all the network and link properties information
+ * pertaining to the current and any potential upstream network.
+ *
+ * Calling #start() registers two callbacks: one to track the system default
+ * network and a second to specifically observe TYPE_MOBILE_DUN networks.
+ *
+ * The methods and data members of this class are only to be accessed and
+ * modified from the tethering master state machine thread. Any other
+ * access semantics would necessitate the addition of locking.
+ *
+ * TODO: Investigate whether more "upstream-specific" logic/functionality
+ * could/should be moved here.
+ */
+ class UpstreamNetworkMonitor {
+ final HashMap<Network, NetworkState> mNetworkMap = new HashMap();
+ NetworkCallback mDefaultNetworkCallback;
+ NetworkCallback mDunTetheringCallback;
+
+ void start() {
+ stop();
+
+ mDefaultNetworkCallback = new UpstreamNetworkCallback();
+ getConnectivityManager().registerDefaultNetworkCallback(mDefaultNetworkCallback);
+
+ final NetworkRequest dunTetheringRequest = new NetworkRequest.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN)
+ .build();
+ mDunTetheringCallback = new UpstreamNetworkCallback();
+ getConnectivityManager().registerNetworkCallback(
+ dunTetheringRequest, mDunTetheringCallback);
+ }
+
+ void stop() {
+ if (mDefaultNetworkCallback != null) {
+ getConnectivityManager().unregisterNetworkCallback(mDefaultNetworkCallback);
+ mDefaultNetworkCallback = null;
+ }
+
+ if (mDunTetheringCallback != null) {
+ getConnectivityManager().unregisterNetworkCallback(mDunTetheringCallback);
+ mDunTetheringCallback = null;
+ }
+
+ mNetworkMap.clear();
+ }
+
+ // Returns true if these updated LinkProperties pertain to the current
+ // upstream network interface, false otherwise (or if there is not
+ // currently any upstream tethering interface).
+ boolean processLinkPropertiesChanged(NetworkState networkState) {
+ if (networkState == null ||
+ networkState.network == null ||
+ networkState.linkProperties == null) {
+ return false;
+ }
+
+ mNetworkMap.put(networkState.network, networkState);
+
+ if (mCurrentUpstreamIface != null) {
+ for (String ifname : networkState.linkProperties.getAllInterfaceNames()) {
+ if (mCurrentUpstreamIface.equals(ifname)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ void processNetworkLost(Network network) {
+ if (network != null) {
+ mNetworkMap.remove(network);
+ }
+ }
+ }
+
class TetherMasterSM extends StateMachine {
// an interface SM has requested Tethering
- static final int CMD_TETHER_MODE_REQUESTED = 1;
+ static final int CMD_TETHER_MODE_REQUESTED = 1;
// an interface SM has unrequested Tethering
- static final int CMD_TETHER_MODE_UNREQUESTED = 2;
+ static final int CMD_TETHER_MODE_UNREQUESTED = 2;
// upstream connection change - do the right thing
- static final int CMD_UPSTREAM_CHANGED = 3;
+ static final int CMD_UPSTREAM_CHANGED = 3;
// we don't have a valid upstream conn, check again after a delay
- static final int CMD_RETRY_UPSTREAM = 4;
+ static final int CMD_RETRY_UPSTREAM = 4;
+ // Events from NetworkCallbacks that we process on the master state
+ // machine thread on behalf of the UpstreamNetworkMonitor.
+ static final int EVENT_UPSTREAM_LINKPROPERTIES_CHANGED = 5;
+ static final int EVENT_UPSTREAM_LOST = 6;
// This indicates what a timeout event relates to. A state that
// sends itself a delayed timeout event and handles incoming timeout events
@@ -1399,9 +1511,7 @@
private ArrayList<TetherInterfaceSM> mNotifyList;
private int mMobileApnReserved = ConnectivityManager.TYPE_NONE;
- private ConnectivityManager.NetworkCallback mMobileUpstreamCallback;
-
- private String mUpstreamIfaceName = null;
+ private NetworkCallback mMobileUpstreamCallback;
private static final int UPSTREAM_SETTLE_TIME_MS = 10000;
@@ -1430,8 +1540,6 @@
}
class TetherMasterUtilState extends State {
- protected final static boolean WAIT_FOR_NETWORK_TO_SETTLE = false;
-
@Override
public boolean processMessage(Message m) {
return false;
@@ -1461,27 +1569,27 @@
return false;
}
- NetworkRequest.Builder builder = new NetworkRequest.Builder()
+ final NetworkRequest.Builder builder = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
if (apnType == ConnectivityManager.TYPE_MOBILE_DUN) {
- builder.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN)
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
+ builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
} else {
builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
}
- NetworkRequest mobileUpstreamRequest = builder.build();
- // Other mechanisms notice network and interface changes and act upon them.
- // TODO, imminently: replace with a proper NetworkCallback-based scheme.
- //
+ final NetworkRequest mobileUpstreamRequest = builder.build();
+
+ // The UpstreamNetworkMonitor's callback will be notified.
+ // Therefore, to avoid duplicate notifications, we only register a no-op.
+ mMobileUpstreamCallback = new NetworkCallback();
+
// TODO: Change the timeout from 0 (no onUnavailable callback) to use some
// moderate callback time (once timeout callbacks are implemented). This might
// be useful for updating some UI. Additionally, we should definitely log a
- // message to aid in any subsequent debugging.
- mMobileUpstreamCallback = new ConnectivityManager.NetworkCallback();
+ // message to aid in any subsequent debugging
if (DBG) Log.d(TAG, "requesting mobile upstream network: " + mobileUpstreamRequest);
getConnectivityManager().requestNetwork(
mobileUpstreamRequest, mMobileUpstreamCallback, 0, apnType);
-
return true;
}
@@ -1513,6 +1621,7 @@
}
return true;
}
+
protected boolean turnOffMasterTetherSettings() {
try {
mNMService.stopTethering();
@@ -1606,34 +1715,41 @@
}
if (iface != null) {
- String[] dnsServers = mDefaultDnsServers;
- Collection<InetAddress> dnses = linkProperties.getDnsServers();
- if (dnses != null && !dnses.isEmpty()) {
- // TODO: remove this invocation of NetworkUtils.makeStrings().
- dnsServers = NetworkUtils.makeStrings(dnses);
+ Network network = getConnectivityManager().getNetworkForType(upType);
+ if (network == null) {
+ Log.e(TAG, "No Network for upstream type " + upType + "!");
}
- try {
- Network network = getConnectivityManager().getNetworkForType(upType);
- if (network == null) {
- Log.e(TAG, "No Network for upstream type " + upType + "!");
- }
- if (VDBG) {
- Log.d(TAG, "Setting DNS forwarders: Network=" + network +
- ", dnsServers=" + Arrays.toString(dnsServers));
- }
- mNMService.setDnsForwarders(network, dnsServers);
- } catch (Exception e) {
- Log.e(TAG, "Setting DNS forwarders failed!");
- transitionTo(mSetDnsForwardersErrorState);
- }
+ setDnsForwarders(network, linkProperties);
}
}
notifyTetheredOfNewUpstreamIface(iface);
}
+ protected void setDnsForwarders(final Network network, final LinkProperties lp) {
+ String[] dnsServers = mDefaultDnsServers;
+ final Collection<InetAddress> dnses = lp.getDnsServers();
+ // TODO: Properly support the absence of DNS servers.
+ if (dnses != null && !dnses.isEmpty()) {
+ // TODO: remove this invocation of NetworkUtils.makeStrings().
+ dnsServers = NetworkUtils.makeStrings(dnses);
+ }
+ if (VDBG) {
+ Log.d(TAG, "Setting DNS forwarders: Network=" + network +
+ ", dnsServers=" + Arrays.toString(dnsServers));
+ }
+ try {
+ mNMService.setDnsForwarders(network, dnsServers);
+ } catch (Exception e) {
+ // TODO: Investigate how this can fail and what exactly
+ // happens if/when such failures occur.
+ Log.e(TAG, "Setting DNS forwarders failed!");
+ transitionTo(mSetDnsForwardersErrorState);
+ }
+ }
+
protected void notifyTetheredOfNewUpstreamIface(String ifaceName) {
if (DBG) Log.d(TAG, "notifying tethered with iface =" + ifaceName);
- mUpstreamIfaceName = ifaceName;
+ mCurrentUpstreamIface = ifaceName;
for (TetherInterfaceSM sm : mNotifyList) {
sm.sendMessage(TetherInterfaceSM.CMD_TETHER_CONNECTION_CHANGED,
ifaceName);
@@ -1772,20 +1888,23 @@
}
class TetherModeAliveState extends TetherMasterUtilState {
- boolean mTryCell = !WAIT_FOR_NETWORK_TO_SETTLE;
+ boolean mTryCell = true;
@Override
public void enter() {
+ // TODO: examine if we should check the return value.
turnOnMasterTetherSettings(); // may transition us out
startListeningForSimChanges();
+ mUpstreamNetworkMonitor.start();
- mTryCell = !WAIT_FOR_NETWORK_TO_SETTLE; // better try something first pass
- // or crazy tests cases will fail
+ mTryCell = true; // better try something first pass or crazy tests cases will fail
chooseUpstreamType(mTryCell);
mTryCell = !mTryCell;
}
@Override
public void exit() {
+ // TODO: examine if we should check the return value.
turnOffUpstreamMobileConnection();
+ mUpstreamNetworkMonitor.stop();
stopListeningForSimChanges();
notifyTetheredOfNewUpstreamIface(null);
}
@@ -1799,7 +1918,7 @@
if (VDBG) Log.d(TAG, "Tether Mode requested by " + who);
mNotifyList.add(who);
who.sendMessage(TetherInterfaceSM.CMD_TETHER_CONNECTION_CHANGED,
- mUpstreamIfaceName);
+ mCurrentUpstreamIface);
break;
case CMD_TETHER_MODE_UNREQUESTED:
who = (TetherInterfaceSM)message.obj;
@@ -1823,7 +1942,7 @@
break;
case CMD_UPSTREAM_CHANGED:
// need to try DUN immediately if Wifi goes down
- mTryCell = !WAIT_FOR_NETWORK_TO_SETTLE;
+ mTryCell = true;
chooseUpstreamType(mTryCell);
mTryCell = !mTryCell;
break;
@@ -1831,6 +1950,24 @@
chooseUpstreamType(mTryCell);
mTryCell = !mTryCell;
break;
+ case EVENT_UPSTREAM_LINKPROPERTIES_CHANGED:
+ NetworkState state = (NetworkState) message.obj;
+ if (mUpstreamNetworkMonitor.processLinkPropertiesChanged(state)) {
+ setDnsForwarders(state.network, state.linkProperties);
+ } else if (mCurrentUpstreamIface == null) {
+ // If we have no upstream interface, try to run through upstream
+ // selection again. If, for example, IPv4 connectivity has shown up
+ // after IPv6 (e.g., 464xlat became available) we want the chance to
+ // notice and act accordingly.
+ chooseUpstreamType(false);
+ }
+ break;
+ case EVENT_UPSTREAM_LOST:
+ // TODO: Re-evaluate possible upstreams. Currently upstream reevaluation
+ // is triggered via received CONNECTIVITY_ACTION broadcasts that result
+ // in being passed a TetherMasterSM.CMD_UPSTREAM_CHANGED.
+ mUpstreamNetworkMonitor.processNetworkLost((Network) message.obj);
+ break;
default:
retValue = false;
break;
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 3b0b79a..a111bf9 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -264,7 +264,12 @@
return true;
}
- // Check if the caller is authorized.
+ // Stop an existing always-on VPN from being dethroned by other apps.
+ if (getAlwaysOnPackage() != null) {
+ return false;
+ }
+
+ // Check that the caller is authorized.
enforceControlPermission();
prepareInternal(newPackage);
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 28170f2..6e7ea99 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -26,9 +26,9 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.IContentService;
+import android.content.ISyncStatusObserver;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.ISyncStatusObserver;
import android.content.PeriodicSync;
import android.content.SyncAdapterType;
import android.content.SyncInfo;
@@ -42,10 +42,10 @@
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
+import android.os.FactoryTest;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -58,24 +58,46 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
-import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
+import com.android.server.SystemService;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.security.InvalidParameterException;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
-import java.util.Objects;
/**
* {@hide}
*/
public final class ContentService extends IContentService.Stub {
- private static final String TAG = "ContentService";
+ static final String TAG = "ContentService";
+ static final boolean DEBUG = false;
+
+ public static class Lifecycle extends SystemService {
+ private ContentService mContentService;
+
+ public Lifecycle(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ final boolean factoryTest = (FactoryTest
+ .getMode() == FactoryTest.FACTORY_TEST_LOW_LEVEL);
+ mContentService = new ContentService(getContext(), factoryTest);
+ publishBinderService(ContentResolver.CONTENT_SERVICE_NAME, mContentService);
+ }
+
+ @Override
+ public void onCleanupUser(int userHandle) {
+ synchronized (mContentService.mCache) {
+ mContentService.mCache.remove(userHandle);
+ }
+ }
+ }
private Context mContext;
private boolean mFactoryTest;
@@ -97,12 +119,18 @@
private BroadcastReceiver mCacheReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- final Uri data = intent.getData();
- if (data != null) {
- final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
- UserHandle.USER_NULL);
- final String packageName = data.getSchemeSpecificPart();
- invalidateCacheLocked(userId, packageName, null);
+ synchronized (mCache) {
+ if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
+ mCache.clear();
+ } else {
+ final Uri data = intent.getData();
+ if (data != null) {
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
+ UserHandle.USER_NULL);
+ final String packageName = data.getSchemeSpecificPart();
+ invalidateCacheLocked(userId, packageName, null);
+ }
+ }
}
}
};
@@ -230,6 +258,11 @@
packageFilter.addDataScheme("package");
mContext.registerReceiverAsUser(mCacheReceiver, UserHandle.ALL,
packageFilter, null, null);
+
+ final IntentFilter localeFilter = new IntentFilter();
+ localeFilter.addAction(Intent.ACTION_LOCALE_CHANGED);
+ mContext.registerReceiverAsUser(mCacheReceiver, UserHandle.ALL,
+ localeFilter, null, null);
}
public void systemReady() {
@@ -307,12 +340,10 @@
*/
@Override
public void notifyChange(Uri uri, IContentObserver observer,
- boolean observerWantsSelfNotifications, boolean syncToNetwork,
+ boolean observerWantsSelfNotifications, int flags,
int userHandle) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "Notifying update of " + uri + " for user " + userHandle
- + " from observer " + observer + ", syncToNetwork " + syncToNetwork);
- }
+ if (DEBUG) Slog.d(TAG, "Notifying update of " + uri + " for user " + userHandle
+ + " from observer " + observer + ", flags " + Integer.toHexString(flags));
final int uid = Binder.getCallingUid();
final int pid = Binder.getCallingPid();
@@ -341,16 +372,15 @@
ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
synchronized (mRootNode) {
mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications,
- userHandle, calls);
+ flags, userHandle, calls);
}
final int numCalls = calls.size();
for (int i=0; i<numCalls; i++) {
ObserverCall oc = calls.get(i);
try {
oc.mObserver.onChange(oc.mSelfChange, uri, userHandle);
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "Notified " + oc.mObserver + " of " + "update at " + uri);
- }
+ if (DEBUG) Slog.d(TAG, "Notified " + oc.mObserver + " of " + "update at "
+ + uri);
} catch (RemoteException ex) {
synchronized (mRootNode) {
Log.w(TAG, "Found dead observer, removing");
@@ -369,7 +399,7 @@
}
}
}
- if (syncToNetwork) {
+ if ((flags&ContentResolver.NOTIFY_SYNC_TO_NETWORK) != 0) {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, uid,
@@ -388,7 +418,8 @@
public void notifyChange(Uri uri, IContentObserver observer,
boolean observerWantsSelfNotifications, boolean syncToNetwork) {
- notifyChange(uri, observer, observerWantsSelfNotifications, syncToNetwork,
+ notifyChange(uri, observer, observerWantsSelfNotifications,
+ syncToNetwork ? ContentResolver.NOTIFY_SYNC_TO_NETWORK : 0,
UserHandle.getCallingUserId());
}
@@ -1030,14 +1061,16 @@
if (uri != null) {
for (int i = 0; i < packageCache.size();) {
- final Uri key = packageCache.keyAt(i).second;
- if (Objects.equals(key, uri)) {
+ final Pair<String, Uri> key = packageCache.keyAt(i);
+ if (key.second != null && key.second.toString().startsWith(uri.toString())) {
+ if (DEBUG) Slog.d(TAG, "Invalidating cache for key " + key);
packageCache.removeAt(i);
} else {
i++;
}
}
} else {
+ if (DEBUG) Slog.d(TAG, "Invalidating cache for package " + providerPackageName);
packageCache.clear();
}
}
@@ -1081,12 +1114,6 @@
}
}
- public static ContentService main(Context context, boolean factoryTest) {
- ContentService service = new ContentService(context, factoryTest);
- ServiceManager.addService(ContentResolver.CONTENT_SERVICE_NAME, service);
- return service;
- }
-
/**
* Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS_FULL
* permission, if the userHandle is not for the caller.
@@ -1282,8 +1309,8 @@
}
private void collectMyObserversLocked(boolean leaf, IContentObserver observer,
- boolean observerWantsSelfNotifications, int targetUserHandle,
- ArrayList<ObserverCall> calls) {
+ boolean observerWantsSelfNotifications, int flags,
+ int targetUserHandle, ArrayList<ObserverCall> calls) {
int N = mObservers.size();
IBinder observerBinder = observer == null ? null : observer.asBinder();
for (int i = 0; i < N; i++) {
@@ -1301,9 +1328,29 @@
|| entry.userHandle == UserHandle.USER_ALL
|| targetUserHandle == entry.userHandle) {
// Make sure the observer is interested in the notification
- if (leaf || (!leaf && entry.notifyForDescendants)) {
- calls.add(new ObserverCall(this, entry.observer, selfChange));
+ if (leaf) {
+ // If we are at the leaf: we always report, unless the sender has asked
+ // to skip observers that are notifying for descendants (since they will
+ // be sending another more specific URI for them).
+ if ((flags&ContentResolver.NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS) != 0
+ && entry.notifyForDescendants) {
+ if (DEBUG) Slog.d(TAG, "Skipping " + entry.observer
+ + ": skip notify for descendants");
+ continue;
+ }
+ } else {
+ // If we are not at the leaf: we report if the observer says it wants
+ // to be notified for all descendants.
+ if (!entry.notifyForDescendants) {
+ if (DEBUG) Slog.d(TAG, "Skipping " + entry.observer
+ + ": not monitor descendants");
+ continue;
+ }
}
+ if (DEBUG) Slog.d(TAG, "Reporting to " + entry.observer + ": leaf=" + leaf
+ + " flags=" + Integer.toHexString(flags)
+ + " desc=" + entry.notifyForDescendants);
+ calls.add(new ObserverCall(this, entry.observer, selfChange));
}
}
}
@@ -1312,19 +1359,22 @@
* targetUserHandle is either a hard user handle or is USER_ALL
*/
public void collectObserversLocked(Uri uri, int index, IContentObserver observer,
- boolean observerWantsSelfNotifications, int targetUserHandle,
- ArrayList<ObserverCall> calls) {
+ boolean observerWantsSelfNotifications, int flags,
+ int targetUserHandle, ArrayList<ObserverCall> calls) {
String segment = null;
int segmentCount = countUriSegments(uri);
if (index >= segmentCount) {
// This is the leaf node, notify all observers
+ if (DEBUG) Slog.d(TAG, "Collecting leaf observers @ #" + index + ", node " + mName);
collectMyObserversLocked(true, observer, observerWantsSelfNotifications,
- targetUserHandle, calls);
+ flags, targetUserHandle, calls);
} else if (index < segmentCount){
segment = getUriSegment(uri, index);
+ if (DEBUG) Slog.d(TAG, "Collecting non-leaf observers @ #" + index + " / "
+ + segment);
// Notify any observers at this level who are interested in descendants
collectMyObserversLocked(false, observer, observerWantsSelfNotifications,
- targetUserHandle, calls);
+ flags, targetUserHandle, calls);
}
int N = mChildren.size();
@@ -1332,8 +1382,8 @@
ObserverNode node = mChildren.get(i);
if (segment == null || node.mName.equals(segment)) {
// We found the child,
- node.collectObserversLocked(uri, index + 1,
- observer, observerWantsSelfNotifications, targetUserHandle, calls);
+ node.collectObserversLocked(uri, index + 1, observer,
+ observerWantsSelfNotifications, flags, targetUserHandle, calls);
if (segment != null) {
break;
}
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index e5342ce..db41a54 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -1402,12 +1402,24 @@
}
}
+ private void restoreLostPeriodicSyncsIfNeeded(int userId) {
+ List<SyncOperation> periodicSyncs = new ArrayList<SyncOperation>();
+ for (SyncOperation sync : getAllPendingSyncs()) {
+ if (sync.isPeriodic && sync.target.userId == userId) {
+ periodicSyncs.add(sync);
+ }
+ }
+ mSyncStorageEngine.restorePeriodicSyncsIfNeededForUser(userId, periodicSyncs);
+ }
+
private void onUserUnlocked(int userId) {
// Make sure that accounts we're about to use are valid.
AccountManagerService.getSingleton().validateAccounts(userId);
mSyncAdapters.invalidateCache(userId);
+ restoreLostPeriodicSyncsIfNeeded(userId);
+
EndPoint target = new EndPoint(null, null, userId);
updateRunningAccounts(target);
@@ -2578,9 +2590,11 @@
}
}
+ // Cancel all jobs from non-existent accounts.
+ AccountAndUser[] allAccounts = AccountManagerService.getSingleton().getAllAccounts();
List<SyncOperation> ops = getAllPendingSyncs();
for (SyncOperation op: ops) {
- if (!containsAccountAndUser(accounts, op.target.account, op.target.userId)) {
+ if (!containsAccountAndUser(allAccounts, op.target.account, op.target.userId)) {
getJobScheduler().cancel(op.jobId);
}
}
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index bc3fc6a..fb23265 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -826,6 +826,35 @@
return true;
}
+ /**
+ * STOPSHIP This is a temporary workaround and should be removed before shipping: b/28052438
+ */
+ void restorePeriodicSyncsIfNeededForUser(int userHandle, List<SyncOperation> periodicSyncs) {
+ if (mPeriodicSyncAddedListener == null) {
+ return;
+ }
+ synchronized (mAuthorities) {
+ for (int i = 0; i < mAuthorities.size(); i++) {
+ AuthorityInfo authority = mAuthorities.valueAt(i);
+ if (authority.target.userId == userHandle && authority.enabled) {
+ boolean periodicSyncAlreadyExists = false;
+ for (SyncOperation sync : periodicSyncs) {
+ if (authority.target.matchesSpec(sync.target)) {
+ periodicSyncAlreadyExists = true;
+ break;
+ }
+ }
+ // The periodic sync must have been lost due to previous bug.
+ if (!periodicSyncAlreadyExists) {
+ mPeriodicSyncAddedListener.onPeriodicSyncAdded(authority.target,
+ new Bundle(), DEFAULT_POLL_FREQUENCY_SECONDS,
+ calculateDefaultFlexTime(DEFAULT_POLL_FREQUENCY_SECONDS));
+ }
+ }
+ }
+ }
+ }
+
public void setMasterSyncAutomatically(boolean flag, int userId) {
synchronized (mAuthorities) {
Boolean auto = mMasterSyncAutomatically.get(userId);
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 1ed7070..8f8afd5 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -103,7 +103,6 @@
private static final float TYPICAL_PROXIMITY_THRESHOLD = 5.0f;
// Brightness animation ramp rate in brightness units per second.
- private static final int BRIGHTNESS_RAMP_RATE_FAST = 200;
private static final int BRIGHTNESS_RAMP_RATE_SLOW = 40;
private static final int REPORTED_TO_POLICY_SCREEN_OFF = 0;
@@ -244,6 +243,9 @@
private boolean mAppliedDimming;
private boolean mAppliedLowPower;
+ // Brightness ramp rate fast.
+ private final int mBrightnessRampRateFast;
+
// The controller for the automatic brightness level.
private AutomaticBrightnessController mAutomaticBrightnessController;
@@ -303,6 +305,9 @@
mAllowAutoBrightnessWhileDozingConfig = resources.getBoolean(
com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing);
+ mBrightnessRampRateFast = resources.getInteger(
+ com.android.internal.R.integer.config_brightness_ramp_rate_fast);
+
int lightSensorRate = resources.getInteger(
com.android.internal.R.integer.config_autoBrightnessLightSensorRate);
long brighteningLightDebounce = resources.getInteger(
@@ -698,7 +703,7 @@
if (!mPendingScreenOff) {
if (state == Display.STATE_ON || state == Display.STATE_DOZE) {
animateScreenBrightness(brightness,
- slowChange ? BRIGHTNESS_RAMP_RATE_SLOW : BRIGHTNESS_RAMP_RATE_FAST);
+ slowChange ? BRIGHTNESS_RAMP_RATE_SLOW : mBrightnessRampRateFast);
} else {
animateScreenBrightness(brightness, 0);
}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 4527f1f..715d2d8 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -17,12 +17,12 @@
package com.android.server.display;
import android.content.res.Resources;
-import android.os.Build;
import com.android.server.LocalServices;
import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;
import android.content.Context;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -31,7 +31,6 @@
import android.os.Trace;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.SparseBooleanArray;
import android.view.Display;
import android.view.DisplayEventReceiver;
import android.view.Surface;
@@ -388,7 +387,7 @@
mInfo.flags |= DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
| DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
if (res.getBoolean(com.android.internal.R.bool.config_mainBuiltInDisplayIsRound)
- || (Build.HARDWARE.contains("goldfish")
+ || (Build.IS_EMULATOR
&& SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false))) {
mInfo.flags |= DisplayDeviceInfo.FLAG_ROUND;
}
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 8813a61..1f6616e 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -335,7 +335,8 @@
private ServiceInfo getServiceInfo(ComponentName name) {
try {
- return name != null ? mContext.getPackageManager().getServiceInfo(name, 0) : null;
+ return name != null ? mContext.getPackageManager().getServiceInfo(name,
+ PackageManager.MATCH_DEBUG_TRIAGED_MISSING) : null;
} catch (NameNotFoundException e) {
return null;
}
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 7b134ca..7ba030f 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -107,6 +107,7 @@
private static final int FINGERPRINT_ACQUIRED_GOOD = 0;
private final String mKeyguardPackage;
private int mCurrentUserId = UserHandle.USER_CURRENT;
+ private int mUserIdForRemove = UserHandle.USER_NULL;
Handler mHandler = new Handler() {
@Override
@@ -205,10 +206,12 @@
protected void handleRemoved(long deviceId, int fingerId, int groupId) {
final ClientMonitor client = mRemoveClient;
if (fingerId != 0) {
- removeTemplateForUser(mRemoveClient, fingerId);
+ removeTemplateForUser(mUserIdForRemove, fingerId);
+ } else {
+ mUserIdForRemove = UserHandle.USER_NULL;
}
if (client != null && client.sendRemoved(fingerId, groupId)) {
- removeClient(mRemoveClient);
+ removeClient(client);
}
}
@@ -325,8 +328,8 @@
return false;
}
- private void removeTemplateForUser(ClientMonitor clientMonitor, int fingerId) {
- mFingerprintUtils.removeFingerprintIdForUser(mContext, fingerId, clientMonitor.userId);
+ private void removeTemplateForUser(int userId, int fingerId) {
+ mFingerprintUtils.removeFingerprintIdForUser(mContext, fingerId, userId);
}
private void addTemplateForUser(ClientMonitor clientMonitor, int fingerId) {
@@ -488,6 +491,7 @@
stopPendingOperations(true);
mRemoveClient = new ClientMonitor(token, receiver, userId, restricted, token.toString());
+ mUserIdForRemove = mCurrentUserId;
// The fingerprint template ids will be removed when we get confirmation from the HAL
try {
final int result = daemon.remove(fingerId, userId);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index a36e671..69c012e 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -165,15 +165,13 @@
@ServiceThreadOnly
protected void onStandby(boolean initiatedByCec, int standbyAction) {
assertRunOnServiceThread();
- if (!mService.isControlEnabled() || initiatedByCec) {
+ if (!mService.isControlEnabled() || initiatedByCec || !mAutoTvOff) {
return;
}
switch (standbyAction) {
case HdmiControlService.STANDBY_SCREEN_OFF:
- if (mAutoTvOff) {
- mService.sendCecCommand(
- HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_TV));
- }
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_TV));
break;
case HdmiControlService.STANDBY_SHUTDOWN:
// ACTION_SHUTDOWN is taken as a signal to power off all the devices.
diff --git a/services/core/java/com/android/server/hdmi/HdmiLogger.java b/services/core/java/com/android/server/hdmi/HdmiLogger.java
index 0b201710..537df81 100644
--- a/services/core/java/com/android/server/hdmi/HdmiLogger.java
+++ b/services/core/java/com/android/server/hdmi/HdmiLogger.java
@@ -21,6 +21,7 @@
import android.os.SystemClock;
import android.util.Pair;
import android.util.Slog;
+import android.util.Log;
import java.util.HashMap;
@@ -42,6 +43,7 @@
// Logging duration for same error message.
private static final long ERROR_LOG_DURATTION_MILLIS = 20 * 1000; // 20s
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final boolean IS_USER_BUILD = "user".equals(Build.TYPE);
private static final ThreadLocal<HdmiLogger> sLogger = new ThreadLocal<>();
@@ -83,10 +85,9 @@
}
private void debugInternal(String logMessage) {
- if (true || IS_USER_BUILD) {
- return;
+ if (DEBUG) {
+ Slog.d(TAG, logMessage);
}
- Slog.d(TAG, logMessage);
}
private static final String toLogString(String logMessage, Object[] objs) {
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 3c04b78..c7c765bb 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -200,6 +200,7 @@
private static native int nativeInjectInputEvent(long ptr, InputEvent event, int displayId,
int injectorPid, int injectorUid, int syncMode, int timeoutMillis,
int policyFlags);
+ private static native void nativeToggleCapsLock(long ptr, int deviceId);
private static native void nativeSetInputWindows(long ptr, InputWindowHandle[] windowHandles);
private static native void nativeSetInputDispatchMode(long ptr, boolean enabled, boolean frozen);
private static native void nativeSetSystemUiVisibility(long ptr, int visibility);
@@ -948,7 +949,7 @@
// Must be called on handler.
private void showMissingKeyboardLayoutNotification(InputDevice device) {
if (!mKeyboardLayoutNotificationShown) {
- final Intent intent = new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS);
+ final Intent intent = new Intent(Settings.ACTION_HARD_KEYBOARD_SETTINGS);
if (device != null) {
intent.putExtra(Settings.EXTRA_INPUT_DEVICE_IDENTIFIER, device.getIdentifier());
}
@@ -2279,5 +2280,10 @@
mHandler.obtainMessage(MSG_INPUT_METHOD_SUBTYPE_CHANGED, userId, 0, someArgs)
.sendToTarget();
}
+
+ @Override
+ public void toggleCapsLock(int deviceId) {
+ nativeToggleCapsLock(mPtr, deviceId);
+ }
}
}
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 6b00f5f..b235002 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -58,8 +58,8 @@
import android.util.TimeUtils;
import com.android.internal.app.IBatteryStats;
+import com.android.internal.app.procstats.ProcessStats;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.app.ProcessStats;
import com.android.server.DeviceIdleController;
import com.android.server.LocalServices;
import com.android.server.job.JobStore.JobStatusFunctor;
@@ -304,7 +304,7 @@
toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());
if (toCancel != null) {
- cancelJobImpl(toCancel);
+ cancelJobImpl(toCancel, jobStatus);
}
startTrackingJob(jobStatus, toCancel);
}
@@ -331,7 +331,7 @@
}
for (int i=0; i<jobsForUser.size(); i++) {
JobStatus toRemove = jobsForUser.get(i);
- cancelJobImpl(toRemove);
+ cancelJobImpl(toRemove, null);
}
}
@@ -360,7 +360,7 @@
} catch (RemoteException e) {
}
}
- cancelJobImpl(toRemove);
+ cancelJobImpl(toRemove, null);
}
}
@@ -377,13 +377,13 @@
toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
}
if (toCancel != null) {
- cancelJobImpl(toCancel);
+ cancelJobImpl(toCancel, null);
}
}
- private void cancelJobImpl(JobStatus cancelled) {
+ private void cancelJobImpl(JobStatus cancelled, JobStatus incomingJob) {
if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
- stopTrackingJob(cancelled, true /* writeBack */);
+ stopTrackingJob(cancelled, incomingJob, true /* writeBack */);
synchronized (mLock) {
// Remove from pending queue.
mPendingJobs.remove(cancelled);
@@ -549,7 +549,7 @@
for (int i = 0; i < mControllers.size(); i++) {
StateController controller = mControllers.get(i);
if (update) {
- controller.maybeStopTrackingJobLocked(jobStatus, true);
+ controller.maybeStopTrackingJobLocked(jobStatus, null, true);
}
controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
}
@@ -561,14 +561,15 @@
* Called when we want to remove a JobStatus object that we've finished executing. Returns the
* object removed.
*/
- private boolean stopTrackingJob(JobStatus jobStatus, boolean writeBack) {
+ private boolean stopTrackingJob(JobStatus jobStatus, JobStatus incomingJob,
+ boolean writeBack) {
synchronized (mLock) {
// Remove from store as well as controllers.
final boolean removed = mJobs.remove(jobStatus, writeBack);
if (removed && mReadyToRock) {
for (int i=0; i<mControllers.size(); i++) {
StateController controller = mControllers.get(i);
- controller.maybeStopTrackingJobLocked(jobStatus, false);
+ controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
}
}
return removed;
@@ -696,7 +697,7 @@
}
// Do not write back immediately if this is a periodic job. The job may get lost if system
// shuts down before it is added back.
- if (!stopTrackingJob(jobStatus, !jobStatus.getJob().isPeriodic())) {
+ if (!stopTrackingJob(jobStatus, null, !jobStatus.getJob().isPeriodic())) {
if (DEBUG) {
Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
}
@@ -780,7 +781,7 @@
}
break;
case MSG_STOP_JOB:
- cancelJobImpl((JobStatus)message.obj);
+ cancelJobImpl((JobStatus)message.obj, null);
break;
}
maybeRunPendingJobsH();
diff --git a/services/core/java/com/android/server/job/controllers/AppIdleController.java b/services/core/java/com/android/server/job/controllers/AppIdleController.java
index 2114fc3..d8490d4 100644
--- a/services/core/java/com/android/server/job/controllers/AppIdleController.java
+++ b/services/core/java/com/android/server/job/controllers/AppIdleController.java
@@ -77,7 +77,7 @@
}
@Override
- public void maybeStopTrackingJobLocked(JobStatus jobStatus, boolean forUpdate) {
+ public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob, boolean forUpdate) {
mTrackedTasks.remove(jobStatus);
}
diff --git a/services/core/java/com/android/server/job/controllers/BatteryController.java b/services/core/java/com/android/server/job/controllers/BatteryController.java
index ac9f425..0772364 100644
--- a/services/core/java/com/android/server/job/controllers/BatteryController.java
+++ b/services/core/java/com/android/server/job/controllers/BatteryController.java
@@ -87,7 +87,7 @@
}
@Override
- public void maybeStopTrackingJobLocked(JobStatus taskStatus, boolean forUpdate) {
+ public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob, boolean forUpdate) {
if (taskStatus.hasChargingConstraint()) {
mTrackedTasks.remove(taskStatus);
}
diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
index 6ef425a..5ad8189 100644
--- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
@@ -92,7 +92,7 @@
}
@Override
- public void maybeStopTrackingJobLocked(JobStatus jobStatus, boolean forUpdate) {
+ public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob, boolean forUpdate) {
if (jobStatus.hasConnectivityConstraint() || jobStatus.hasUnmeteredConstraint()) {
mTrackedJobs.remove(jobStatus);
}
diff --git a/services/core/java/com/android/server/job/controllers/ContentObserverController.java b/services/core/java/com/android/server/job/controllers/ContentObserverController.java
index c5cf30f..b2f1958 100644
--- a/services/core/java/com/android/server/job/controllers/ContentObserverController.java
+++ b/services/core/java/com/android/server/job/controllers/ContentObserverController.java
@@ -84,17 +84,8 @@
boolean havePendingUris = false;
// If there is a previous job associated with the new job, propagate over
// any pending content URI trigger reports.
- if (lastJob != null && lastJob.contentObserverJobInstance != null
- && lastJob.contentObserverJobInstance
- != taskStatus.contentObserverJobInstance
- && lastJob.contentObserverJobInstance.mChangedAuthorities != null) {
+ if (taskStatus.contentObserverJobInstance.mChangedAuthorities != null) {
havePendingUris = true;
- taskStatus.contentObserverJobInstance.mChangedAuthorities
- = lastJob.contentObserverJobInstance.mChangedAuthorities;
- taskStatus.contentObserverJobInstance.mChangedUris
- = lastJob.contentObserverJobInstance.mChangedUris;
- lastJob.contentObserverJobInstance.mChangedAuthorities = null;
- lastJob.contentObserverJobInstance.mChangedUris = null;
}
// If we have previously reported changed authorities/uris, then we failed
// to complete the job with them so will re-record them to report again.
@@ -138,15 +129,34 @@
}
@Override
- public void maybeStopTrackingJobLocked(JobStatus taskStatus, boolean forUpdate) {
+ public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob,
+ boolean forUpdate) {
if (taskStatus.hasContentTriggerConstraint()) {
- if (!forUpdate) {
- // We won't do this reset if being called for an update, because
- // we know it will be immediately followed by maybeStartTrackingJobLocked...
- // and we don't want to lose any content changes in-between.
- if (taskStatus.contentObserverJobInstance != null) {
- taskStatus.contentObserverJobInstance.detach();
- taskStatus.contentObserverJobInstance = null;
+ if (taskStatus.contentObserverJobInstance != null) {
+ if (incomingJob != null && taskStatus.contentObserverJobInstance != null
+ && taskStatus.contentObserverJobInstance.mChangedAuthorities != null) {
+ // We are stopping this job, but it is going to be replaced by this given
+ // incoming job. We want to propagate our state over to it, so we don't
+ // lose any content changes that had happend since the last one started.
+ // If there is a previous job associated with the new job, propagate over
+ // any pending content URI trigger reports.
+ if (incomingJob.contentObserverJobInstance == null) {
+ incomingJob.contentObserverJobInstance = new JobInstance(incomingJob);
+ }
+ incomingJob.contentObserverJobInstance.mChangedAuthorities
+ = taskStatus.contentObserverJobInstance.mChangedAuthorities;
+ incomingJob.contentObserverJobInstance.mChangedUris
+ = taskStatus.contentObserverJobInstance.mChangedUris;
+ taskStatus.contentObserverJobInstance.mChangedAuthorities = null;
+ taskStatus.contentObserverJobInstance.mChangedUris = null;
+ } else {
+ // We won't do this reset if being called for an update, because
+ // we know it will be immediately followed by maybeStartTrackingJobLocked...
+ // and we don't want to lose any content changes in-between.
+ if (taskStatus.contentObserverJobInstance != null) {
+ taskStatus.contentObserverJobInstance.detach();
+ taskStatus.contentObserverJobInstance = null;
+ }
}
}
mTrackedTasks.remove(taskStatus);
diff --git a/services/core/java/com/android/server/job/controllers/DeviceIdleJobsController.java b/services/core/java/com/android/server/job/controllers/DeviceIdleJobsController.java
index d2ef6a2..64887e8 100644
--- a/services/core/java/com/android/server/job/controllers/DeviceIdleJobsController.java
+++ b/services/core/java/com/android/server/job/controllers/DeviceIdleJobsController.java
@@ -166,7 +166,7 @@
}
@Override
- public void maybeStopTrackingJobLocked(JobStatus jobStatus, boolean forUpdate) {
+ public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob, boolean forUpdate) {
mTrackedTasks.remove(jobStatus);
}
diff --git a/services/core/java/com/android/server/job/controllers/IdleController.java b/services/core/java/com/android/server/job/controllers/IdleController.java
index d9eb45c..50aa882 100644
--- a/services/core/java/com/android/server/job/controllers/IdleController.java
+++ b/services/core/java/com/android/server/job/controllers/IdleController.java
@@ -75,7 +75,7 @@
}
@Override
- public void maybeStopTrackingJobLocked(JobStatus taskStatus, boolean forUpdate) {
+ public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob, boolean forUpdate) {
mTrackedTasks.remove(taskStatus);
}
diff --git a/services/core/java/com/android/server/job/controllers/StateController.java b/services/core/java/com/android/server/job/controllers/StateController.java
index ac7f4c3f..0139039 100644
--- a/services/core/java/com/android/server/job/controllers/StateController.java
+++ b/services/core/java/com/android/server/job/controllers/StateController.java
@@ -56,7 +56,8 @@
/**
* Remove task - this will happen if the task is cancelled, completed, etc.
*/
- public abstract void maybeStopTrackingJobLocked(JobStatus jobStatus, boolean forUpdate);
+ public abstract void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
+ boolean forUpdate);
/**
* Called when a new job is being created to reschedule an old failed job.
*/
diff --git a/services/core/java/com/android/server/job/controllers/TimeController.java b/services/core/java/com/android/server/job/controllers/TimeController.java
index 3543249..ab6768e 100644
--- a/services/core/java/com/android/server/job/controllers/TimeController.java
+++ b/services/core/java/com/android/server/job/controllers/TimeController.java
@@ -74,7 +74,7 @@
@Override
public void maybeStartTrackingJobLocked(JobStatus job, JobStatus lastJob) {
if (job.hasTimingDelayConstraint() || job.hasDeadlineConstraint()) {
- maybeStopTrackingJobLocked(job, false);
+ maybeStopTrackingJobLocked(job, null, false);
boolean isInsert = false;
ListIterator<JobStatus> it = mTrackedJobs.listIterator(mTrackedJobs.size());
while (it.hasPrevious()) {
@@ -101,7 +101,7 @@
* Really an == comparison should be enough, but why play with fate? We'll do <=.
*/
@Override
- public void maybeStopTrackingJobLocked(JobStatus job, boolean forUpdate) {
+ public void maybeStopTrackingJobLocked(JobStatus job, JobStatus incomingJob, boolean forUpdate) {
if (mTrackedJobs.remove(job)) {
checkExpiredDelaysAndResetAlarm();
checkExpiredDeadlinesAndResetAlarm();
diff --git a/services/core/java/com/android/server/lights/LightsService.java b/services/core/java/com/android/server/lights/LightsService.java
index 257c7da..5953dde 100644
--- a/services/core/java/com/android/server/lights/LightsService.java
+++ b/services/core/java/com/android/server/lights/LightsService.java
@@ -18,12 +18,17 @@
import com.android.server.SystemService;
import com.android.server.vr.VrManagerInternal;
+import com.android.server.vr.VrManagerService;
import com.android.server.vr.VrStateListener;
import android.content.Context;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Message;
+import android.os.RemoteException;
import android.os.Trace;
+import android.service.vr.IVrManager;
+import android.service.vr.IVrStateCallbacks;
import android.util.Slog;
public class LightsService extends SystemService {
@@ -164,13 +169,19 @@
@Override
public void onBootPhase(int phase) {
if (phase == PHASE_SYSTEM_SERVICES_READY) {
- getLocalService(VrManagerInternal.class).registerListener(mVrStateListener);
+ IVrManager vrManager =
+ (IVrManager) getBinderService(VrManagerService.VR_MANAGER_BINDER_SERVICE);
+ try {
+ vrManager.registerListener(mVrStateCallbacks);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to register VR mode state listener: " + e);
+ }
}
}
- private final VrStateListener mVrStateListener = new VrStateListener() {
+ private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
@Override
- public void onVrStateChanged(boolean enabled) {
+ public void onVrStateChanged(boolean enabled) throws RemoteException {
LightImpl l = mLights[LightsManager.LIGHT_ID_BACKLIGHT];
if (enabled) {
if (DEBUG) Slog.v(TAG, "VR mode enabled, setting brightness to low persistence");
diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java
index 5b1cedc..fc412e3 100644
--- a/services/core/java/com/android/server/net/LockdownVpnTracker.java
+++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java
@@ -20,6 +20,7 @@
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NONE;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
+import static android.provider.Settings.ACTION_VPN_SETTINGS;
import android.app.Notification;
import android.app.NotificationManager;
@@ -66,9 +67,6 @@
private static final String ACTION_LOCKDOWN_RESET = "com.android.server.action.LOCKDOWN_RESET";
- private static final String ACTION_VPN_SETTINGS = "android.net.vpn.SETTINGS";
- private static final String EXTRA_PICK_LOCKDOWN = "android.net.vpn.PICK_LOCKDOWN";
-
private static final int ROOT_UID = 0;
private final Context mContext;
@@ -101,7 +99,6 @@
mProfile = Preconditions.checkNotNull(profile);
final Intent configIntent = new Intent(ACTION_VPN_SETTINGS);
- configIntent.putExtra(EXTRA_PICK_LOCKDOWN, true);
mConfigIntent = PendingIntent.getActivity(mContext, 0, configIntent, 0);
final Intent resetIntent = new Intent(ACTION_LOCKDOWN_RESET);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 49ae293..612bae2 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -49,7 +49,9 @@
import static android.net.NetworkPolicyManager.POLICY_NONE;
import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
+import static android.net.NetworkPolicyManager.RULE_ALLOW_METERED;
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
+import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED;
import static android.net.NetworkPolicyManager.RULE_UNKNOWN;
import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
import static android.net.NetworkTemplate.MATCH_MOBILE_3G_LOWER;
@@ -687,7 +689,7 @@
// global background data policy
if (LOGV) Slog.v(TAG, "ACTION_PACKAGE_ADDED for uid=" + uid);
synchronized (mRulesLock) {
- updateRestrictDataRulesForUidLocked(uid);
+ updateRestrictionRulesForUidLocked(uid);
}
}
}
@@ -705,7 +707,7 @@
if (LOGV) Slog.v(TAG, "ACTION_UID_REMOVED for uid=" + uid);
synchronized (mRulesLock) {
mUidPolicy.delete(uid);
- updateRestrictDataRulesForUidLocked(uid);
+ updateRuleForRestrictBackgroundLocked(uid);
writePolicyLocked();
}
}
@@ -1678,7 +1680,7 @@
mUidPolicy.put(uid, policy);
// uid policy changed, recompute rules and persist policy.
- updateRestrictDataRulesForUidLocked(uid);
+ updateRuleForRestrictBackgroundLocked(uid);
if (persist) {
writePolicyLocked();
}
@@ -1729,7 +1731,7 @@
if (wlUids.length > 0) {
for (int uid : wlUids) {
- removeRestrictBackgroundWhitelistedUidLocked(uid, false);
+ removeRestrictBackgroundWhitelistedUidLocked(uid, false, false);
}
writePolicy = true;
}
@@ -1896,10 +1898,12 @@
try {
maybeRefreshTrustedTime();
synchronized (mRulesLock) {
- mRestrictBackground = restrictBackground;
- updateRulesForRestrictDataLocked();
- updateNotificationsLocked();
- writePolicyLocked();
+ if (restrictBackground == mRestrictBackground) {
+ // Ideally, UI should never allow this scenario...
+ Slog.w(TAG, "setRestrictBackground: already " + restrictBackground);
+ return;
+ }
+ setRestrictBackgroundLocked(restrictBackground);
}
} finally {
@@ -1910,17 +1914,40 @@
.sendToTarget();
}
+ private void setRestrictBackgroundLocked(boolean restrictBackground) {
+ final boolean oldRestrictBackground = mRestrictBackground;
+ mRestrictBackground = restrictBackground;
+ // Must whitelist foreground apps before turning data saver mode on.
+ // TODO: there is no need to iterate through all apps here, just those in the foreground,
+ // so it could call AM to get the UIDs of such apps, and iterate through them instead.
+ updateRulesForRestrictBackgroundLocked();
+ try {
+ if (!mNetworkManager.setDataSaverModeEnabled(mRestrictBackground)) {
+ Slog.e(TAG, "Could not change Data Saver Mode on NMS to " + mRestrictBackground);
+ mRestrictBackground = oldRestrictBackground;
+ // TODO: if it knew the foreground apps (see TODO above), it could call
+ // updateRulesForRestrictBackgroundLocked() again to restore state.
+ return;
+ }
+ } catch (RemoteException e) {
+ // ignored; service lives in system_server
+ }
+ updateNotificationsLocked();
+ writePolicyLocked();
+ }
+
@Override
public void addRestrictBackgroundWhitelistedUid(int uid) {
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
- if (!isUidValidForRules(uid)) return;
- final boolean changed;
+ final boolean oldStatus;
+ final boolean needFirewallRules;
synchronized (mRulesLock) {
- final boolean oldStatus = mRestrictBackgroundWhitelistUids.get(uid);
+ oldStatus = mRestrictBackgroundWhitelistUids.get(uid);
if (oldStatus) {
if (LOGD) Slog.d(TAG, "uid " + uid + " is already whitelisted");
return;
}
+ needFirewallRules = isUidValidForWhitelistRules(uid);
Slog.i(TAG, "adding uid " + uid + " to restrict background whitelist");
mRestrictBackgroundWhitelistUids.append(uid, true);
if (mDefaultRestrictBackgroundWhitelistUids.get(uid)
@@ -1929,13 +1956,14 @@
+ " from revoked restrict background whitelist");
mRestrictBackgroundWhitelistRevokedUids.delete(uid);
}
- changed = mRestrictBackground && !oldStatus;
- if (changed && hasInternetPermissions(uid)) {
- setUidNetworkRules(uid, false);
+ if (needFirewallRules) {
+ // Only update firewall rules if necessary...
+ updateRuleForRestrictBackgroundLocked(uid);
}
+ // ...but always persists the whitelist request.
writePolicyLocked();
}
- if (changed) {
+ if (mRestrictBackground && !oldStatus && needFirewallRules) {
mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_WHITELIST_CHANGED, uid, 0)
.sendToTarget();
}
@@ -1944,10 +1972,9 @@
@Override
public void removeRestrictBackgroundWhitelistedUid(int uid) {
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
- if (!isUidValidForRules(uid)) return;
final boolean changed;
synchronized (mRulesLock) {
- changed = removeRestrictBackgroundWhitelistedUidLocked(uid, true);
+ changed = removeRestrictBackgroundWhitelistedUidLocked(uid, false, true);
}
if (changed) {
mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_WHITELIST_CHANGED, uid, 0)
@@ -1955,14 +1982,19 @@
}
}
- private boolean removeRestrictBackgroundWhitelistedUidLocked(int uid, boolean updateNow) {
+ /**
+ * Removes a uid from the restricted background whitelist, returning whether its current
+ * {@link ConnectivityManager.RestrictBackgroundStatus} changed.
+ */
+ private boolean removeRestrictBackgroundWhitelistedUidLocked(int uid, boolean uidDeleted,
+ boolean updateNow) {
final boolean oldStatus = mRestrictBackgroundWhitelistUids.get(uid);
if (!oldStatus) {
if (LOGD) Slog.d(TAG, "uid " + uid + " was not whitelisted before");
return false;
}
+ final boolean needFirewallRules = uidDeleted || isUidValidForWhitelistRules(uid);
Slog.i(TAG, "removing uid " + uid + " from restrict background whitelist");
- final boolean changed = mRestrictBackground && oldStatus;
mRestrictBackgroundWhitelistUids.delete(uid);
if (mDefaultRestrictBackgroundWhitelistUids.get(uid)
&& !mRestrictBackgroundWhitelistRevokedUids.get(uid)) {
@@ -1970,13 +2002,17 @@
+ " to revoked restrict background whitelist");
mRestrictBackgroundWhitelistRevokedUids.append(uid, true);
}
+ if (needFirewallRules) {
+ // Only update firewall rules if necessary...
+ updateRuleForRestrictBackgroundLocked(uid, uidDeleted);
+ }
if (updateNow) {
- if (changed && hasInternetPermissions(uid)) {
- setUidNetworkRules(uid, true);
- }
+ // ...but always persists the whitelist request.
writePolicyLocked();
}
- return changed;
+ // Status only changes if Data Saver is turned on (otherwise it is DISABLED, even if the
+ // app was whitelisted before).
+ return mRestrictBackground && needFirewallRules;
}
@Override
@@ -2268,11 +2304,16 @@
final int state = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
fout.print(" state=");
fout.print(state);
- fout.print(state <= ActivityManager.PROCESS_STATE_TOP ? " (fg)" : " (bg)");
+ if (state <= ActivityManager.PROCESS_STATE_TOP) {
+ fout.print(" (fg)");
+ } else {
+ fout.print(state <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
+ ? " (fg svc)" : " (bg)");
+ }
final int rule = mUidRules.get(uid, RULE_UNKNOWN);
fout.print(" rule=");
- fout.print(DebugUtils.valueToString(NetworkPolicyManager.class, "RULE_", rule));
+ fout.print(ruleToString(rule));
fout.println();
}
@@ -2280,6 +2321,10 @@
}
}
+ private String ruleToString(int rule) {
+ return DebugUtils.valueToString(NetworkPolicyManager.class, "RULE_", rule);
+ }
+
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ResultReceiver resultReceiver) throws RemoteException {
@@ -2296,57 +2341,80 @@
}
}
- boolean isUidForegroundLocked(int uid) {
+ private boolean isUidForegroundLocked(int uid) {
+ return isUidStateForegroundLocked(
+ mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY));
+ }
+
+ private boolean isUidForegroundOnRestrictBackgroundLocked(int uid) {
+ final int procState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
+ return isProcStateAllowedWhileOnRestrictBackgroundLocked(procState);
+ }
+
+ private boolean isUidStateForegroundLocked(int state) {
// only really in foreground when screen is also on
- return mScreenOn && mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY)
- <= ActivityManager.PROCESS_STATE_TOP;
+ return mScreenOn && state <= ActivityManager.PROCESS_STATE_TOP;
}
/**
* Process state of UID changed; if needed, will trigger
- * {@link #updateRestrictDataRulesForUidLocked(int)}.
+ * {@link #updateRuleForRestrictBackgroundLocked(int)}.
*/
- void updateUidStateLocked(int uid, int uidState) {
+ private void updateUidStateLocked(int uid, int uidState) {
final int oldUidState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
if (oldUidState != uidState) {
// state changed, push updated rules
mUidState.put(uid, uidState);
- updateRulesForUidStateChangeLocked(uid, oldUidState, uidState);
+ updateRestrictBackgroundRulesOnUidStatusChangedLocked(uid, oldUidState, uidState);
if (isProcStateAllowedWhileIdleOrPowerSaveMode(oldUidState)
!= isProcStateAllowedWhileIdleOrPowerSaveMode(uidState) ) {
if (mDeviceIdleMode) {
updateRuleForDeviceIdleLocked(uid);
}
if (mRestrictPower) {
- updateRulesForRestrictPowerLocked(uid);
+ updateRuleForRestrictPowerLocked(uid);
}
}
+ updateNetworkStats(uid, isUidStateForegroundLocked(uidState));
}
}
- void removeUidStateLocked(int uid) {
+ private void removeUidStateLocked(int uid) {
final int index = mUidState.indexOfKey(uid);
if (index >= 0) {
final int oldUidState = mUidState.valueAt(index);
mUidState.removeAt(index);
if (oldUidState != ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
- updateRulesForUidStateChangeLocked(uid, oldUidState,
+ updateRestrictBackgroundRulesOnUidStatusChangedLocked(uid, oldUidState,
ActivityManager.PROCESS_STATE_CACHED_EMPTY);
if (mDeviceIdleMode) {
updateRuleForDeviceIdleLocked(uid);
}
if (mRestrictPower) {
- updateRulesForRestrictPowerLocked(uid);
+ updateRuleForRestrictPowerLocked(uid);
}
+ updateNetworkStats(uid, false);
}
}
}
- void updateRulesForUidStateChangeLocked(int uid, int oldUidState, int newUidState) {
- final boolean oldForeground = oldUidState <= ActivityManager.PROCESS_STATE_TOP;
- final boolean newForeground = newUidState <= ActivityManager.PROCESS_STATE_TOP;
+ // adjust stats accounting based on foreground status
+ private void updateNetworkStats(int uid, boolean uidForeground) {
+ try {
+ mNetworkStats.setUidForeground(uid, uidForeground);
+ } catch (RemoteException e) {
+ // ignored; service lives in system_server
+ }
+ }
+
+ private void updateRestrictBackgroundRulesOnUidStatusChangedLocked(int uid, int oldUidState,
+ int newUidState) {
+ final boolean oldForeground =
+ isProcStateAllowedWhileOnRestrictBackgroundLocked(oldUidState);
+ final boolean newForeground =
+ isProcStateAllowedWhileOnRestrictBackgroundLocked(newUidState);
if (oldForeground != newForeground) {
- updateRestrictDataRulesForUidLocked(uid);
+ updateRuleForRestrictBackgroundLocked(uid);
}
}
@@ -2368,9 +2436,9 @@
// only update rules for anyone with foreground activities
final int size = mUidState.size();
for (int i = 0; i < size; i++) {
- if (mUidState.valueAt(i) <= ActivityManager.PROCESS_STATE_TOP) {
+ if (mUidState.valueAt(i) <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
final int uid = mUidState.keyAt(i);
- updateRestrictDataRulesForUidLocked(uid);
+ updateRestrictionRulesForUidLocked(uid);
}
}
}
@@ -2379,12 +2447,16 @@
return procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
}
+ static boolean isProcStateAllowedWhileOnRestrictBackgroundLocked(int procState) {
+ return procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+ }
+
void updateRulesForRestrictPowerLocked() {
updateRulesForWhitelistedPowerSaveLocked(mRestrictPower, FIREWALL_CHAIN_POWERSAVE,
mUidFirewallPowerSaveRules);
}
- void updateRulesForRestrictPowerLocked(int uid) {
+ void updateRuleForRestrictPowerLocked(int uid) {
updateRulesForWhitelistedPowerSaveLocked(uid, mRestrictPower, FIREWALL_CHAIN_POWERSAVE);
}
@@ -2397,8 +2469,8 @@
updateRulesForWhitelistedPowerSaveLocked(uid, mDeviceIdleMode, FIREWALL_CHAIN_DOZABLE);
}
- // NOTE: since both fw_dozable and fw_powersave uses the same map (mPowerSaveTempWhitelistAppIds)
- // for whitelisting, we can reuse their logic in this method.
+ // NOTE: since both fw_dozable and fw_powersave uses the same map
+ // (mPowerSaveTempWhitelistAppIds) for whitelisting, we can reuse their logic in this method.
private void updateRulesForWhitelistedPowerSaveLocked(boolean enabled, int chain,
SparseIntArray rules) {
if (enabled) {
@@ -2433,8 +2505,8 @@
enableFirewallChainLocked(chain, enabled);
}
- // NOTE: since both fw_dozable and fw_powersave uses the same map (mPowerSaveTempWhitelistAppIds)
- // for whitelisting, we can reuse their logic in this method.
+ // NOTE: since both fw_dozable and fw_powersave uses the same map
+ // (mPowerSaveTempWhitelistAppIds) for whitelisting, we can reuse their logic in this method.
private void updateRulesForWhitelistedPowerSaveLocked(int uid, boolean enabled, int chain) {
if (enabled) {
int appId = UserHandle.getAppId(uid);
@@ -2452,7 +2524,6 @@
uidRules.clear();
// Fully update the app idle firewall chain.
- final IPackageManager ipm = AppGlobals.getPackageManager();
final List<UserInfo> users = mUserManager.getUsers();
for (int ui = users.size() - 1; ui >= 0; ui--) {
UserInfo user = users.get(ui);
@@ -2473,7 +2544,7 @@
}
void updateRuleForAppIdleLocked(int uid) {
- if (!isUidValidForRules(uid)) return;
+ if (!isUidValidForBlacklistRules(uid)) return;
int appId = UserHandle.getAppId(uid);
if (!mPowerSaveTempWhitelistAppIds.get(appId) && isUidIdle(uid)) {
@@ -2499,7 +2570,8 @@
updateRulesForDeviceIdleLocked();
updateRulesForAppIdleLocked();
updateRulesForRestrictPowerLocked();
- updateRulesForRestrictDataLocked();
+ updateRulesForRestrictBackgroundLocked();
+ setRestrictBackgroundLocked(mRestrictBackground);
// If the set of restricted networks may have changed, re-evaluate those.
if (restrictedNetworksChanged) {
@@ -2513,7 +2585,7 @@
}
}
- private void updateRulesForRestrictDataLocked() {
+ private void updateRulesForRestrictBackgroundLocked() {
final PackageManager pm = mContext.getPackageManager();
// update rules for all installed applications
@@ -2530,13 +2602,9 @@
for (int j = 0; j < appsSize; j++) {
final ApplicationInfo app = apps.get(j);
final int uid = UserHandle.getUid(user.id, app.uid);
- updateRestrictDataRulesForUidLocked(uid);
+ updateRuleForRestrictBackgroundLocked(uid);
}
}
-
- // limit data usage for some internal system services
- updateRestrictDataRulesForUidLocked(android.os.Process.MEDIA_UID);
- updateRestrictDataRulesForUidLocked(android.os.Process.DRM_UID);
}
private void updateRulesForTempWhitelistChangeLocked() {
@@ -2548,21 +2616,27 @@
int uid = UserHandle.getUid(user.id, appId);
updateRuleForAppIdleLocked(uid);
updateRuleForDeviceIdleLocked(uid);
- updateRulesForRestrictPowerLocked(uid);
+ updateRuleForRestrictPowerLocked(uid);
}
}
}
- private static boolean isUidValidForRules(int uid) {
+ // TODO: the MEDIA / DRM restriction might not be needed anymore, in which case both
+ // methods below could be merged into a isUidValidForRules() method.
+ private boolean isUidValidForBlacklistRules(int uid) {
// allow rules on specific system services, and any apps
if (uid == android.os.Process.MEDIA_UID || uid == android.os.Process.DRM_UID
- || UserHandle.isApp(uid)) {
+ || (UserHandle.isApp(uid) && hasInternetPermissions(uid))) {
return true;
}
return false;
}
+ private boolean isUidValidForWhitelistRules(int uid) {
+ return UserHandle.isApp(uid) && hasInternetPermissions(uid);
+ }
+
private boolean isUidIdle(int uid) {
final String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
final int userId = UserHandle.getUserId(uid);
@@ -2594,32 +2668,99 @@
}
/**
- * Applies network rules to bandwidth controllers based on uid policy.
+ * Applies network rules to bandwidth and firewall controllers based on uid policy.
*
- * @param uid The uid for which to apply the latest policy
+ * <p>There are currently 2 types of restriction rules:
+ * <ul>
+ * <li>Battery Saver Mode (also referred as power save).
+ * <li>Data Saver Mode (formerly known as restrict background data).
+ * </ul>
*/
- private void updateRestrictDataRulesForUidLocked(int uid) {
- if (!isUidValidForRules(uid) || !hasInternetPermissions(uid)) return;
+ private void updateRestrictionRulesForUidLocked(int uid) {
+ updateRuleForRestrictPowerLocked(uid);
+ updateRuleForRestrictBackgroundLocked(uid);
+ }
+
+ /**
+ * Applies network rules to bandwidth controllers based on process state and user-defined
+ * restrictions (blacklist / whitelist).
+ *
+ * <p>
+ * {@code netd} defines 3 firewall chains that govern whether an app has access to metered
+ * networks:
+ * <ul>
+ * <li>@{code bw_penalty_box}: UIDs added to this chain do not have access (blacklist).
+ * <li>@{code bw_happy_box}: UIDs added to this chain have access (whitelist), unless they're
+ * also blacklisted.
+ * <li>@{code bw_data_saver}: when enabled (through {@link #setRestrictBackground(boolean)}),
+ * no UIDs other those whitelisted will have access.
+ * <ul>
+ *
+ * <p>The @{code bw_penalty_box} and @{code bw_happy_box} are primarily managed through the
+ * {@link #setUidPolicy(int, int)} and {@link #addRestrictBackgroundWhitelistedUid(int)} /
+ * {@link #removeRestrictBackgroundWhitelistedUid(int)} methods (for blacklist and whitelist
+ * respectively): these methods set the proper internal state (blacklist / whitelist), then call
+ * this ({@link #updateRuleForRestrictBackgroundLocked(int)}) to propagate the rules to
+ * {@link INetworkManagementService}, but this method should also be called in events (like
+ * Data Saver Mode flips or UID state changes) that might affect the foreground app, since the
+ * following rules should also be applied:
+ *
+ * <ul>
+ * <li>When Data Saver mode is on, the foreground app should be temporarily added to
+ * {@code bw_happy_box} before the @{code bw_data_saver} chain is enabled.
+ * <li>If the foreground app is blacklisted by the user, it should be temporarily removed from
+ * {@code bw_penalty_box}.
+ * <li>When the app leaves foreground state, the temporary changes above should be reverted.
+ * </ul>
+ *
+ * <p>For optimization, the rules are only applied on user apps that have internet access
+ * permission, since there is no need to change the {@code iptables} rule if the app does not
+ * have permission to use the internet.
+ *
+ * <p>The {@link #mUidRules} map is used to define the transtion of states of an UID.
+ */
+ private void updateRuleForRestrictBackgroundLocked(int uid) {
+ updateRuleForRestrictBackgroundLocked(uid, false);
+ }
+
+ /**
+ * Overloaded version of {@link #updateRuleForRestrictBackgroundLocked(int)} called when an
+ * app is removed - it ignores the UID validity check.
+ */
+ private void updateRuleForRestrictBackgroundLocked(int uid, boolean uidDeleted) {
+ if (!uidDeleted && !isUidValidForWhitelistRules(uid)) {
+ if (LOGD) Slog.d(TAG, "no need to update restrict data rules for uid " + uid);
+ return;
+ }
final int uidPolicy = mUidPolicy.get(uid, POLICY_NONE);
- final boolean uidForeground = isUidForegroundLocked(uid);
+ final boolean isForeground = isUidForegroundOnRestrictBackgroundLocked(uid);
+ final boolean isBlacklisted = (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
+ final boolean isWhitelisted = mRestrictBackgroundWhitelistUids.get(uid);
- // Derive active rules based on policy and active state
int newRule = RULE_ALLOW_ALL;
+ final int oldRule = mUidRules.get(uid);
- if (!uidForeground) {
- // If the app is not in foreground, reject access if:
- // - app is blacklisted by policy or
- // - data saver mode is and app is not whitelisted
- if (((uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0)
- || (mRestrictBackground && !mRestrictBackgroundWhitelistUids.get(uid))) {
+ // First step: define the new rule based on user restrictions and foreground state.
+ if (isForeground) {
+ if (isBlacklisted || (mRestrictBackground && !isWhitelisted)) {
+ newRule = RULE_TEMPORARY_ALLOW_METERED;
+ }
+ } else {
+ if (isBlacklisted) {
newRule = RULE_REJECT_METERED;
+ } else if (isWhitelisted) {
+ newRule = RULE_ALLOW_METERED;
}
}
- final int oldRule = mUidRules.get(uid);
- if (LOGV) Log.v(TAG, "updateBandwithControllerRulesForUidLocked(" + uid + "): oldRule = "
- + oldRule + ", newRule = " + newRule);
+ if (LOGV) {
+ Log.v(TAG, "updateRuleForRestrictBackgroundLocked(" + uid + "):"
+ + " isForeground=" +isForeground + ", isBlacklisted: " + isBlacklisted
+ + ", isWhitelisted: " + isWhitelisted + ", newRule: " + ruleToString(newRule)
+ + ", oldRule: " + ruleToString(oldRule));
+ }
+
if (newRule == RULE_ALLOW_ALL) {
mUidRules.delete(uid);
@@ -2627,20 +2768,55 @@
mUidRules.put(uid, newRule);
}
- final boolean rejectMetered = (newRule == RULE_REJECT_METERED);
- setUidNetworkRules(uid, rejectMetered);
+ // Second step: apply bw changes based on change of state.
+ if (newRule != oldRule) {
+ if (newRule == RULE_TEMPORARY_ALLOW_METERED) {
+ // Temporarily whitelist foreground app, removing from blacklist if necessary
+ // (since bw_penalty_box prevails over bw_happy_box).
- // dispatch changed rule to existing listeners
- if (oldRule != newRule) {
+ setMeteredNetworkWhitelist(uid, true);
+ // TODO: if statement below is used to avoid an unnecessary call to netd / iptables,
+ // but ideally it should be just:
+ // setMeteredNetworkBlacklist(uid, isBlacklisted);
+ if (isBlacklisted) {
+ setMeteredNetworkBlacklist(uid, false);
+ }
+ } else if (oldRule == RULE_TEMPORARY_ALLOW_METERED) {
+ // Remove temporary whitelist from app that is not on foreground anymore.
+
+ // TODO: if statements below are used to avoid unnecessary calls to netd / iptables,
+ // but ideally they should be just:
+ // setMeteredNetworkWhitelist(uid, isWhitelisted);
+ // setMeteredNetworkBlacklist(uid, isBlacklisted);
+ if (!isWhitelisted) {
+ setMeteredNetworkWhitelist(uid, false);
+ }
+ if (isBlacklisted) {
+ setMeteredNetworkBlacklist(uid, true);
+ }
+ } else if (newRule == RULE_REJECT_METERED || oldRule == RULE_REJECT_METERED) {
+ // Flip state because app was explicitly added or removed to blacklist.
+ setMeteredNetworkBlacklist(uid, isBlacklisted);
+ if (oldRule == RULE_REJECT_METERED && isWhitelisted) {
+ // Since blacklist prevails over whitelist, we need to handle the special case
+ // where app is whitelisted and blacklisted at the same time (although such
+ // scenario should be blocked by the UI), then blacklist is removed.
+ setMeteredNetworkWhitelist(uid, isWhitelisted);
+ }
+ } else if (newRule == RULE_ALLOW_METERED || oldRule == RULE_ALLOW_METERED) {
+ // Flip state because app was explicitly added or removed to whitelist.
+ setMeteredNetworkWhitelist(uid, isWhitelisted);
+ } else {
+ // All scenarios should have been covered above
+ Log.wtf(TAG, "Unexpected change of state for " + uid
+ + ": foreground=" + isForeground + ", whitelisted=" + isWhitelisted
+ + ", blacklisted=" + isBlacklisted + ", newRule="
+ + ruleToString(newRule) + ", oldRule=" + ruleToString(oldRule));
+ }
+
+ // dispatch changed rule to existing listeners
mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newRule).sendToTarget();
}
-
- try {
- // adjust stats accounting based on foreground status
- mNetworkStats.setUidForeground(uid, uidForeground);
- } catch (RemoteException e) {
- // ignored; service lives in system_server
- }
}
private class AppIdleStateChangeListener
@@ -2801,11 +2977,23 @@
}
}
- private void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) {
+ private void setMeteredNetworkBlacklist(int uid, boolean enable) {
+ if (LOGV) Slog.v(TAG, "setMeteredNetworkBlacklist " + uid + ": " + enable);
try {
- mNetworkManager.setUidNetworkRules(uid, rejectOnQuotaInterfaces);
+ mNetworkManager.setUidMeteredNetworkBlacklist(uid, enable);
} catch (IllegalStateException e) {
- Log.wtf(TAG, "problem setting uid rules", e);
+ Log.wtf(TAG, "problem setting blacklist (" + enable + ") rules for " + uid, e);
+ } catch (RemoteException e) {
+ // ignored; service lives in system_server
+ }
+ }
+
+ private void setMeteredNetworkWhitelist(int uid, boolean enable) {
+ if (LOGV) Slog.v(TAG, "setMeteredNetworkWhitelist " + uid + ": " + enable);
+ try {
+ mNetworkManager.setUidMeteredNetworkWhitelist(uid, enable);
+ } catch (IllegalStateException e) {
+ Log.wtf(TAG, "problem setting whitelist (" + enable + ") rules for " + uid, e);
} catch (RemoteException e) {
// ignored; service lives in system_server
}
@@ -2987,7 +3175,7 @@
public void onPackageRemoved(String packageName, int uid) {
if (LOGV) Slog.v(TAG, "onPackageRemoved: " + packageName + " ->" + uid);
synchronized (mRulesLock) {
- removeRestrictBackgroundWhitelistedUidLocked(uid, true);
+ removeRestrictBackgroundWhitelistedUidLocked(uid, true, true);
}
}
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
index 9cfb590..d339f69 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
@@ -24,7 +24,6 @@
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -35,7 +34,6 @@
import android.net.NetworkTemplate;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
-import android.os.Binder;
import android.os.RemoteException;
import android.os.ShellCommand;
import android.util.Log;
@@ -88,12 +86,10 @@
pw.println(" Adds a UID to the whitelist for restrict background usage.");
pw.println(" add restrict-background-blacklist UID");
pw.println(" Adds a UID to the blacklist for restrict background usage.");
- pw.println(" get metered-network ID");
- pw.println(" Checks whether the given non-mobile network is metered or not.");
pw.println(" get restrict-background");
pw.println(" Gets the global restrict background usage status.");
- pw.println(" list metered-networks [BOOLEAN]");
- pw.println(" Lists all non-mobile networks and whether they are metered or not.");
+ pw.println(" list wifi-networks [BOOLEAN]");
+ pw.println(" Lists all saved wifi networks and whether they are metered or not.");
pw.println(" If a boolean argument is passed, filters just the metered (or unmetered)");
pw.println(" networks.");
pw.println(" list restrict-background-whitelist");
@@ -105,7 +101,7 @@
pw.println(" remove restrict-background-blacklist UID");
pw.println(" Removes a UID from the blacklist for restrict background usage.");
pw.println(" set metered-network ID BOOLEAN");
- pw.println(" Toggles whether the given non-mobile network is metered.");
+ pw.println(" Toggles whether the given wi-fi network is metered.");
pw.println(" set restrict-background BOOLEAN");
pw.println(" Sets the global restrict background usage status.");
}
@@ -118,8 +114,6 @@
return -1;
}
switch(type) {
- case "metered-network":
- return getMeteredWifiNetwork();
case "restrict-background":
return getRestrictBackground();
}
@@ -152,8 +146,8 @@
return -1;
}
switch(type) {
- case "metered-networks":
- return listMeteredWifiNetworks();
+ case "wifi-networks":
+ return listWifiNetworks();
case "restrict-background-whitelist":
return listRestrictBackgroundWhitelist();
case "restrict-background-blacklist":
@@ -284,7 +278,7 @@
return 0;
}
- private int listMeteredWifiNetworks() throws RemoteException {
+ private int listWifiNetworks() throws RemoteException {
final PrintWriter pw = getOutPrintWriter();
final String arg = getNextArg();
final Boolean filter = arg == null ? null : Boolean.valueOf(arg);
@@ -299,23 +293,6 @@
return 0;
}
- private int getMeteredWifiNetwork() throws RemoteException {
- final PrintWriter pw = getOutPrintWriter();
- final String id = getNextArg();
- if (id == null) {
- pw.println("Error: didn't specify ID");
- return -1;
- }
- final List<NetworkPolicy> policies = getWifiPolicies();
- for (NetworkPolicy policy: policies) {
- if (id.equals(getNetworkId(policy))) {
- pw.println(policy.metered);
- return 0;
- }
- }
- return 0;
- }
-
private int setMeteredWifiNetwork() throws RemoteException {
final PrintWriter pw = getOutPrintWriter();
final String id = getNextArg();
diff --git a/services/core/java/com/android/server/net/NetworkStatsObservers.java b/services/core/java/com/android/server/net/NetworkStatsObservers.java
index 2f55562..6f781b3 100644
--- a/services/core/java/com/android/server/net/NetworkStatsObservers.java
+++ b/services/core/java/com/android/server/net/NetworkStatsObservers.java
@@ -52,7 +52,7 @@
*/
class NetworkStatsObservers {
private static final String TAG = "NetworkStatsObservers";
- private static final boolean LOGV = true;
+ private static final boolean LOGV = false;
private static final long MIN_THRESHOLD_BYTES = 2 * MB_IN_BYTES;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 0f23fde..99c41ea 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -120,6 +120,7 @@
import android.widget.Toast;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.Preconditions;
@@ -226,7 +227,7 @@
private VrManagerInternal mVrManagerInternal;
final IBinder mForegroundToken = new Binder();
- private WorkerHandler mHandler;
+ private Handler mHandler;
private final HandlerThread mRankingThread = new HandlerThread("ranker",
Process.THREAD_PRIORITY_BACKGROUND);
@@ -572,33 +573,9 @@
public void clearEffects() {
synchronized (mNotificationList) {
if (DBG) Slog.d(TAG, "clearEffects");
-
- // sound
- mSoundNotificationKey = null;
-
- long identity = Binder.clearCallingIdentity();
- try {
- final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
- if (player != null) {
- player.stopAsync();
- }
- } catch (RemoteException e) {
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
-
- // vibrate
- mVibrateNotificationKey = null;
- identity = Binder.clearCallingIdentity();
- try {
- mVibrator.cancel();
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
-
- // light
- mLights.clear();
- updateLightsLocked();
+ clearSoundLocked();
+ clearVibrateLocked();
+ clearLightsLocked();
}
}
@@ -658,6 +635,36 @@
}
};
+ private void clearSoundLocked() {
+ mSoundNotificationKey = null;
+ long identity = Binder.clearCallingIdentity();
+ try {
+ final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
+ if (player != null) {
+ player.stopAsync();
+ }
+ } catch (RemoteException e) {
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ private void clearVibrateLocked() {
+ mVibrateNotificationKey = null;
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mVibrator.cancel();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ private void clearLightsLocked() {
+ // light
+ mLights.clear();
+ updateLightsLocked();
+ }
+
private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -764,10 +771,9 @@
cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
REASON_USER_STOPPED, null);
}
- } else if (action.equals(Intent.ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED)) {
- boolean inQuietMode = intent.getBooleanExtra(Intent.EXTRA_QUIET_MODE, false);
+ } else if (action.equals(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)) {
int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
- if (inQuietMode && userHandle >= 0) {
+ if (userHandle >= 0) {
cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
REASON_PROFILE_TURNED_OFF, null);
}
@@ -863,6 +869,26 @@
super(context);
}
+ @VisibleForTesting
+ void setAudioManager(AudioManager audioMananger) {
+ mAudioManager = audioMananger;
+ }
+
+ @VisibleForTesting
+ void setVibrator(Vibrator vibrator) {
+ mVibrator = vibrator;
+ }
+
+ @VisibleForTesting
+ void setSystemReady(boolean systemReady) {
+ mSystemReady = systemReady;
+ }
+
+ @VisibleForTesting
+ void setHandler(Handler handler) {
+ mHandler = handler;
+ }
+
@Override
public void onStart() {
Resources resources = getContext().getResources();
@@ -981,7 +1007,7 @@
filter.addAction(Intent.ACTION_USER_ADDED);
filter.addAction(Intent.ACTION_USER_REMOVED);
filter.addAction(Intent.ACTION_USER_UNLOCKED);
- filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED);
+ filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
getContext().registerReceiver(mIntentReceiver, filter);
IntentFilter pkgFilter = new IntentFilter();
@@ -1852,10 +1878,16 @@
}
private boolean checkPolicyAccess(String pkg) {
- if (PackageManager.PERMISSION_GRANTED == ActivityManager.checkComponentPermission(
- android.Manifest.permission.MANAGE_NOTIFICATIONS, Binder.getCallingUid(),
- -1, true)) {
- return true;
+ try {
+ int uid = getContext().getPackageManager().getPackageUidAsUser(
+ pkg, UserHandle.getCallingUserId());
+ if (PackageManager.PERMISSION_GRANTED == ActivityManager.checkComponentPermission(
+ android.Manifest.permission.MANAGE_NOTIFICATIONS, uid,
+ -1, true)) {
+ return true;
+ }
+ } catch (NameNotFoundException e) {
+ return false;
}
return checkPackagePolicyAccess(pkg) || mListeners.isComponentEnabledForPackage(pkg);
}
@@ -2486,12 +2518,14 @@
return false;
}
- private void buzzBeepBlinkLocked(NotificationRecord record) {
+ @VisibleForTesting
+ void buzzBeepBlinkLocked(NotificationRecord record) {
boolean buzz = false;
boolean beep = false;
boolean blink = false;
final Notification notification = record.sbn.getNotification();
+ final String key = record.getKey();
// Should this notification make noise, vibe, or use the LED?
final boolean aboveThreshold = record.getImportance() >= IMPORTANCE_DEFAULT;
@@ -2515,9 +2549,15 @@
if (disableEffects != null) {
ZenLog.traceDisableEffects(record, disableEffects);
}
+
+ // Remember if this notification already owns the notification channels.
+ boolean wasBeep = key != null && key.equals(mSoundNotificationKey);
+ boolean wasBuzz = key != null && key.equals(mVibrateNotificationKey);
+
+ // These are set inside the conditional if the notification is allowed to make noise.
+ boolean hasValidVibrate = false;
+ boolean hasValidSound = false;
if (disableEffects == null
- && (!(record.isUpdate
- && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
&& (record.getUserId() == UserHandle.USER_ALL ||
record.getUserId() == currentUser ||
mUserProfiles.isCurrentProfile(record.getUserId()))
@@ -2526,10 +2566,6 @@
&& mAudioManager != null) {
if (DBG) Slog.v(TAG, "Interrupting!");
- sendAccessibilityEvent(notification, record.sbn.getPackageName());
-
- // sound
-
// should we use the default notification sound? (indicated either by
// DEFAULT_SOUND or because notification.sound is pointing at
// Settings.System.NOTIFICATION_SOUND)
@@ -2539,8 +2575,6 @@
.equals(notification.sound);
Uri soundUri = null;
- boolean hasValidSound = false;
-
if (useDefaultSound) {
soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
@@ -2553,88 +2587,105 @@
hasValidSound = (soundUri != null);
}
- if (hasValidSound) {
- boolean looping =
- (notification.flags & Notification.FLAG_INSISTENT) != 0;
- AudioAttributes audioAttributes = audioAttributesForNotification(notification);
- mSoundNotificationKey = record.getKey();
- // do not play notifications if stream volume is 0 (typically because
- // ringer mode is silent) or if there is a user of exclusive audio focus
- if ((mAudioManager.getStreamVolume(
- AudioAttributes.toLegacyStreamType(audioAttributes)) != 0)
- && !mAudioManager.isAudioFocusExclusive()) {
- final long identity = Binder.clearCallingIdentity();
- try {
- final IRingtonePlayer player =
- mAudioManager.getRingtonePlayer();
- if (player != null) {
- if (DBG) Slog.v(TAG, "Playing sound " + soundUri
- + " with attributes " + audioAttributes);
- player.playAsync(soundUri, record.sbn.getUser(), looping,
- audioAttributes);
- beep = true;
- }
- } catch (RemoteException e) {
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
- }
-
- // vibrate
// Does the notification want to specify its own vibration?
final boolean hasCustomVibrate = notification.vibrate != null;
// new in 4.2: if there was supposed to be a sound and we're in vibrate
// mode, and no other vibration is specified, we fall back to vibration
final boolean convertSoundToVibration =
- !hasCustomVibrate
- && hasValidSound
- && (mAudioManager.getRingerModeInternal()
- == AudioManager.RINGER_MODE_VIBRATE);
+ !hasCustomVibrate
+ && hasValidSound
+ && (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE);
// The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
final boolean useDefaultVibrate =
(notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
- if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
- && !(mAudioManager.getRingerModeInternal()
- == AudioManager.RINGER_MODE_SILENT)) {
- mVibrateNotificationKey = record.getKey();
+ hasValidVibrate = useDefaultVibrate || convertSoundToVibration ||
+ hasCustomVibrate;
- if (useDefaultVibrate || convertSoundToVibration) {
- // Escalate privileges so we can use the vibrator even if the
- // notifying app does not have the VIBRATE permission.
- long identity = Binder.clearCallingIdentity();
- try {
- mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
- useDefaultVibrate ? mDefaultVibrationPattern
- : mFallbackVibrationPattern,
- ((notification.flags & Notification.FLAG_INSISTENT) != 0)
- ? 0: -1, audioAttributesForNotification(notification));
- buzz = true;
- } finally {
- Binder.restoreCallingIdentity(identity);
+ // We can alert, and we're allowed to alert, but if the developer asked us to only do
+ // it once, and we already have, then don't.
+ if (!(record.isUpdate
+ && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0)) {
+
+ sendAccessibilityEvent(notification, record.sbn.getPackageName());
+
+ if (hasValidSound) {
+ boolean looping =
+ (notification.flags & Notification.FLAG_INSISTENT) != 0;
+ AudioAttributes audioAttributes = audioAttributesForNotification(notification);
+ mSoundNotificationKey = key;
+ // do not play notifications if stream volume is 0 (typically because
+ // ringer mode is silent) or if there is a user of exclusive audio focus
+ if ((mAudioManager.getStreamVolume(
+ AudioAttributes.toLegacyStreamType(audioAttributes)) != 0)
+ && !mAudioManager.isAudioFocusExclusive()) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ final IRingtonePlayer player =
+ mAudioManager.getRingtonePlayer();
+ if (player != null) {
+ if (DBG) Slog.v(TAG, "Playing sound " + soundUri
+ + " with attributes " + audioAttributes);
+ player.playAsync(soundUri, record.sbn.getUser(), looping,
+ audioAttributes);
+ beep = true;
+ }
+ } catch (RemoteException e) {
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
- } else if (notification.vibrate.length > 1) {
- // If you want your own vibration pattern, you need the VIBRATE
- // permission
- mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
- notification.vibrate,
- ((notification.flags & Notification.FLAG_INSISTENT) != 0)
- ? 0: -1, audioAttributesForNotification(notification));
- buzz = true;
+ }
+
+ if (hasValidVibrate && !(mAudioManager.getRingerModeInternal()
+ == AudioManager.RINGER_MODE_SILENT)) {
+ mVibrateNotificationKey = key;
+
+ if (useDefaultVibrate || convertSoundToVibration) {
+ // Escalate privileges so we can use the vibrator even if the
+ // notifying app does not have the VIBRATE permission.
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
+ useDefaultVibrate ? mDefaultVibrationPattern
+ : mFallbackVibrationPattern,
+ ((notification.flags & Notification.FLAG_INSISTENT) != 0)
+ ? 0: -1, audioAttributesForNotification(notification));
+ buzz = true;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ } else if (notification.vibrate.length > 1) {
+ // If you want your own vibration pattern, you need the VIBRATE
+ // permission
+ mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
+ notification.vibrate,
+ ((notification.flags & Notification.FLAG_INSISTENT) != 0)
+ ? 0: -1, audioAttributesForNotification(notification));
+ buzz = true;
+ }
}
}
+
+ }
+ // If a notification is updated to remove the actively playing sound or vibrate,
+ // cancel that feedback now
+ if (wasBeep && !hasValidSound) {
+ clearSoundLocked();
+ }
+ if (wasBuzz && !hasValidVibrate) {
+ clearVibrateLocked();
}
// light
// release the light
- boolean wasShowLights = mLights.remove(record.getKey());
+ boolean wasShowLights = mLights.remove(key);
if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThreshold
&& ((record.getSuppressedVisualEffects()
& NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) == 0)) {
- mLights.add(record.getKey());
+ mLights.add(key);
updateLightsLocked();
if (mUseAttentionLight) {
mAttentionLight.pulse();
@@ -2648,7 +2699,7 @@
& NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) != 0)) {
if (DBG) Slog.v(TAG, "Suppressed SystemUI from triggering screen on");
} else {
- EventLogTags.writeNotificationAlert(record.getKey(),
+ EventLogTags.writeNotificationAlert(key,
buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
mHandler.post(mBuzzBeepBlinked);
}
@@ -2846,9 +2897,10 @@
}
private void scheduleSendRankingUpdate() {
- mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE);
- Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
- mHandler.sendMessage(m);
+ if (!mHandler.hasMessages(MESSAGE_SEND_RANKING_UPDATE)) {
+ Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
+ mHandler.sendMessage(m);
+ }
}
private void handleSendRankingUpdate() {
@@ -3435,6 +3487,9 @@
return AppGlobals.getPackageManager().isPackageSuspendedForUser(pkg, userId);
} catch (RemoteException re) {
throw new SecurityException("Could not talk to package manager service");
+ } catch (IllegalArgumentException ex) {
+ // Package not found.
+ return false;
}
}
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index d449ce5..eae2eaa 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -16,8 +16,6 @@
package com.android.server.pm;
-import static com.android.server.pm.PackageManagerServiceCompilerMapping.REASON_BACKGROUND_DEXOPT;
-
import android.app.AlarmManager;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
@@ -26,7 +24,6 @@
import android.content.ComponentName;
import android.content.Context;
import android.os.ServiceManager;
-import android.os.SystemProperties;
import android.util.ArraySet;
import android.util.Log;
@@ -94,7 +91,7 @@
continue;
}
if (!pm.performDexOpt(pkg, /* instruction set */ null, /* checkProfiles */ true,
- REASON_BACKGROUND_DEXOPT, /* force */ false)) {
+ PackageManagerService.REASON_BACKGROUND_DEXOPT, /* force */ false)) {
// there was a problem running dexopt,
// remember this so we do not keep retrying.
sFailedPackageNames.add(pkg);
diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
index 13a96ae..098b39e 100644
--- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
@@ -247,10 +247,8 @@
}
// SetupWizard
- Intent setupIntent = new Intent(Intent.ACTION_MAIN);
- setupIntent.addCategory(Intent.CATEGORY_SETUP_WIZARD);
- PackageParser.Package setupPackage = getDefaultSystemHandlerActivityPackageLPr(
- setupIntent, userId);
+ PackageParser.Package setupPackage = getSystemPackageLPr(
+ mService.mSetupWizardPackage);
if (setupPackage != null
&& doesPackageSupportRuntimePermissions(setupPackage)) {
grantRuntimePermissionsLPw(setupPackage, PHONE_PERMISSIONS, userId);
@@ -597,6 +595,16 @@
grantRuntimePermissionsLPw(emergencyInfoPckg, PHONE_PERMISSIONS, true, userId);
}
+ // NFC Tag viewer
+ Intent nfcTagIntent = new Intent(Intent.ACTION_VIEW);
+ nfcTagIntent.setType("vnd.android.cursor.item/ndef_msg");
+ PackageParser.Package nfcTagPkg = getDefaultSystemHandlerActivityPackageLPr(
+ nfcTagIntent, userId);
+ if (nfcTagPkg != null
+ && doesPackageSupportRuntimePermissions(nfcTagPkg)) {
+ grantRuntimePermissionsLPw(nfcTagPkg, CONTACTS_PERMISSIONS, false, userId);
+ grantRuntimePermissionsLPw(nfcTagPkg, PHONE_PERMISSIONS, false, userId);
+ }
mService.mSettings.onDefaultRuntimePermissionsGrantedLPr(userId);
}
}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index a1f937ab..7e25632 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -169,8 +169,12 @@
mInstaller.execute("rmpackagedir", packageDir);
}
- public void rmProfiles(String pkgName) throws InstallerException {
- mInstaller.execute("rmprofiles", pkgName);
+ public void clearAppProfiles(String pkgName) throws InstallerException {
+ mInstaller.execute("clear_app_profiles", pkgName);
+ }
+
+ public void destroyAppProfiles(String pkgName) throws InstallerException {
+ mInstaller.execute("destroy_app_profiles", pkgName);
}
public void createUserConfig(int userid) throws InstallerException {
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 2f0532a..4c18e15 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -42,6 +42,7 @@
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
+import android.os.Handler;
import android.os.IInterface;
import android.os.ParcelFileDescriptor;
import android.os.RemoteCallbackList;
@@ -54,10 +55,12 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -75,7 +78,6 @@
@Override
public void onStart() {
- Binder.LOG_RUNTIME_EXCEPTION = true;
publishBinderService(Context.LAUNCHER_APPS_SERVICE, mLauncherAppsImpl);
}
@@ -102,6 +104,8 @@
private final MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
+ private final Handler mCallbackHandler;
+
public LauncherAppsImpl(Context context) {
mContext = context;
mPm = mContext.getPackageManager();
@@ -109,6 +113,7 @@
mShortcutServiceInternal = Preconditions.checkNotNull(
LocalServices.getService(ShortcutServiceInternal.class));
mShortcutServiceInternal.addListener(mPackageMonitor);
+ mCallbackHandler = BackgroundThread.getHandler();
}
@VisibleForTesting
@@ -116,6 +121,21 @@
return getCallingUid();
}
+ final int injectCallingUserId() {
+ return UserHandle.getUserId(injectBinderCallingUid());
+ }
+
+ @VisibleForTesting
+ long injectClearCallingIdentity() {
+ return Binder.clearCallingIdentity();
+ }
+
+ // Injection point.
+ @VisibleForTesting
+ void injectRestoreCallingIdentity(long token) {
+ Binder.restoreCallingIdentity(token);
+ }
+
private int getCallingUserId() {
return UserHandle.getUserId(injectBinderCallingUid());
}
@@ -165,7 +185,7 @@
* Register a receiver to watch for package broadcasts
*/
private void startWatchingPackageBroadcasts() {
- mPackageMonitor.register(mContext, null, UserHandle.ALL, true);
+ mPackageMonitor.register(mContext, UserHandle.ALL, true, mCallbackHandler);
}
/**
@@ -192,14 +212,13 @@
/**
* Checks if the caller is in the same group as the userToCheck.
*/
- @VisibleForTesting // We override it in unit tests
- void ensureInUserProfiles(UserHandle userToCheck, String message) {
- final int callingUserId = UserHandle.getCallingUserId();
+ private void ensureInUserProfiles(UserHandle userToCheck, String message) {
+ final int callingUserId = injectCallingUserId();
final int targetUserId = userToCheck.getIdentifier();
if (targetUserId == callingUserId) return;
- long ident = Binder.clearCallingIdentity();
+ long ident = injectClearCallingIdentity();
try {
UserInfo callingUserInfo = mUm.getUserInfo(callingUserId);
UserInfo targetUserInfo = mUm.getUserInfo(targetUserId);
@@ -209,7 +228,7 @@
throw new SecurityException(message);
}
} finally {
- Binder.restoreCallingIdentity(ident);
+ injectRestoreCallingIdentity(ident);
}
}
@@ -234,12 +253,12 @@
* Checks if the user is enabled.
*/
private boolean isUserEnabled(UserHandle user) {
- long ident = Binder.clearCallingIdentity();
+ long ident = injectClearCallingIdentity();
try {
UserInfo targetUserInfo = mUm.getUserInfo(user.getIdentifier());
return targetUserInfo != null && targetUserInfo.isEnabled();
} finally {
- Binder.restoreCallingIdentity(ident);
+ injectRestoreCallingIdentity(ident);
}
}
@@ -330,75 +349,102 @@
verifyCallingPackage(callingPackage);
ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user);
- if (!mShortcutServiceInternal.hasShortcutHostPermission(callingPackage,
- user.getIdentifier())) {
+ if (!mShortcutServiceInternal.hasShortcutHostPermission(getCallingUserId(),
+ callingPackage)) {
throw new SecurityException("Caller can't access shortcut information");
}
}
@Override
public ParceledListSlice getShortcuts(String callingPackage, long changedSince,
- String packageName, ComponentName componentName, int flags, UserHandle user)
- throws RemoteException {
+ String packageName, ComponentName componentName, int flags, UserHandle user) {
ensureShortcutPermission(callingPackage, user);
+ if (!isUserEnabled(user)) {
+ return new ParceledListSlice<>(new ArrayList(0));
+ }
return new ParceledListSlice<>(
- mShortcutServiceInternal.getShortcuts(callingPackage, changedSince, packageName,
- componentName, flags, user.getIdentifier()));
+ mShortcutServiceInternal.getShortcuts(getCallingUserId(),
+ callingPackage, changedSince, packageName,
+ componentName, flags, user.getIdentifier()));
}
@Override
public ParceledListSlice getShortcutInfo(String callingPackage, String packageName,
- List<String> ids, UserHandle user) throws RemoteException {
+ List<String> ids, UserHandle user) {
ensureShortcutPermission(callingPackage, user);
+ if (!isUserEnabled(user)) {
+ return new ParceledListSlice<>(new ArrayList(0));
+ }
return new ParceledListSlice<>(
- mShortcutServiceInternal.getShortcutInfo(callingPackage, packageName,
- ids, user.getIdentifier()));
+ mShortcutServiceInternal.getShortcutInfo(getCallingUserId(),
+ callingPackage, packageName, ids, user.getIdentifier()));
}
@Override
public void pinShortcuts(String callingPackage, String packageName, List<String> ids,
- UserHandle user) throws RemoteException {
+ UserHandle user) {
ensureShortcutPermission(callingPackage, user);
+ if (!isUserEnabled(user)) {
+ throw new IllegalStateException("Cannot pin shortcuts for disabled profile "
+ + user);
+ }
- mShortcutServiceInternal.pinShortcuts(callingPackage, packageName,
- ids, user.getIdentifier());
+ mShortcutServiceInternal.pinShortcuts(getCallingUserId(),
+ callingPackage, packageName, ids, user.getIdentifier());
}
@Override
public int getShortcutIconResId(String callingPackage, ShortcutInfo shortcut,
UserHandle user) {
ensureShortcutPermission(callingPackage, user);
+ if (!isUserEnabled(user)) {
+ return 0;
+ }
- return mShortcutServiceInternal.getShortcutIconResId(callingPackage, shortcut,
- user.getIdentifier());
+ return mShortcutServiceInternal.getShortcutIconResId(getCallingUserId(),
+ callingPackage, shortcut, user.getIdentifier());
}
@Override
public ParcelFileDescriptor getShortcutIconFd(String callingPackage, ShortcutInfo shortcut,
UserHandle user) {
ensureShortcutPermission(callingPackage, user);
+ if (!isUserEnabled(user)) {
+ return null;
+ }
- return mShortcutServiceInternal.getShortcutIconFd(callingPackage, shortcut,
- user.getIdentifier());
+ return mShortcutServiceInternal.getShortcutIconFd(getCallingUserId(),
+ callingPackage, shortcut, user.getIdentifier());
}
@Override
- public boolean hasShortcutHostPermission(String callingPackage) throws RemoteException {
+ public boolean hasShortcutHostPermission(String callingPackage) {
verifyCallingPackage(callingPackage);
- return mShortcutServiceInternal.hasShortcutHostPermission(callingPackage,
- getCallingUserId());
+ return mShortcutServiceInternal.hasShortcutHostPermission(getCallingUserId(),
+ callingPackage);
}
@Override
public boolean startShortcut(String callingPackage, String packageName, String shortcutId,
- Rect sourceBounds, Bundle startActivityOptions, UserHandle user)
- throws RemoteException {
- ensureShortcutPermission(callingPackage, user);
+ Rect sourceBounds, Bundle startActivityOptions, UserHandle user) {
+ verifyCallingPackage(callingPackage);
+ ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user);
- final Intent intent = mShortcutServiceInternal.createShortcutIntent(callingPackage,
- packageName, shortcutId, user.getIdentifier());
+ if (!isUserEnabled(user)) {
+ throw new IllegalStateException("Cannot start a shortcut for disabled profile "
+ + user);
+ }
+
+ // Even without the permission, pinned shortcuts are always launchable.
+ if (!mShortcutServiceInternal.isPinnedByCaller(getCallingUserId(),
+ callingPackage, packageName, shortcutId, user.getIdentifier())) {
+ ensureShortcutPermission(callingPackage, user);
+ }
+
+ final Intent intent = mShortcutServiceInternal.createShortcutIntent(getCallingUserId(),
+ callingPackage, packageName, shortcutId, user.getIdentifier());
if (intent == null) {
return false;
}
@@ -519,13 +565,13 @@
/** Checks if user is a profile of or same as listeningUser.
* and the user is enabled. */
- boolean isEnabledProfileOf(UserHandle user, UserHandle listeningUser,
+ private boolean isEnabledProfileOf(UserHandle user, UserHandle listeningUser,
String debugMsg) {
if (user.getIdentifier() == listeningUser.getIdentifier()) {
if (DEBUG) Log.d(TAG, "Delivering msg to same user " + debugMsg);
return true;
}
- long ident = Binder.clearCallingIdentity();
+ long ident = injectClearCallingIdentity();
try {
UserInfo userInfo = mUm.getUserInfo(user.getIdentifier());
UserInfo listeningUserInfo = mUm.getUserInfo(listeningUser.getIdentifier());
@@ -546,12 +592,13 @@
return true;
}
} finally {
- Binder.restoreCallingIdentity(ident);
+ injectRestoreCallingIdentity(ident);
}
}
+ @VisibleForTesting
void postToPackageMonitorHandler(Runnable r) {
- mPackageMonitor.getRegisteredHandler().post(r);
+ mCallbackHandler.post(r);
}
private class MyPackageMonitor extends PackageMonitor implements ShortcutChangeListener {
@@ -707,9 +754,11 @@
BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
if (!isEnabledProfileOf(user, cookie.user, "onShortcutChanged")) continue;
+ final int launcherUserId = cookie.user.getIdentifier();
+
// Make sure the caller has the permission.
- if (!mShortcutServiceInternal.hasShortcutHostPermission(cookie.packageName,
- cookie.user.getIdentifier())) {
+ if (!mShortcutServiceInternal.hasShortcutHostPermission(
+ launcherUserId, cookie.packageName)) {
continue;
}
// Each launcher has a different set of pinned shortcuts, so we need to do a
@@ -717,8 +766,9 @@
// (As of now, only one launcher has the permission at a time, so it's bit
// moot, but we may change the permission model eventually.)
final List<ShortcutInfo> list =
- mShortcutServiceInternal.getShortcuts(cookie.packageName,
- /* changedSince= */ 0, packageName, /* component= */ null,
+ mShortcutServiceInternal.getShortcuts(launcherUserId,
+ cookie.packageName,
+ /* changedSince= */ 0, packageName, /* component= */ null,
ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY
| ShortcutQuery.FLAG_GET_PINNED
| ShortcutQuery.FLAG_GET_DYNAMIC
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 03e838b..c3a9226 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -20,7 +20,6 @@
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason;
-import static com.android.server.pm.PackageManagerServiceCompilerMapping.REASON_AB_OTA;
import android.content.Context;
import android.content.pm.IOtaDexopt;
@@ -144,7 +143,7 @@
}
mPackageDexOptimizer.performDexOpt(nextPackage, null /* ISAs */, false /* useProfiles */,
- getCompilerFilterForReason(REASON_AB_OTA));
+ getCompilerFilterForReason(PackageManagerService.REASON_AB_OTA));
}
private void moveAbArtifacts(Installer installer) {
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 5ceb65f..d13f472 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -42,7 +42,7 @@
import static com.android.server.pm.Installer.DEXOPT_SAFEMODE;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
-import static com.android.server.pm.PackageManagerServiceCompilerMapping.getFullCompilerFilter;
+import static com.android.server.pm.PackageManagerServiceCompilerMapping.getNonProfileGuidedCompilerFilter;
/**
* Helper class for running dexopt command on packages.
@@ -138,16 +138,16 @@
boolean isProfileGuidedFilter = DexFile.isProfileGuidedCompilerFilter(targetCompilerFilter);
// If any part of the app is used by other apps, we cannot use profile-guided
// compilation.
- // TODO: This needs to be refactored to be also checked when the target mode is
- // profile-guided.
- if (isProfileGuidedFilter) {
+ // Skip the check for forward locked packages since they don't share their code.
+ if (isProfileGuidedFilter && !pkg.isForwardLocked()) {
for (String path : paths) {
if (isUsedByOtherApps(path)) {
checkProfiles = false;
- // TODO: Should we only upgrade to the non-profile-guided version? That is,
- // given verify-profile, should we move to interpret-only?
- targetCompilerFilter = getFullCompilerFilter();
+ targetCompilerFilter = getNonProfileGuidedCompilerFilter(targetCompilerFilter);
+ if (DexFile.isProfileGuidedCompilerFilter(targetCompilerFilter)) {
+ throw new IllegalStateException(targetCompilerFilter);
+ }
isProfileGuidedFilter = false;
break;
@@ -182,6 +182,10 @@
return DEX_OPT_FAILED;
}
dexoptNeeded = adjustDexoptNeeded(dexoptNeeded);
+ if (PackageManagerService.DEBUG_DEXOPT) {
+ Log.i(TAG, "DexoptNeeded for " + path + "@" + targetCompilerFilter + " is " +
+ dexoptNeeded);
+ }
final String dexoptType;
String oatDir = null;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b73d8f3..31eac1f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -64,6 +64,7 @@
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
+import static android.content.pm.PackageManager.MATCH_FACTORY_ONLY;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
import static android.content.pm.PackageManager.MOVE_FAILED_DEVICE_ADMIN;
@@ -75,6 +76,7 @@
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.pm.PackageParser.PARSE_IS_PRIVILEGED;
import static android.content.pm.PackageParser.isApkFile;
+import static android.os.Process.FIRST_APPLICATION_UID;
import static android.os.Process.PACKAGE_INFO_GID;
import static android.os.Process.SYSTEM_UID;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
@@ -94,11 +96,6 @@
import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet;
import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason;
import static com.android.server.pm.PackageManagerServiceCompilerMapping.getFullCompilerFilter;
-import static com.android.server.pm.PackageManagerServiceCompilerMapping.REASON_BOOT;
-import static com.android.server.pm.PackageManagerServiceCompilerMapping.REASON_FORCED_DEXOPT;
-import static com.android.server.pm.PackageManagerServiceCompilerMapping.REASON_INSTALL;
-import static com.android.server.pm.PackageManagerServiceCompilerMapping.REASON_NON_SYSTEM_LIBRARY;
-import static com.android.server.pm.PackageManagerServiceCompilerMapping.REASON_SHARED_APK;
import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_FAILURE;
import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS;
import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
@@ -111,13 +108,16 @@
import android.app.IActivityManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.IDevicePolicyManager;
+import android.app.admin.SecurityLog;
import android.app.backup.IBackupManager;
+import android.app.usage.UsageStatsManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.IntentFilter.AuthorityEntry;
import android.content.IntentSender;
import android.content.IntentSender.SendIntentException;
import android.content.ServiceConnection;
@@ -150,6 +150,7 @@
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.ActivityIntentInfo;
+import android.content.pm.PackageParser.IntentInfo;
import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.PackageStats;
@@ -323,6 +324,7 @@
private static final boolean DEBUG_INTENT_MATCHING = false;
private static final boolean DEBUG_PACKAGE_SCANNING = false;
private static final boolean DEBUG_VERIFY = false;
+ private static final boolean DEBUG_FILTERS = false;
// Debug output for dexopting. This is shared between PackageManagerService, OtaDexoptService
// and PackageDexOptimizer. All these classes have their own flag to allow switching a single
@@ -448,10 +450,36 @@
sBrowserIntent.setData(Uri.parse("http:"));
}
+ /**
+ * The set of all protected actions [i.e. those actions for which a high priority
+ * intent filter is disallowed].
+ */
+ private static final Set<String> PROTECTED_ACTIONS = new ArraySet<>();
+ static {
+ PROTECTED_ACTIONS.add(Intent.ACTION_SEND);
+ PROTECTED_ACTIONS.add(Intent.ACTION_SENDTO);
+ PROTECTED_ACTIONS.add(Intent.ACTION_SEND_MULTIPLE);
+ PROTECTED_ACTIONS.add(Intent.ACTION_VIEW);
+ }
+
+ // Compilation reasons.
+ public static final int REASON_FIRST_BOOT = 0;
+ public static final int REASON_BOOT = 1;
+ public static final int REASON_INSTALL = 2;
+ public static final int REASON_BACKGROUND_DEXOPT = 3;
+ public static final int REASON_AB_OTA = 4;
+ public static final int REASON_NON_SYSTEM_LIBRARY = 5;
+ public static final int REASON_SHARED_APK = 6;
+ public static final int REASON_FORCED_DEXOPT = 7;
+
+ public static final int REASON_LAST = REASON_FORCED_DEXOPT;
+
final ServiceThread mHandlerThread;
final PackageHandler mHandler;
+ private final ProcessLoggingHandler mProcessLoggingHandler;
+
/**
* Messages for {@link #mHandler} that need to wait for system ready before
* being dispatched.
@@ -467,6 +495,7 @@
final int mDefParseFlags;
final String[] mSeparateProcesses;
final boolean mIsUpgrade;
+ final boolean mIsPreNUpgrade;
/** The location for ASEC container files on internal storage. */
final String mAsecInternalPath;
@@ -519,6 +548,20 @@
* are package location.
*/
final private ArrayMap<String, File> mExpectingBetter = new ArrayMap<>();
+ /**
+ * Tracks high priority intent filters for protected actions. During boot, certain
+ * filter actions are protected and should never be allowed to have a high priority
+ * intent filter for them. However, there is one, and only one exception -- the
+ * setup wizard. It must be able to define a high priority intent filter for these
+ * actions to ensure there are no escapes from the wizard. We need to delay processing
+ * of these during boot as we need to look at all of the system packages in order
+ * to know which component is the setup wizard.
+ */
+ private final List<PackageParser.ActivityIntentInfo> mProtectedFilters = new ArrayList<>();
+ /**
+ * Whether or not processing protected filters should be deferred.
+ */
+ private boolean mDeferProtectedFilters = true;
/**
* Tracks existing system packages prior to receiving an OTA. Keys are package name.
@@ -1023,6 +1066,7 @@
final @Nullable String mRequiredVerifierPackage;
final @Nullable String mRequiredInstallerPackage;
+ final @Nullable String mSetupWizardPackage;
private final PackageUsage mPackageUsage = new PackageUsage();
@@ -1704,6 +1748,8 @@
// Send installed broadcasts if the install/update is not ephemeral
if (!isEphemeral(res.pkg)) {
+ mProcessLoggingHandler.invalidateProcessLoggingBaseApkHash(res.pkg.baseCodePath);
+
// Send added for users that see the package for the first time
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
extras, 0 /*flags*/, null /*targetPackage*/,
@@ -2088,6 +2134,7 @@
Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
mHandlerThread.start();
mHandler = new PackageHandler(mHandlerThread.getLooper());
+ mProcessLoggingHandler = new ProcessLoggingHandler();
Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
File dataDir = Environment.getDataDirectory();
@@ -2200,6 +2247,7 @@
final VersionInfo ver = mSettings.getInternalVersion();
mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint);
+
// when upgrading from pre-M, promote system app permissions from install to runtime
mPromoteSystemApps =
mIsUpgrade && ver.sdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1;
@@ -2216,6 +2264,11 @@
}
}
+ // When upgrading from pre-N, we need to handle package extraction like first boot,
+ // as there is no profiling data available.
+ mIsPreNUpgrade = !mSettings.isNWorkDone();
+ mSettings.setNWorkDone();
+
// Collect vendor overlay packages.
// (Do this before scanning any apps.)
// For security and version matching reason, only consider
@@ -2407,6 +2460,36 @@
}
mExpectingBetter.clear();
+ // Resolve protected action filters. Only the setup wizard is allowed to
+ // have a high priority filter for these actions.
+ mSetupWizardPackage = getSetupWizardPackageName();
+ if (mProtectedFilters.size() > 0) {
+ if (DEBUG_FILTERS && mSetupWizardPackage == null) {
+ Slog.i(TAG, "No setup wizard;"
+ + " All protected intents capped to priority 0");
+ }
+ for (ActivityIntentInfo filter : mProtectedFilters) {
+ if (filter.activity.info.packageName.equals(mSetupWizardPackage)) {
+ if (DEBUG_FILTERS) {
+ Slog.i(TAG, "Found setup wizard;"
+ + " allow priority " + filter.getPriority() + ";"
+ + " package: " + filter.activity.info.packageName
+ + " activity: " + filter.activity.className
+ + " priority: " + filter.getPriority());
+ }
+ // skip setup wizard; allow it to keep the high priority filter
+ continue;
+ }
+ Slog.w(TAG, "Protected action; cap priority to 0;"
+ + " package: " + filter.activity.info.packageName
+ + " activity: " + filter.activity.className
+ + " origPrio: " + filter.getPriority());
+ filter.setPriority(0);
+ }
+ }
+ mDeferProtectedFilters = false;
+ mProtectedFilters.clear();
+
// Now that we know all of the shared libraries, update all clients to have
// the correct library paths.
updateAllSharedLibrariesLPw();
@@ -2866,12 +2949,15 @@
return cur;
}
- PackageInfo generatePackageInfo(PackageParser.Package p, int flags, int userId) {
+ private PackageInfo generatePackageInfo(PackageSetting ps, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
- final PackageSetting ps = (PackageSetting) p.mExtras;
if (ps == null) {
return null;
}
+ final PackageParser.Package p = ps.pkg;
+ if (p == null) {
+ return null;
+ }
final PermissionsState permissionsState = ps.getPermissionsState();
@@ -2893,6 +2979,11 @@
throw new SecurityException("Package " + packageName + " was not found!");
}
+ if (!ps.getInstalled(userId)) {
+ throw new SecurityException(
+ "Package " + packageName + " was not installed for user " + userId + "!");
+ }
+
if (mSafeMode && !ps.isSystem()) {
throw new SecurityException("Package " + packageName + " not a system app!");
}
@@ -2936,14 +3027,28 @@
false /* requireFullPermission */, false /* checkShell */, "get package info");
// reader
synchronized (mPackages) {
- PackageParser.Package p = mPackages.get(packageName);
+ final boolean matchFactoryOnly = (flags & MATCH_FACTORY_ONLY) != 0;
+ PackageParser.Package p = null;
+ if (matchFactoryOnly) {
+ final PackageSetting ps = mSettings.getDisabledSystemPkgLPr(packageName);
+ if (ps != null) {
+ return generatePackageInfo(ps, flags, userId);
+ }
+ }
+ if (p == null) {
+ p = mPackages.get(packageName);
+ if (matchFactoryOnly && !isSystemApp(p)) {
+ return null;
+ }
+ }
if (DEBUG_PACKAGE_INFO)
Log.v(TAG, "getPackageInfo " + packageName + ": " + p);
if (p != null) {
- return generatePackageInfo(p, flags, userId);
+ return generatePackageInfo((PackageSetting)p.mExtras, flags, userId);
}
- if ((flags & MATCH_UNINSTALLED_PACKAGES) != 0) {
- return generatePackageInfoFromSettingsLPw(packageName, flags, userId);
+ if (!matchFactoryOnly && (flags & MATCH_UNINSTALLED_PACKAGES) != 0) {
+ final PackageSetting ps = mSettings.mPackages.get(packageName);
+ return generatePackageInfo(ps, flags, userId);
}
}
return null;
@@ -3104,8 +3209,7 @@
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null) {
if (ps.pkg == null) {
- PackageInfo pInfo = generatePackageInfoFromSettingsLPw(packageName,
- flags, userId);
+ final PackageInfo pInfo = generatePackageInfo(ps, flags, userId);
if (pInfo != null) {
return pInfo.applicationInfo;
}
@@ -3117,31 +3221,6 @@
return null;
}
- private PackageInfo generatePackageInfoFromSettingsLPw(String packageName, int flags,
- int userId) {
- if (!sUserManager.exists(userId)) return null;
- PackageSetting ps = mSettings.mPackages.get(packageName);
- if (ps != null) {
- PackageParser.Package pkg = ps.pkg;
- if (pkg == null) {
- if ((flags & MATCH_UNINSTALLED_PACKAGES) == 0) {
- return null;
- }
- // Only data remains, so we aren't worried about code paths
- pkg = new PackageParser.Package(packageName);
- pkg.applicationInfo.packageName = packageName;
- pkg.applicationInfo.flags = ps.pkgFlags | ApplicationInfo.FLAG_IS_DATA_ONLY;
- pkg.applicationInfo.privateFlags = ps.pkgPrivateFlags;
- pkg.applicationInfo.uid = ps.appId;
- pkg.applicationInfo.initForUser(userId);
- pkg.applicationInfo.primaryCpuAbi = ps.primaryCpuAbiString;
- pkg.applicationInfo.secondaryCpuAbi = ps.secondaryCpuAbiString;
- }
- return generatePackageInfo(pkg, flags, userId);
- }
- return null;
- }
-
@Override
public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
@@ -4919,8 +4998,10 @@
if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Skipping mAlways=false entry");
continue;
}
- final ActivityInfo ai = getActivityInfo(pa.mPref.mComponent,
- flags | MATCH_DISABLED_COMPONENTS, userId);
+ final ActivityInfo ai = getActivityInfo(
+ pa.mPref.mComponent, flags | MATCH_DISABLED_COMPONENTS
+ | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
+ userId);
if (DEBUG_PREFERRED || debug) {
Slog.v(TAG, "Found preferred activity:");
if (ai != null) {
@@ -5888,11 +5969,11 @@
if (listUninstalled) {
list = new ArrayList<PackageInfo>(mSettings.mPackages.size());
for (PackageSetting ps : mSettings.mPackages.values()) {
- PackageInfo pi;
+ final PackageInfo pi;
if (ps.pkg != null) {
- pi = generatePackageInfo(ps.pkg, flags, userId);
+ pi = generatePackageInfo(ps, flags, userId);
} else {
- pi = generatePackageInfoFromSettingsLPw(ps.name, flags, userId);
+ pi = generatePackageInfo(ps, flags, userId);
}
if (pi != null) {
list.add(pi);
@@ -5901,7 +5982,8 @@
} else {
list = new ArrayList<PackageInfo>(mPackages.size());
for (PackageParser.Package p : mPackages.values()) {
- PackageInfo pi = generatePackageInfo(p, flags, userId);
+ final PackageInfo pi =
+ generatePackageInfo((PackageSetting)p.mExtras, flags, userId);
if (pi != null) {
list.add(pi);
}
@@ -5928,11 +6010,11 @@
if (numMatch == 0) {
return;
}
- PackageInfo pi;
+ final PackageInfo pi;
if (ps.pkg != null) {
- pi = generatePackageInfo(ps.pkg, flags, userId);
+ pi = generatePackageInfo(ps, flags, userId);
} else {
- pi = generatePackageInfoFromSettingsLPw(ps.name, flags, userId);
+ pi = generatePackageInfo(ps, flags, userId);
}
// The above might return null in cases of uninstalled apps or install-state
// skew across users/profiles.
@@ -6901,13 +6983,21 @@
}
@Override
- public void extractPackagesIfNeeded() {
- enforceSystemOrRoot("Only the system can request package extraction");
+ public void updatePackagesIfNeeded() {
+ enforceSystemOrRoot("Only the system can request package update");
- // Extract pacakges only if profile-guided compilation is enabled because
- // otherwise BackgroundDexOptService will not dexopt them later.
- boolean prunedCache = VMRuntime.didPruneDalvikCache();
- if (!isUpgrade() && !prunedCache) {
+ // We need to re-extract after an OTA.
+ boolean causeUpgrade = isUpgrade();
+
+ // First boot or factory reset.
+ // Note: we also handle devices that are upgrading to N right now as if it is their
+ // first boot, as they do not have profile data.
+ boolean causeFirstBoot = isFirstBoot() || mIsPreNUpgrade;
+
+ // We need to re-extract after a pruned cache, as AoT-ed files will be out of date.
+ boolean causePrunedCache = VMRuntime.didPruneDalvikCache();
+
+ if (!causeUpgrade && !causeFirstBoot && !causePrunedCache) {
return;
}
@@ -6916,11 +7006,28 @@
pkgs = PackageManagerServiceUtils.getPackagesForDexopt(mPackages.values(), this);
}
+ UsageStatsManager usageMgr =
+ (UsageStatsManager) mContext.getSystemService(Context.USAGE_STATS_SERVICE);
+
int curr = 0;
int total = pkgs.size();
for (PackageParser.Package pkg : pkgs) {
curr++;
+ if (!PackageDexOptimizer.canOptimizePackage(pkg)) {
+ if (DEBUG_DEXOPT) {
+ Log.i(TAG, "Skipping update of of non-optimizable app " + pkg.packageName);
+ }
+ continue;
+ }
+
+ if (!causeFirstBoot && usageMgr.isAppInactive(pkg.packageName)) {
+ if (DEBUG_DEXOPT) {
+ Log.i(TAG, "Skipping update of of idle app " + pkg.packageName);
+ }
+ continue;
+ }
+
if (DEBUG_DEXOPT) {
Log.i(TAG, "Extracting app " + curr + " of " + total + ": " + pkg.packageName);
}
@@ -6934,13 +7041,11 @@
}
}
- if (PackageDexOptimizer.canOptimizePackage(pkg)) {
- // If the cache was pruned, any compiled odex files will likely be out of date
- // and would have to be patched (would be SELF_PATCHOAT, which is deprecated).
- // Instead, force the extraction in this case.
- performDexOpt(pkg.packageName, null /* instructionSet */,
- false /* checkProfiles */, REASON_BOOT, prunedCache);
- }
+ performDexOpt(pkg.packageName,
+ null /* instructionSet */,
+ false /* checkProfiles */,
+ causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT,
+ false /* force */);
}
}
@@ -7197,6 +7302,30 @@
}
}
+ private void deleteProfilesLI(String packageName, boolean destroy) {
+ final PackageParser.Package pkg;
+ synchronized (mPackages) {
+ pkg = mPackages.get(packageName);
+ }
+ if (pkg == null) {
+ Slog.w(TAG, "Failed to delete profiles. No package: " + packageName);
+ return;
+ }
+ deleteProfilesLI(pkg, destroy);
+ }
+
+ private void deleteProfilesLI(PackageParser.Package pkg, boolean destroy) {
+ try {
+ if (destroy) {
+ mInstaller.destroyAppProfiles(pkg.packageName);
+ } else {
+ mInstaller.clearAppProfiles(pkg.packageName);
+ }
+ } catch (InstallerException ex) {
+ Log.e(TAG, "Could not delete profiles for package " + pkg.packageName);
+ }
+ }
+
private void deleteCodeCacheDirsLI(String volumeUuid, String packageName) {
final PackageParser.Package pkg;
synchronized (mPackages) {
@@ -9651,6 +9780,12 @@
// is granted only if it was already granted.
allowed = origPermissions.hasInstallPermission(perm);
}
+ if (!allowed && (bp.protectionLevel & PermissionInfo.PROTECTION_FLAG_SETUP) != 0
+ && pkg.packageName.equals(mSetupWizardPackage)) {
+ // If this permission is to be granted to the system setup wizard and
+ // this app is a setup wizard, then it gets the permission.
+ allowed = true;
+ }
}
return allowed;
}
@@ -9708,8 +9843,314 @@
return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId);
}
+ /**
+ * Finds a privileged activity that matches the specified activity names.
+ */
+ private PackageParser.Activity findMatchingActivity(
+ List<PackageParser.Activity> activityList, ActivityInfo activityInfo) {
+ for (PackageParser.Activity sysActivity : activityList) {
+ if (sysActivity.info.name.equals(activityInfo.name)) {
+ return sysActivity;
+ }
+ if (sysActivity.info.name.equals(activityInfo.targetActivity)) {
+ return sysActivity;
+ }
+ if (sysActivity.info.targetActivity != null) {
+ if (sysActivity.info.targetActivity.equals(activityInfo.name)) {
+ return sysActivity;
+ }
+ if (sysActivity.info.targetActivity.equals(activityInfo.targetActivity)) {
+ return sysActivity;
+ }
+ }
+ }
+ return null;
+ }
+
+ public class IterGenerator<E> {
+ public Iterator<E> generate(ActivityIntentInfo info) {
+ return null;
+ }
+ }
+
+ public class ActionIterGenerator extends IterGenerator<String> {
+ @Override
+ public Iterator<String> generate(ActivityIntentInfo info) {
+ return info.actionsIterator();
+ }
+ }
+
+ public class CategoriesIterGenerator extends IterGenerator<String> {
+ @Override
+ public Iterator<String> generate(ActivityIntentInfo info) {
+ return info.categoriesIterator();
+ }
+ }
+
+ public class SchemesIterGenerator extends IterGenerator<String> {
+ @Override
+ public Iterator<String> generate(ActivityIntentInfo info) {
+ return info.schemesIterator();
+ }
+ }
+
+ public class AuthoritiesIterGenerator extends IterGenerator<IntentFilter.AuthorityEntry> {
+ @Override
+ public Iterator<IntentFilter.AuthorityEntry> generate(ActivityIntentInfo info) {
+ return info.authoritiesIterator();
+ }
+ }
+
+ /**
+ * <em>WARNING</em> for performance reasons, the passed in intentList WILL BE
+ * MODIFIED. Do not pass in a list that should not be changed.
+ */
+ private <T> void getIntentListSubset(List<ActivityIntentInfo> intentList,
+ IterGenerator<T> generator, Iterator<T> searchIterator) {
+ // loop through the set of actions; every one must be found in the intent filter
+ while (searchIterator.hasNext()) {
+ // we must have at least one filter in the list to consider a match
+ if (intentList.size() == 0) {
+ break;
+ }
+
+ final T searchAction = searchIterator.next();
+
+ // loop through the set of intent filters
+ final Iterator<ActivityIntentInfo> intentIter = intentList.iterator();
+ while (intentIter.hasNext()) {
+ final ActivityIntentInfo intentInfo = intentIter.next();
+ boolean selectionFound = false;
+
+ // loop through the intent filter's selection criteria; at least one
+ // of them must match the searched criteria
+ final Iterator<T> intentSelectionIter = generator.generate(intentInfo);
+ while (intentSelectionIter != null && intentSelectionIter.hasNext()) {
+ final T intentSelection = intentSelectionIter.next();
+ if (intentSelection != null && intentSelection.equals(searchAction)) {
+ selectionFound = true;
+ break;
+ }
+ }
+
+ // the selection criteria wasn't found in this filter's set; this filter
+ // is not a potential match
+ if (!selectionFound) {
+ intentIter.remove();
+ }
+ }
+ }
+ }
+
+ private boolean isProtectedAction(ActivityIntentInfo filter) {
+ final Iterator<String> actionsIter = filter.actionsIterator();
+ while (actionsIter != null && actionsIter.hasNext()) {
+ final String filterAction = actionsIter.next();
+ if (PROTECTED_ACTIONS.contains(filterAction)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Adjusts the priority of the given intent filter according to policy.
+ * <p>
+ * <ul>
+ * <li>The priority for non privileged applications is capped to '0'</li>
+ * <li>The priority for protected actions on privileged applications is capped to '0'</li>
+ * <li>The priority for unbundled updates to privileged applications is capped to the
+ * priority defined on the system partition</li>
+ * </ul>
+ * <p>
+ * <em>NOTE:</em> There is one exception. For security reasons, the setup wizard is
+ * allowed to obtain any priority on any action.
+ */
+ private void adjustPriority(
+ List<PackageParser.Activity> systemActivities, ActivityIntentInfo intent) {
+ // nothing to do; priority is fine as-is
+ if (intent.getPriority() <= 0) {
+ return;
+ }
+
+ final ActivityInfo activityInfo = intent.activity.info;
+ final ApplicationInfo applicationInfo = activityInfo.applicationInfo;
+
+ final boolean privilegedApp =
+ ((applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0);
+ if (!privilegedApp) {
+ // non-privileged applications can never define a priority >0
+ Slog.w(TAG, "Non-privileged app; cap priority to 0;"
+ + " package: " + applicationInfo.packageName
+ + " activity: " + intent.activity.className
+ + " origPrio: " + intent.getPriority());
+ intent.setPriority(0);
+ return;
+ }
+
+ if (systemActivities == null) {
+ // the system package is not disabled; we're parsing the system partition
+ if (isProtectedAction(intent)) {
+ if (mDeferProtectedFilters) {
+ // We can't deal with these just yet. No component should ever obtain a
+ // >0 priority for a protected actions, with ONE exception -- the setup
+ // wizard. The setup wizard, however, cannot be known until we're able to
+ // query it for the category CATEGORY_SETUP_WIZARD. Which we can't do
+ // until all intent filters have been processed. Chicken, meet egg.
+ // Let the filter temporarily have a high priority and rectify the
+ // priorities after all system packages have been scanned.
+ mProtectedFilters.add(intent);
+ if (DEBUG_FILTERS) {
+ Slog.i(TAG, "Protected action; save for later;"
+ + " package: " + applicationInfo.packageName
+ + " activity: " + intent.activity.className
+ + " origPrio: " + intent.getPriority());
+ }
+ return;
+ } else {
+ if (DEBUG_FILTERS && mSetupWizardPackage == null) {
+ Slog.i(TAG, "No setup wizard;"
+ + " All protected intents capped to priority 0");
+ }
+ if (intent.activity.info.packageName.equals(mSetupWizardPackage)) {
+ if (DEBUG_FILTERS) {
+ Slog.i(TAG, "Found setup wizard;"
+ + " allow priority " + intent.getPriority() + ";"
+ + " package: " + intent.activity.info.packageName
+ + " activity: " + intent.activity.className
+ + " priority: " + intent.getPriority());
+ }
+ // setup wizard gets whatever it wants
+ return;
+ }
+ Slog.w(TAG, "Protected action; cap priority to 0;"
+ + " package: " + intent.activity.info.packageName
+ + " activity: " + intent.activity.className
+ + " origPrio: " + intent.getPriority());
+ intent.setPriority(0);
+ return;
+ }
+ }
+ // privileged apps on the system image get whatever priority they request
+ return;
+ }
+
+ // privileged app unbundled update ... try to find the same activity
+ final PackageParser.Activity foundActivity =
+ findMatchingActivity(systemActivities, activityInfo);
+ if (foundActivity == null) {
+ // this is a new activity; it cannot obtain >0 priority
+ if (DEBUG_FILTERS) {
+ Slog.i(TAG, "New activity; cap priority to 0;"
+ + " package: " + applicationInfo.packageName
+ + " activity: " + intent.activity.className
+ + " origPrio: " + intent.getPriority());
+ }
+ intent.setPriority(0);
+ return;
+ }
+
+ // found activity, now check for filter equivalence
+
+ // a shallow copy is enough; we modify the list, not its contents
+ final List<ActivityIntentInfo> intentListCopy =
+ new ArrayList<>(foundActivity.intents);
+ final List<ActivityIntentInfo> foundFilters = findFilters(intent);
+
+ // find matching action subsets
+ final Iterator<String> actionsIterator = intent.actionsIterator();
+ if (actionsIterator != null) {
+ getIntentListSubset(
+ intentListCopy, new ActionIterGenerator(), actionsIterator);
+ if (intentListCopy.size() == 0) {
+ // no more intents to match; we're not equivalent
+ if (DEBUG_FILTERS) {
+ Slog.i(TAG, "Mismatched action; cap priority to 0;"
+ + " package: " + applicationInfo.packageName
+ + " activity: " + intent.activity.className
+ + " origPrio: " + intent.getPriority());
+ }
+ intent.setPriority(0);
+ return;
+ }
+ }
+
+ // find matching category subsets
+ final Iterator<String> categoriesIterator = intent.categoriesIterator();
+ if (categoriesIterator != null) {
+ getIntentListSubset(intentListCopy, new CategoriesIterGenerator(),
+ categoriesIterator);
+ if (intentListCopy.size() == 0) {
+ // no more intents to match; we're not equivalent
+ if (DEBUG_FILTERS) {
+ Slog.i(TAG, "Mismatched category; cap priority to 0;"
+ + " package: " + applicationInfo.packageName
+ + " activity: " + intent.activity.className
+ + " origPrio: " + intent.getPriority());
+ }
+ intent.setPriority(0);
+ return;
+ }
+ }
+
+ // find matching schemes subsets
+ final Iterator<String> schemesIterator = intent.schemesIterator();
+ if (schemesIterator != null) {
+ getIntentListSubset(intentListCopy, new SchemesIterGenerator(),
+ schemesIterator);
+ if (intentListCopy.size() == 0) {
+ // no more intents to match; we're not equivalent
+ if (DEBUG_FILTERS) {
+ Slog.i(TAG, "Mismatched scheme; cap priority to 0;"
+ + " package: " + applicationInfo.packageName
+ + " activity: " + intent.activity.className
+ + " origPrio: " + intent.getPriority());
+ }
+ intent.setPriority(0);
+ return;
+ }
+ }
+
+ // find matching authorities subsets
+ final Iterator<IntentFilter.AuthorityEntry>
+ authoritiesIterator = intent.authoritiesIterator();
+ if (authoritiesIterator != null) {
+ getIntentListSubset(intentListCopy,
+ new AuthoritiesIterGenerator(),
+ authoritiesIterator);
+ if (intentListCopy.size() == 0) {
+ // no more intents to match; we're not equivalent
+ if (DEBUG_FILTERS) {
+ Slog.i(TAG, "Mismatched authority; cap priority to 0;"
+ + " package: " + applicationInfo.packageName
+ + " activity: " + intent.activity.className
+ + " origPrio: " + intent.getPriority());
+ }
+ intent.setPriority(0);
+ return;
+ }
+ }
+
+ // we found matching filter(s); app gets the max priority of all intents
+ int cappedPriority = 0;
+ for (int i = intentListCopy.size() - 1; i >= 0; --i) {
+ cappedPriority = Math.max(cappedPriority, intentListCopy.get(i).getPriority());
+ }
+ if (intent.getPriority() > cappedPriority) {
+ if (DEBUG_FILTERS) {
+ Slog.i(TAG, "Found matching filter(s);"
+ + " cap priority to " + cappedPriority + ";"
+ + " package: " + applicationInfo.packageName
+ + " activity: " + intent.activity.className
+ + " origPrio: " + intent.getPriority());
+ }
+ intent.setPriority(cappedPriority);
+ return;
+ }
+ // all this for nothing; the requested priority was <= what was on the system
+ }
+
public final void addActivity(PackageParser.Activity a, String type) {
- final boolean systemApp = a.info.applicationInfo.isSystemApp();
mActivities.put(a.getComponentName(), a);
if (DEBUG_SHOW_INFO)
Log.v(
@@ -9720,10 +10161,12 @@
final int NI = a.intents.size();
for (int j=0; j<NI; j++) {
PackageParser.ActivityIntentInfo intent = a.intents.get(j);
- if (!systemApp && intent.getPriority() > 0 && "activity".equals(type)) {
- intent.setPriority(0);
- Log.w(TAG, "Package " + a.info.applicationInfo.packageName + " has activity "
- + a.className + " with priority > 0, forcing to 0");
+ if ("activity".equals(type)) {
+ final PackageSetting ps =
+ mSettings.getDisabledSystemPkgLPr(intent.activity.info.packageName);
+ final List<PackageParser.Activity> systemActivities =
+ ps != null && ps.pkg != null ? ps.pkg.activities : null;
+ adjustPriority(systemActivities, intent);
}
if (DEBUG_SHOW_INFO) {
Log.v(TAG, " IntentFilter:");
@@ -9873,18 +10316,6 @@
out.println();
}
-// List<ResolveInfo> filterEnabled(List<ResolveInfo> resolveInfoList) {
-// final Iterator<ResolveInfo> i = resolveInfoList.iterator();
-// final List<ResolveInfo> retList = Lists.newArrayList();
-// while (i.hasNext()) {
-// final ResolveInfo resolveInfo = i.next();
-// if (isEnabledLP(resolveInfo.activityInfo)) {
-// retList.add(resolveInfo);
-// }
-// }
-// return retList;
-// }
-
// Keys are String (activity class name), values are Activity.
private final ArrayMap<ComponentName, PackageParser.Activity> mActivities
= new ArrayMap<ComponentName, PackageParser.Activity>();
@@ -10847,7 +11278,10 @@
"isPackageSuspendedForUser for user " + userId);
synchronized (mPackages) {
final PackageSetting pkgSetting = mSettings.mPackages.get(packageName);
- return pkgSetting != null && pkgSetting.getSuspended(userId);
+ if (pkgSetting == null) {
+ throw new IllegalArgumentException("Unknown target package: " + packageName);
+ }
+ return pkgSetting.getSuspended(userId);
}
}
@@ -11233,8 +11667,8 @@
* @return the current "allow unknown sources" setting
*/
private int getUnknownSourcesSettings() {
- return android.provider.Settings.Global.getInt(mContext.getContentResolver(),
- android.provider.Settings.Global.INSTALL_NON_MARKET_APPS,
+ return android.provider.Settings.Secure.getInt(mContext.getContentResolver(),
+ android.provider.Settings.Secure.INSTALL_NON_MARKET_APPS,
-1);
}
@@ -11722,8 +12156,23 @@
// predecessor. As a security measure, this is permited only if this is not a
// version downgrade or if the predecessor package is marked as debuggable and
// a downgrade is explicitly requested.
- if (((dataOwnerPkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0)
- || ((installFlags & PackageManager.INSTALL_ALLOW_DOWNGRADE) == 0)) {
+ //
+ // On debuggable platform builds, downgrades are permitted even for
+ // non-debuggable packages to make testing easier. Debuggable platform builds do
+ // not offer security guarantees and thus it's OK to disable some security
+ // mechanisms to make debugging/testing easier on those builds. However, even on
+ // debuggable builds downgrades of packages are permitted only if requested via
+ // installFlags. This is because we aim to keep the behavior of debuggable
+ // platform builds as close as possible to the behavior of non-debuggable
+ // platform builds.
+ final boolean downgradeRequested =
+ (installFlags & PackageManager.INSTALL_ALLOW_DOWNGRADE) != 0;
+ final boolean packageDebuggable =
+ (dataOwnerPkg.applicationInfo.flags
+ & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+ final boolean downgradePermitted =
+ (downgradeRequested) && ((Build.IS_DEBUGGABLE) || (packageDebuggable));
+ if (!downgradePermitted) {
try {
checkDowngrade(dataOwnerPkg, pkgLite);
} catch (PackageManagerException e) {
@@ -12198,8 +12647,6 @@
* Called after the source arguments are copied. This is used mostly for
* MoveParams when it needs to read the source file to put it in the
* destination.
- *
- * @return
*/
int doPostCopy(int uid) {
return PackageManager.INSTALL_SUCCEEDED;
@@ -13100,6 +13547,7 @@
final PackageParser.Package oldPackage;
final String pkgName = pkg.packageName;
final int[] allUsers;
+ final boolean weFroze;
// First find the old package info and check signatures
synchronized(mPackages) {
@@ -13132,8 +13580,32 @@
// In case of rollback, remember per-user/profile install state
allUsers = sUserManager.getUserIds();
+
+ // Mark the app as frozen to prevent launching during the upgrade
+ // process, and then kill all running instances
+ if (!ps.frozen) {
+ ps.frozen = true;
+ weFroze = true;
+ } else {
+ weFroze = false;
+ }
}
+ try {
+ replacePackageDirtyLI(pkg, oldPackage, parseFlags, scanFlags, user, allUsers,
+ installerPackageName, res);
+ } finally {
+ // Regardless of success or failure of upgrade steps above, always
+ // unfreeze the package if we froze it
+ if (weFroze) {
+ unfreezePackage(pkgName);
+ }
+ }
+ }
+
+ private void replacePackageDirtyLI(PackageParser.Package pkg, PackageParser.Package oldPackage,
+ int parseFlags, int scanFlags, UserHandle user, int[] allUsers,
+ String installerPackageName, PackageInstalledInfo res) {
// Update what is removed
res.removedInfo = new PackageRemovedInfo();
res.removedInfo.uid = oldPackage.applicationInfo.uid;
@@ -13229,6 +13701,7 @@
}
deleteCodeCacheDirsLI(pkg);
+ deleteProfilesLI(pkg, /*destroy*/ false);
try {
final PackageParser.Package newPackage = scanPackageTracedLI(pkg, parseFlags,
@@ -13363,6 +13836,7 @@
// Successfully disabled the old package. Now proceed with re-installation
deleteCodeCacheDirsLI(pkg);
+ deleteProfilesLI(pkg, /*destroy*/ false);
res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
pkg.setApplicationInfoFlags(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP,
@@ -13799,14 +14273,11 @@
}
}
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
try {
PackageParser.collectCertificates(pkg, parseFlags);
} catch (PackageParserException e) {
res.setError("Failed collect during installPackageLI", e);
return;
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
// Get rid of all references to package scan path via parser.
@@ -15202,7 +15673,7 @@
public void clearApplicationProfileData(String packageName) {
enforceSystemOrRoot("Only the system can clear all profile data");
try {
- mInstaller.rmProfiles(packageName);
+ mInstaller.clearAppProfiles(packageName);
} catch (InstallerException ex) {
Log.e(TAG, "Could not clear profile data of package " + packageName);
}
@@ -15418,17 +15889,14 @@
// Otherwise, reset the permission.
final int revokeResult = permissionsState.revokeRuntimePermission(bp, userId);
switch (revokeResult) {
- case PERMISSION_OPERATION_SUCCESS: {
- writeRuntimePermissions = true;
- } break;
-
+ case PERMISSION_OPERATION_SUCCESS:
case PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {
writeRuntimePermissions = true;
final int appId = ps.appId;
mHandler.post(new Runnable() {
@Override
public void run() {
- killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED);
+ killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED);
}
});
} break;
@@ -16543,6 +17011,23 @@
set, comp, userId);
}
+ private @Nullable String getSetupWizardPackageName() {
+ final Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.addCategory(Intent.CATEGORY_SETUP_WIZARD);
+
+ final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null,
+ MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE
+ | MATCH_DISABLED_COMPONENTS,
+ UserHandle.myUserId());
+ if (matches.size() == 1) {
+ return matches.get(0).getComponentInfo().packageName;
+ } else {
+ Slog.e(TAG, "There should probably be exactly one setup wizard; found " + matches.size()
+ + ": matches=" + matches);
+ return null;
+ }
+ }
+
@Override
public void setApplicationEnabledSetting(String appPackageName,
int newState, int flags, int userId, String callingPackage) {
@@ -19377,4 +19862,26 @@
return new ArrayList<>(mPackages.values());
}
}
+
+ /**
+ * Logs process start information (including base APK hash) to the security log.
+ * @hide
+ */
+ public void logAppProcessStartIfNeeded(String processName, int uid, String seinfo,
+ String apkFile, int pid) {
+ if (!SecurityLog.isLoggingEnabled()) {
+ return;
+ }
+ Bundle data = new Bundle();
+ data.putLong("startTimestamp", System.currentTimeMillis());
+ data.putString("processName", processName);
+ data.putInt("uid", uid);
+ data.putString("seinfo", seinfo);
+ data.putString("apkFile", apkFile);
+ data.putInt("pid", pid);
+ Message msg = mProcessLoggingHandler.obtainMessage(
+ ProcessLoggingHandler.LOG_APP_PROCESS_START_MSG);
+ msg.setData(data);
+ mProcessLoggingHandler.sendMessage(msg);
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
index b53f8d1..a7512db 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
@@ -24,25 +24,15 @@
* Manage (retrieve) mappings from compilation reason to compilation filter.
*/
class PackageManagerServiceCompilerMapping {
- // Compilation reasons.
- public static final int REASON_BOOT = 0;
- public static final int REASON_INSTALL = 1;
- public static final int REASON_BACKGROUND_DEXOPT = 2;
- public static final int REASON_AB_OTA = 3;
- public static final int REASON_NON_SYSTEM_LIBRARY = 4;
- public static final int REASON_SHARED_APK = 5;
- public static final int REASON_FORCED_DEXOPT = 6;
-
- private static final int REASON_LAST = REASON_FORCED_DEXOPT;
-
// Names for compilation reasons.
static final String REASON_STRINGS[] = {
- "boot", "install", "bg-dexopt", "ab-ota", "nsys-library", "shared-apk", "forced-dexopt"
+ "first-boot", "boot", "install", "bg-dexopt", "ab-ota", "nsys-library", "shared-apk",
+ "forced-dexopt"
};
// Static block to ensure the strings array is of the right length.
static {
- if (REASON_LAST + 1 != REASON_STRINGS.length) {
+ if (PackageManagerService.REASON_LAST + 1 != REASON_STRINGS.length) {
throw new IllegalStateException("REASON_STRINGS not correct");
}
}
@@ -67,8 +57,8 @@
// Ensure that some reasons are not mapped to profile-guided filters.
switch (reason) {
- case REASON_SHARED_APK:
- case REASON_FORCED_DEXOPT:
+ case PackageManagerService.REASON_SHARED_APK:
+ case PackageManagerService.REASON_FORCED_DEXOPT:
if (DexFile.isProfileGuidedCompilerFilter(sysPropValue)) {
throw new IllegalStateException("\"" + sysPropValue + "\" is profile-guided, "
+ "but not allowed for " + REASON_STRINGS[reason]);
@@ -86,7 +76,7 @@
// overview. Store the exceptions here.
RuntimeException toThrow = null;
- for (int reason = 0; reason <= REASON_LAST; reason++) {
+ for (int reason = 0; reason <= PackageManagerService.REASON_LAST; reason++) {
try {
// Check that the system property name is legal.
String sysPropName = getSystemPropertyName(reason);
@@ -136,4 +126,10 @@
return value;
}
+ /**
+ * Return the non-profile-guided filter corresponding to the given filter.
+ */
+ public static String getNonProfileGuidedCompilerFilter(String filter) {
+ return DexFile.getNonProfileGuidedCompilerFilter(filter);
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 7f626b2..8527fd4 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -190,7 +190,7 @@
pw.println("Package " + packageName + " new suspended state: "
+ mInterface.isPackageSuspendedForUser(packageName, userId));
return 0;
- } catch (RemoteException e) {
+ } catch (RemoteException | IllegalArgumentException e) {
pw.println(e.toString());
return 1;
}
@@ -250,31 +250,34 @@
private int runCompile() throws RemoteException {
final PrintWriter pw = getOutPrintWriter();
- boolean useJitProfiles = false;
+ boolean checkProfiles = SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false);
boolean forceCompilation = false;
boolean allPackages = false;
boolean clearProfileData = false;
String compilerFilter = null;
String compilationReason = null;
+ String checkProfilesRaw = null;
if (peekNextArg() == null) {
// No arguments, show help.
pw.println("Usage: cmd package compile [-c] [-f] [--reset] [-m mode] " +
"[-r reason] [-a|pkg]");
pw.println();
- pw.println(" -c Clear profile data");
- pw.println(" -f Force compilation");
- pw.println(" --reset Reset package");
- pw.println(" -m mode Compilation mode, one of the dex2oat compiler filters");
- pw.println(" verify-none");
- pw.println(" verify-at-runtime");
- pw.println(" verify-profile");
- pw.println(" interpret-only");
- pw.println(" space-profile");
- pw.println(" space");
- pw.println(" speed-profile");
- pw.println(" speed");
- pw.println(" everything");
+ pw.println(" -c Clear profile data");
+ pw.println(" -f Force compilation");
+ pw.println(" --check-prof val Look at profiles when doing dexopt.");
+ pw.println(" Overrides dalvik.vm.usejitprofiles to true of false");
+ pw.println(" --reset Reset package");
+ pw.println(" -m mode Compilation mode, one of the dex2oat compiler filters");
+ pw.println(" verify-none");
+ pw.println(" verify-at-runtime");
+ pw.println(" verify-profile");
+ pw.println(" interpret-only");
+ pw.println(" space-profile");
+ pw.println(" space");
+ pw.println(" speed-profile");
+ pw.println(" speed");
+ pw.println(" everything");
pw.println(" -r reason Compiler reason, one of the package manager reasons");
for (int i = 0; i < PackageManagerServiceCompilerMapping.REASON_STRINGS.length; i++) {
pw.println(" " +
@@ -302,6 +305,9 @@
case "-r":
compilationReason = getNextArgRequired();
break;
+ case "-check-prof":
+ checkProfilesRaw = getNextArgRequired();
+ break;
case "--reset":
forceCompilation = true;
clearProfileData = true;
@@ -313,6 +319,17 @@
}
}
+ if (checkProfilesRaw != null) {
+ if ("true".equals(checkProfilesRaw)) {
+ checkProfiles = true;
+ } else if ("false".equals(checkProfilesRaw)) {
+ checkProfiles = false;
+ } else {
+ pw.println("Invalid value for \"--check-prof\". Expected \"true\" or \"false\".");
+ return 1;
+ }
+ }
+
if (compilerFilter != null && compilationReason != null) {
pw.println("Cannot use compilation filter (\"-m\") and compilation reason (\"-r\") " +
"at the same time");
@@ -331,12 +348,12 @@
// Use the default mode for background dexopt.
targetCompilerFilter =
PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
- PackageManagerServiceCompilerMapping.REASON_BACKGROUND_DEXOPT);
+ PackageManagerService.REASON_BACKGROUND_DEXOPT);
} else if ("reset".equals(compilerFilter)) {
// Use the default mode for install.
targetCompilerFilter =
PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
- PackageManagerServiceCompilerMapping.REASON_INSTALL);
+ PackageManagerService.REASON_INSTALL);
} else {
if (!DexFile.isValidCompilerFilter(compilerFilter)) {
pw.println("Error: \"" + compilerFilter +
@@ -382,7 +399,7 @@
}
boolean result = mInterface.performDexOptMode(packageName, null /* instructionSet */,
- useJitProfiles, targetCompilerFilter, forceCompilation);
+ checkProfiles, targetCompilerFilter, forceCompilation);
if (!result) {
failedPackages.add(packageName);
}
diff --git a/services/core/java/com/android/server/pm/ProcessLoggingHandler.java b/services/core/java/com/android/server/pm/ProcessLoggingHandler.java
new file mode 100644
index 0000000..c47dda4
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ProcessLoggingHandler.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.app.admin.SecurityLog;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+
+import com.android.internal.os.BackgroundThread;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+import android.util.Slog;
+
+public final class ProcessLoggingHandler extends Handler {
+
+ private static final String TAG = "ProcessLoggingHandler";
+ static final int LOG_APP_PROCESS_START_MSG = 1;
+ static final int INVALIDATE_BASE_APK_HASH_MSG = 2;
+
+ private final HashMap<String, String> mProcessLoggingBaseApkHashes = new HashMap();
+
+ ProcessLoggingHandler() {
+ super(BackgroundThread.getHandler().getLooper());
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case LOG_APP_PROCESS_START_MSG: {
+ Bundle bundle = msg.getData();
+ String processName = bundle.getString("processName");
+ int uid = bundle.getInt("uid");
+ String seinfo = bundle.getString("seinfo");
+ String apkFile = bundle.getString("apkFile");
+ int pid = bundle.getInt("pid");
+ long startTimestamp = bundle.getLong("startTimestamp");
+ String apkHash = computeStringHashOfApk(apkFile);
+ SecurityLog.writeEvent(SecurityLog.TAG_APP_PROCESS_START, processName,
+ startTimestamp, uid, pid, seinfo, apkHash);
+ break;
+ }
+ case INVALIDATE_BASE_APK_HASH_MSG: {
+ Bundle bundle = msg.getData();
+ mProcessLoggingBaseApkHashes.remove(bundle.getString("apkFile"));
+ break;
+ }
+ }
+ }
+
+ void invalidateProcessLoggingBaseApkHash(String apkPath) {
+ Bundle data = new Bundle();
+ data.putString("apkFile", apkPath);
+ Message msg = obtainMessage(INVALIDATE_BASE_APK_HASH_MSG);
+ msg.setData(data);
+ sendMessage(msg);
+ }
+
+ private String computeStringHashOfApk(String apkFile) {
+ if (apkFile == null) {
+ return "No APK";
+ }
+ String apkHash = mProcessLoggingBaseApkHashes.get(apkFile);
+ if (apkHash == null) {
+ try {
+ byte[] hash = computeHashOfApkFile(apkFile);
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < hash.length; i++) {
+ sb.append(String.format("%02x", hash[i]));
+ }
+ apkHash = sb.toString();
+ mProcessLoggingBaseApkHashes.put(apkFile, apkHash);
+ } catch (IOException | NoSuchAlgorithmException e) {
+ Slog.w(TAG, "computeStringHashOfApk() failed", e);
+ }
+ }
+ return apkHash != null ? apkHash : "Failed to count APK hash";
+ }
+
+ private byte[] computeHashOfApkFile(String packageArchiveLocation)
+ throws IOException, NoSuchAlgorithmException {
+ MessageDigest md = MessageDigest.getInstance("SHA-256");
+ FileInputStream input = new FileInputStream(new File(packageArchiveLocation));
+ byte[] buffer = new byte[65536];
+ int size;
+ while ((size = input.read(buffer)) > 0) {
+ md.update(buffer, 0, size);
+ }
+ input.close();
+ return md.digest();
+ }
+}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index e66ec3c..3c3c576 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -190,6 +190,7 @@
"all-intent-filter-verifications";
private static final String TAG_DEFAULT_BROWSER = "default-browser";
private static final String TAG_VERSION = "version";
+ private static final String TAG_N_WORK = "n-work";
private static final String ATTR_NAME = "name";
private static final String ATTR_USER = "user";
@@ -214,6 +215,7 @@
private static final String ATTR_VOLUME_UUID = "volumeUuid";
private static final String ATTR_SDK_VERSION = "sdkVersion";
private static final String ATTR_DATABASE_VERSION = "databaseVersion";
+ private static final String ATTR_DONE = "done";
// Bookkeeping for restored permission grants
private static final String TAG_RESTORED_RUNTIME_PERMISSIONS = "restored-perms";
@@ -386,6 +388,17 @@
public final KeySetManagerService mKeySetManagerService = new KeySetManagerService(mPackages);
+ /**
+ * Used to track whether N+ work has been done. This is similar to the file-system level
+ * and denotes that first-boot or upgrade-to-N work has been done.
+ *
+ * Note: the flag has been added to a) allow tracking while an API level check is impossible
+ * and b) to merge upgrade as well as first boot (because the flag is false, by default).
+ *
+ * STOPSHIP: b/27872764
+ */
+ private boolean mIsNWorkDone = false;
+
Settings(Object lock) {
this(Environment.getDataDirectory(), lock);
}
@@ -2327,6 +2340,10 @@
mKeySetManagerService.writeKeySetManagerServiceLPr(serializer);
+ serializer.startTag(null, TAG_N_WORK);
+ serializer.attribute(null, ATTR_DONE, Boolean.toString(mIsNWorkDone));
+ serializer.endTag(null, TAG_N_WORK);
+
serializer.endTag(null, "packages");
serializer.endDocument();
@@ -2860,7 +2877,8 @@
ver.sdkVersion = XmlUtils.readIntAttribute(parser, ATTR_SDK_VERSION);
ver.databaseVersion = XmlUtils.readIntAttribute(parser, ATTR_SDK_VERSION);
ver.fingerprint = XmlUtils.readStringAttribute(parser, ATTR_FINGERPRINT);
-
+ } else if (TAG_N_WORK.equals(tagName)) {
+ mIsNWorkDone = XmlUtils.readBooleanAttribute(parser, ATTR_DONE, false);
} else {
Slog.w(PackageManagerService.TAG, "Unknown element under <packages>: "
+ parser.getName());
@@ -4140,6 +4158,14 @@
return res;
}
+ public boolean isNWorkDone() {
+ return mIsNWorkDone;
+ }
+
+ void setNWorkDone() {
+ mIsNWorkDone = true;
+ }
+
static void printFlags(PrintWriter pw, int val, Object[] spec) {
pw.print("[ ");
for (int i=0; i<spec.length; i+=2) {
@@ -4456,7 +4482,8 @@
}
}
- if ((permissionNames != null || dumpAll) && ps.pkg.requestedPermissions != null
+ if ((permissionNames != null || dumpAll) && ps.pkg != null
+ && ps.pkg.requestedPermissions != null
&& ps.pkg.requestedPermissions.size() > 0) {
final ArrayList<String> perms = ps.pkg.requestedPermissions;
pw.print(prefix); pw.println(" requested permissions:");
diff --git a/services/core/java/com/android/server/pm/ShortcutLauncher.java b/services/core/java/com/android/server/pm/ShortcutLauncher.java
index f1920c7..c6d66fe 100644
--- a/services/core/java/com/android/server/pm/ShortcutLauncher.java
+++ b/services/core/java/com/android/server/pm/ShortcutLauncher.java
@@ -20,6 +20,10 @@
import android.content.pm.ShortcutInfo;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.pm.ShortcutUser.PackageWithUser;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -27,12 +31,13 @@
import java.io.IOException;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.List;
/**
* Launcher information used by {@link ShortcutService}.
*/
-class ShortcutLauncher {
+class ShortcutLauncher extends ShortcutPackageItem {
private static final String TAG = ShortcutService.TAG;
static final String TAG_ROOT = "launcher-pins";
@@ -40,38 +45,70 @@
private static final String TAG_PACKAGE = "package";
private static final String TAG_PIN = "pin";
+ private static final String ATTR_LAUNCHER_USER_ID = "launcher-user";
private static final String ATTR_VALUE = "value";
private static final String ATTR_PACKAGE_NAME = "package-name";
+ private static final String ATTR_PACKAGE_USER_ID = "package-user";
- @UserIdInt
- final int mUserId;
-
- @NonNull
- final String mPackageName;
+ private final int mOwnerUserId;
/**
* Package name -> IDs.
*/
- final private ArrayMap<String, ArraySet<String>> mPinnedShortcuts = new ArrayMap<>();
+ final private ArrayMap<PackageWithUser, ArraySet<String>> mPinnedShortcuts = new ArrayMap<>();
- ShortcutLauncher(@UserIdInt int userId, @NonNull String packageName) {
- mUserId = userId;
- mPackageName = packageName;
+ private ShortcutLauncher(@UserIdInt int ownerUserId, @NonNull String packageName,
+ @UserIdInt int launcherUserId, ShortcutPackageInfo spi) {
+ super(launcherUserId, packageName, spi != null ? spi : ShortcutPackageInfo.newEmpty());
+ mOwnerUserId = ownerUserId;
}
- public void pinShortcuts(@NonNull ShortcutService s, @NonNull String packageName,
- @NonNull List<String> ids) {
+ public ShortcutLauncher(@UserIdInt int ownerUserId, @NonNull String packageName,
+ @UserIdInt int launcherUserId) {
+ this(ownerUserId, packageName, launcherUserId, null);
+ }
+
+ @Override
+ public int getOwnerUserId() {
+ return mOwnerUserId;
+ }
+
+ /**
+ * Called when the new package can't receive the backup, due to signature or version mismatch.
+ */
+ @Override
+ protected void onRestoreBlocked(ShortcutService s) {
+ final ArrayList<PackageWithUser> pinnedPackages =
+ new ArrayList<>(mPinnedShortcuts.keySet());
+ mPinnedShortcuts.clear();
+ for (int i = pinnedPackages.size() - 1; i >= 0; i--) {
+ final PackageWithUser pu = pinnedPackages.get(i);
+ s.getPackageShortcutsLocked(pu.packageName, pu.userId)
+ .refreshPinnedFlags(s);
+ }
+ }
+
+ @Override
+ protected void onRestored(ShortcutService s) {
+ // Nothing to do.
+ }
+
+ public void pinShortcuts(@NonNull ShortcutService s, @UserIdInt int packageUserId,
+ @NonNull String packageName, @NonNull List<String> ids) {
+ final ShortcutPackage packageShortcuts =
+ s.getPackageShortcutsLocked(packageName, packageUserId);
+
+ final PackageWithUser pu = PackageWithUser.of(packageUserId, packageName);
+
final int idSize = ids.size();
if (idSize == 0) {
- mPinnedShortcuts.remove(packageName);
+ mPinnedShortcuts.remove(pu);
} else {
- final ArraySet<String> prevSet = mPinnedShortcuts.get(packageName);
+ final ArraySet<String> prevSet = mPinnedShortcuts.get(pu);
// Pin shortcuts. Make sure only pin the ones that were visible to the caller.
// i.e. a non-dynamic, pinned shortcut by *other launchers* shouldn't be pinned here.
- final ShortcutPackage packageShortcuts =
- s.getPackageShortcutsLocked(packageName, mUserId);
final ArraySet<String> newSet = new ArraySet<>();
for (int i = 0; i < idSize; i++) {
@@ -84,39 +121,49 @@
newSet.add(id);
}
}
- mPinnedShortcuts.put(packageName, newSet);
+ mPinnedShortcuts.put(pu, newSet);
}
- s.getPackageShortcutsLocked(packageName, mUserId).refreshPinnedFlags(s);
+ packageShortcuts.refreshPinnedFlags(s);
}
/**
* Return the pinned shortcut IDs for the publisher package.
*/
- public ArraySet<String> getPinnedShortcutIds(@NonNull String packageName) {
- return mPinnedShortcuts.get(packageName);
+ public ArraySet<String> getPinnedShortcutIds(@NonNull String packageName,
+ @UserIdInt int packageUserId) {
+ return mPinnedShortcuts.get(PackageWithUser.of(packageUserId, packageName));
}
- boolean cleanUpPackage(String packageName) {
- return mPinnedShortcuts.remove(packageName) != null;
+ boolean cleanUpPackage(String packageName, @UserIdInt int packageUserId) {
+ return mPinnedShortcuts.remove(PackageWithUser.of(packageUserId, packageName)) != null;
}
/**
* Persist.
*/
- public void saveToXml(XmlSerializer out) throws IOException {
+ @Override
+ public void saveToXml(XmlSerializer out, boolean forBackup)
+ throws IOException {
final int size = mPinnedShortcuts.size();
if (size == 0) {
return; // Nothing to write.
}
out.startTag(null, TAG_ROOT);
- ShortcutService.writeAttr(out, ATTR_PACKAGE_NAME,
- mPackageName);
+ ShortcutService.writeAttr(out, ATTR_PACKAGE_NAME, getPackageName());
+ ShortcutService.writeAttr(out, ATTR_LAUNCHER_USER_ID, getPackageUserId());
+ getPackageInfo().saveToXml(out);
for (int i = 0; i < size; i++) {
+ final PackageWithUser pu = mPinnedShortcuts.keyAt(i);
+
+ if (forBackup && (pu.userId != getOwnerUserId())) {
+ continue; // Target package on a different user, skip. (i.e. work profile)
+ }
+
out.startTag(null, TAG_PACKAGE);
- ShortcutService.writeAttr(out, ATTR_PACKAGE_NAME,
- mPinnedShortcuts.keyAt(i));
+ ShortcutService.writeAttr(out, ATTR_PACKAGE_NAME, pu.packageName);
+ ShortcutService.writeAttr(out, ATTR_PACKAGE_USER_ID, pu.userId);
final ArraySet<String> ids = mPinnedShortcuts.valueAt(i);
final int idSize = ids.size();
@@ -132,12 +179,18 @@
/**
* Load.
*/
- public static ShortcutLauncher loadFromXml(XmlPullParser parser, int userId)
- throws IOException, XmlPullParserException {
+ public static ShortcutLauncher loadFromXml(XmlPullParser parser, int ownerUserId,
+ boolean fromBackup) throws IOException, XmlPullParserException {
final String launcherPackageName = ShortcutService.parseStringAttribute(parser,
ATTR_PACKAGE_NAME);
- final ShortcutLauncher ret = new ShortcutLauncher(userId, launcherPackageName);
+ // If restoring, just use the real user ID.
+ final int launcherUserId =
+ fromBackup ? ownerUserId
+ : ShortcutService.parseIntAttribute(parser, ATTR_LAUNCHER_USER_ID, ownerUserId);
+
+ final ShortcutLauncher ret = new ShortcutLauncher(launcherUserId, launcherPackageName,
+ launcherUserId);
ArraySet<String> ids = null;
final int outerDepth = parser.getDepth();
@@ -149,21 +202,37 @@
}
final int depth = parser.getDepth();
final String tag = parser.getName();
- switch (tag) {
- case TAG_PACKAGE: {
- final String packageName = ShortcutService.parseStringAttribute(parser,
- ATTR_PACKAGE_NAME);
- ids = new ArraySet<>();
- ret.mPinnedShortcuts.put(packageName, ids);
- continue;
- }
- case TAG_PIN: {
- ids.add(ShortcutService.parseStringAttribute(parser,
- ATTR_VALUE));
- continue;
+ if (depth == outerDepth + 1) {
+ switch (tag) {
+ case ShortcutPackageInfo.TAG_ROOT:
+ ret.getPackageInfo().loadFromXml(parser, fromBackup);
+ continue;
+ case TAG_PACKAGE: {
+ final String packageName = ShortcutService.parseStringAttribute(parser,
+ ATTR_PACKAGE_NAME);
+ final int packageUserId = fromBackup ? ownerUserId
+ : ShortcutService.parseIntAttribute(parser,
+ ATTR_PACKAGE_USER_ID, ownerUserId);
+ ids = new ArraySet<>();
+ ret.mPinnedShortcuts.put(
+ PackageWithUser.of(packageUserId, packageName), ids);
+ continue;
+ }
}
}
- throw ShortcutService.throwForInvalidTag(depth, tag);
+ if (depth == outerDepth + 2) {
+ switch (tag) {
+ case TAG_PIN: {
+ if (ids == null) {
+ Slog.w(TAG, TAG_PIN + " in invalid place");
+ } else {
+ ids.add(ShortcutService.parseStringAttribute(parser, ATTR_VALUE));
+ }
+ continue;
+ }
+ }
+ }
+ ShortcutService.warnForInvalidTag(depth, tag);
}
return ret;
}
@@ -173,27 +242,43 @@
pw.print(prefix);
pw.print("Launcher: ");
- pw.print(mPackageName);
+ pw.print(getPackageName());
+ pw.print(" Package user: ");
+ pw.print(getPackageUserId());
+ pw.print(" Owner user: ");
+ pw.print(getOwnerUserId());
+ pw.println();
+
+ getPackageInfo().dump(s, pw, prefix + " ");
pw.println();
final int size = mPinnedShortcuts.size();
for (int i = 0; i < size; i++) {
pw.println();
+ final PackageWithUser pu = mPinnedShortcuts.keyAt(i);
+
pw.print(prefix);
pw.print(" ");
pw.print("Package: ");
- pw.println(mPinnedShortcuts.keyAt(i));
+ pw.print(pu.packageName);
+ pw.print(" User: ");
+ pw.println(pu.userId);
final ArraySet<String> ids = mPinnedShortcuts.valueAt(i);
final int idSize = ids.size();
for (int j = 0; j < idSize; j++) {
pw.print(prefix);
- pw.print(" ");
+ pw.print(" Pinned: ");
pw.print(ids.valueAt(j));
pw.println();
}
}
}
+
+ @VisibleForTesting
+ ArraySet<String> getAllPinnedShortcutsForTest(String packageName, int packageUserId) {
+ return new ArraySet<>(mPinnedShortcuts.get(PackageWithUser.of(packageUserId, packageName)));
+ }
}
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index d614251..1076a7a 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -17,7 +17,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
@@ -27,6 +26,8 @@
import android.util.ArraySet;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -35,13 +36,14 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import java.util.function.Predicate;
/**
* Package information used by {@link ShortcutService}.
*/
-class ShortcutPackage {
+class ShortcutPackage extends ShortcutPackageItem {
private static final String TAG = ShortcutService.TAG;
static final String TAG_ROOT = "package";
@@ -56,6 +58,7 @@
private static final String ATTR_ID = "id";
private static final String ATTR_ACTIVITY = "activity";
private static final String ATTR_TITLE = "title";
+ private static final String ATTR_TEXT = "text";
private static final String ATTR_INTENT = "intent";
private static final String ATTR_WEIGHT = "weight";
private static final String ATTR_TIMESTAMP = "timestamp";
@@ -63,12 +66,6 @@
private static final String ATTR_ICON_RES = "icon-res";
private static final String ATTR_BITMAP_PATH = "bitmap-path";
- @UserIdInt
- final int mUserId;
-
- @NonNull
- final String mPackageName;
-
/**
* All the shortcuts from the package, keyed on IDs.
*/
@@ -89,11 +86,36 @@
*/
private long mLastResetTime;
- ShortcutPackage(int userId, String packageName) {
- mUserId = userId;
- mPackageName = packageName;
+ private ShortcutPackage(int packageUserId, String packageName, ShortcutPackageInfo spi) {
+ super(packageUserId, packageName, spi != null ? spi : ShortcutPackageInfo.newEmpty());
}
+ public ShortcutPackage(int packageUserId, String packageName) {
+ this(packageUserId, packageName, null);
+ }
+
+ @Override
+ public int getOwnerUserId() {
+ // For packages, always owner user == package user.
+ return getPackageUserId();
+ }
+
+ @Override
+ protected void onRestoreBlocked(ShortcutService s) {
+ // Can't restore due to version/signature mismatch. Remove all shortcuts.
+ mShortcuts.clear();
+ }
+
+ @Override
+ protected void onRestored(ShortcutService s) {
+ // Because some launchers may not have been restored (e.g. allowBackup=false),
+ // we need to re-calculate the pinned shortcuts.
+ refreshPinnedFlags(s);
+ }
+
+ /**
+ * Note this does *not* provide a correct view to the calling launcher.
+ */
@Nullable
public ShortcutInfo findShortcutById(String id) {
return mShortcuts.get(id);
@@ -103,7 +125,7 @@
@NonNull String id) {
final ShortcutInfo shortcut = mShortcuts.remove(id);
if (shortcut != null) {
- s.removeIcon(mUserId, shortcut);
+ s.removeIcon(getPackageUserId(), shortcut);
shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_PINNED);
}
return shortcut;
@@ -111,7 +133,7 @@
void addShortcut(@NonNull ShortcutService s, @NonNull ShortcutInfo newShortcut) {
deleteShortcut(s, newShortcut.getId());
- s.saveIconAndFixUpShortcut(mUserId, newShortcut);
+ s.saveIconAndFixUpShortcut(getPackageUserId(), newShortcut);
mShortcuts.put(newShortcut.getId(), newShortcut);
}
@@ -219,23 +241,30 @@
}
// Then, for the pinned set for each launcher, set the pin flag one by one.
- final ArrayMap<String, ShortcutLauncher> launchers =
- s.getUserShortcutsLocked(mUserId).getLaunchers();
+ final ArrayMap<ShortcutUser.PackageWithUser, ShortcutLauncher> launchers =
+ s.getUserShortcutsLocked(getPackageUserId()).getAllLaunchers();
for (int l = launchers.size() - 1; l >= 0; l--) {
+ // Note even if a launcher that hasn't been installed can still pin shortcuts.
+
final ShortcutLauncher launcherShortcuts = launchers.valueAt(l);
- final ArraySet<String> pinned = launcherShortcuts.getPinnedShortcutIds(mPackageName);
+ final ArraySet<String> pinned = launcherShortcuts.getPinnedShortcutIds(
+ getPackageName(), getPackageUserId());
if (pinned == null || pinned.size() == 0) {
continue;
}
for (int i = pinned.size() - 1; i >= 0; i--) {
- final ShortcutInfo si = mShortcuts.get(pinned.valueAt(i));
+ final String id = pinned.valueAt(i);
+ final ShortcutInfo si = mShortcuts.get(id);
if (si == null) {
- s.wtf("Shortcut not found");
- } else {
- si.addFlags(ShortcutInfo.FLAG_PINNED);
+ // This happens if a launcher pinned shortcuts from this package, then backup&
+ // restored, but this package doesn't allow backing up.
+ // In that case the launcher ends up having a dangling pinned shortcuts.
+ // That's fine, when the launcher is restored, we'll fix it.
+ continue;
}
+ si.addFlags(ShortcutInfo.FLAG_PINNED);
}
}
@@ -291,13 +320,29 @@
* Find all shortcuts that match {@code query}.
*/
public void findAll(@NonNull ShortcutService s, @NonNull List<ShortcutInfo> result,
+ @Nullable Predicate<ShortcutInfo> query, int cloneFlag) {
+ findAll(s, result, query, cloneFlag, null, 0);
+ }
+
+ /**
+ * Find all shortcuts that match {@code query}.
+ *
+ * This will also provide a "view" for each launcher -- a non-dynamic shortcut that's not pinned
+ * by the calling launcher will not be included in the result, and also "isPinned" will be
+ * adjusted for the caller too.
+ */
+ public void findAll(@NonNull ShortcutService s, @NonNull List<ShortcutInfo> result,
@Nullable Predicate<ShortcutInfo> query, int cloneFlag,
- @Nullable String callingLauncher) {
+ @Nullable String callingLauncher, int launcherUserId) {
+ if (getPackageInfo().isShadow()) {
+ // Restored and the app not installed yet, so don't return any.
+ return;
+ }
// Set of pinned shortcuts by the calling launcher.
final ArraySet<String> pinnedByCallerSet = (callingLauncher == null) ? null
- : s.getLauncherShortcuts(callingLauncher, mUserId)
- .getPinnedShortcutIds(mPackageName);
+ : s.getLauncherShortcutsLocked(callingLauncher, getPackageUserId(), launcherUserId)
+ .getPinnedShortcutIds(getPackageName(), getPackageUserId());
for (int i = 0; i < mShortcuts.size(); i++) {
final ShortcutInfo si = mShortcuts.valueAt(i);
@@ -309,7 +354,8 @@
|| ((pinnedByCallerSet != null) && pinnedByCallerSet.contains(si.getId()));
if (!si.isDynamic()) {
if (!si.isPinned()) {
- s.wtf("Shortcut not pinned here");
+ s.wtf("Shortcut not pinned: package " + getPackageName()
+ + ", user=" + getPackageUserId() + ", id=" + si.getId());
continue;
}
if (!isPinnedByCaller) {
@@ -337,7 +383,7 @@
pw.print(prefix);
pw.print("Package: ");
- pw.print(mPackageName);
+ pw.print(getPackageName());
pw.println();
pw.print(prefix);
@@ -355,6 +401,9 @@
pw.print(s.formatTime(mLastResetTime));
pw.println();
+ getPackageInfo().dump(s, pw, prefix + " ");
+ pw.println();
+
pw.println(" Shortcuts:");
long totalBitmapSize = 0;
final ArrayMap<String, ShortcutInfo> shortcuts = mShortcuts;
@@ -381,7 +430,9 @@
pw.println(")");
}
- public void saveToXml(@NonNull XmlSerializer out) throws IOException, XmlPullParserException {
+ @Override
+ public void saveToXml(@NonNull XmlSerializer out, boolean forBackup)
+ throws IOException, XmlPullParserException {
final int size = mShortcuts.size();
if (size == 0 && mApiCallCount == 0) {
@@ -390,33 +441,48 @@
out.startTag(null, TAG_ROOT);
- ShortcutService.writeAttr(out, ATTR_NAME, mPackageName);
+ ShortcutService.writeAttr(out, ATTR_NAME, getPackageName());
ShortcutService.writeAttr(out, ATTR_DYNAMIC_COUNT, mDynamicShortcutCount);
ShortcutService.writeAttr(out, ATTR_CALL_COUNT, mApiCallCount);
ShortcutService.writeAttr(out, ATTR_LAST_RESET, mLastResetTime);
+ getPackageInfo().saveToXml(out);
for (int j = 0; j < size; j++) {
- saveShortcut(out, mShortcuts.valueAt(j));
+ saveShortcut(out, mShortcuts.valueAt(j), forBackup);
}
out.endTag(null, TAG_ROOT);
}
- private static void saveShortcut(XmlSerializer out, ShortcutInfo si)
+ private static void saveShortcut(XmlSerializer out, ShortcutInfo si, boolean forBackup)
throws IOException, XmlPullParserException {
+ if (forBackup) {
+ if (!si.isPinned()) {
+ return; // Backup only pinned icons.
+ }
+ }
out.startTag(null, TAG_SHORTCUT);
ShortcutService.writeAttr(out, ATTR_ID, si.getId());
// writeAttr(out, "package", si.getPackageName()); // not needed
ShortcutService.writeAttr(out, ATTR_ACTIVITY, si.getActivityComponent());
// writeAttr(out, "icon", si.getIcon()); // We don't save it.
ShortcutService.writeAttr(out, ATTR_TITLE, si.getTitle());
+ ShortcutService.writeAttr(out, ATTR_TEXT, si.getText());
ShortcutService.writeAttr(out, ATTR_INTENT, si.getIntentNoExtras());
ShortcutService.writeAttr(out, ATTR_WEIGHT, si.getWeight());
ShortcutService.writeAttr(out, ATTR_TIMESTAMP,
si.getLastChangedTimestamp());
- ShortcutService.writeAttr(out, ATTR_FLAGS, si.getFlags());
- ShortcutService.writeAttr(out, ATTR_ICON_RES, si.getIconResourceId());
- ShortcutService.writeAttr(out, ATTR_BITMAP_PATH, si.getBitmapPath());
+ if (forBackup) {
+ // Don't write icon information. Also drop the dynamic flag.
+ ShortcutService.writeAttr(out, ATTR_FLAGS,
+ si.getFlags() &
+ ~(ShortcutInfo.FLAG_HAS_ICON_FILE | ShortcutInfo.FLAG_HAS_ICON_RES
+ | ShortcutInfo.FLAG_DYNAMIC));
+ } else {
+ ShortcutService.writeAttr(out, ATTR_FLAGS, si.getFlags());
+ ShortcutService.writeAttr(out, ATTR_ICON_RES, si.getIconResourceId());
+ ShortcutService.writeAttr(out, ATTR_BITMAP_PATH, si.getBitmapPath());
+ }
ShortcutService.writeTagExtra(out, TAG_INTENT_EXTRAS,
si.getIntentPersistableExtras());
@@ -425,13 +491,14 @@
out.endTag(null, TAG_SHORTCUT);
}
- public static ShortcutPackage loadFromXml(XmlPullParser parser, int userId)
+ public static ShortcutPackage loadFromXml(ShortcutService s, XmlPullParser parser,
+ int ownerUserId, boolean fromBackup)
throws IOException, XmlPullParserException {
final String packageName = ShortcutService.parseStringAttribute(parser,
ATTR_NAME);
- final ShortcutPackage ret = new ShortcutPackage(userId, packageName);
+ final ShortcutPackage ret = new ShortcutPackage(ownerUserId, packageName);
ret.mDynamicShortcutCount =
ShortcutService.parseIntAttribute(parser, ATTR_DYNAMIC_COUNT);
@@ -449,15 +516,20 @@
}
final int depth = parser.getDepth();
final String tag = parser.getName();
- switch (tag) {
- case TAG_SHORTCUT:
- final ShortcutInfo si = parseShortcut(parser, packageName);
+ if (depth == outerDepth + 1) {
+ switch (tag) {
+ case ShortcutPackageInfo.TAG_ROOT:
+ ret.getPackageInfo().loadFromXml(parser, fromBackup);
+ continue;
+ case TAG_SHORTCUT:
+ final ShortcutInfo si = parseShortcut(parser, packageName);
- // Don't use addShortcut(), we don't need to save the icon.
- ret.mShortcuts.put(si.getId(), si);
- continue;
+ // Don't use addShortcut(), we don't need to save the icon.
+ ret.mShortcuts.put(si.getId(), si);
+ continue;
+ }
}
- throw ShortcutService.throwForInvalidTag(depth, tag);
+ ShortcutService.warnForInvalidTag(depth, tag);
}
return ret;
}
@@ -468,6 +540,7 @@
ComponentName activityComponent;
// Icon icon;
String title;
+ String text;
Intent intent;
PersistableBundle intentPersistableExtras = null;
int weight;
@@ -481,10 +554,10 @@
activityComponent = ShortcutService.parseComponentNameAttribute(parser,
ATTR_ACTIVITY);
title = ShortcutService.parseStringAttribute(parser, ATTR_TITLE);
+ text = ShortcutService.parseStringAttribute(parser, ATTR_TEXT);
intent = ShortcutService.parseIntentAttribute(parser, ATTR_INTENT);
weight = (int) ShortcutService.parseLongAttribute(parser, ATTR_WEIGHT);
- lastChangedTimestamp = (int) ShortcutService.parseLongAttribute(parser,
- ATTR_TIMESTAMP);
+ lastChangedTimestamp = ShortcutService.parseLongAttribute(parser, ATTR_TIMESTAMP);
flags = (int) ShortcutService.parseLongAttribute(parser, ATTR_FLAGS);
iconRes = (int) ShortcutService.parseLongAttribute(parser, ATTR_ICON_RES);
bitmapPath = ShortcutService.parseStringAttribute(parser, ATTR_BITMAP_PATH);
@@ -513,8 +586,13 @@
throw ShortcutService.throwForInvalidTag(depth, tag);
}
return new ShortcutInfo(
- id, packageName, activityComponent, /* icon =*/ null, title, intent,
+ id, packageName, activityComponent, /* icon =*/ null, title, text, intent,
intentPersistableExtras, weight, extras, lastChangedTimestamp, flags,
iconRes, bitmapPath);
}
+
+ @VisibleForTesting
+ List<ShortcutInfo> getAllShortcutsForTest() {
+ return new ArrayList<>(mShortcuts.values());
+ }
}
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
new file mode 100644
index 0000000..2c45890
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.pm;
+
+import android.annotation.UserIdInt;
+import android.content.pm.PackageInfo;
+import android.util.Slog;
+
+import com.android.server.backup.BackupUtils;
+
+import libcore.io.Base64;
+import libcore.util.HexEncoding;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * Package information used by {@link android.content.pm.ShortcutManager} for backup / restore.
+ */
+class ShortcutPackageInfo {
+ private static final String TAG = ShortcutService.TAG;
+
+ static final String TAG_ROOT = "package-info";
+ private static final String ATTR_VERSION = "version";
+ private static final String ATTR_SHADOW = "shadow";
+
+ private static final String TAG_SIGNATURE = "signature";
+ private static final String ATTR_SIGNATURE_HASH = "hash";
+
+ /**
+ * When true, this package information was restored from the previous device, and the app hasn't
+ * been installed yet.
+ */
+ private boolean mIsShadow;
+ private int mVersionCode;
+ private ArrayList<byte[]> mSigHashes;
+
+ private ShortcutPackageInfo(int versionCode, ArrayList<byte[]> sigHashes, boolean isShadow) {
+ mVersionCode = versionCode;
+ mIsShadow = isShadow;
+ mSigHashes = sigHashes;
+ }
+
+ public static ShortcutPackageInfo newEmpty() {
+ return new ShortcutPackageInfo(0, new ArrayList<>(0), /* isShadow */ false);
+ }
+
+ public boolean isShadow() {
+ return mIsShadow;
+ }
+
+ public void setShadow(boolean shadow) {
+ mIsShadow = shadow;
+ }
+
+ public int getVersionCode() {
+ return mVersionCode;
+ }
+
+ public boolean hasSignatures() {
+ return mSigHashes.size() > 0;
+ }
+
+ public boolean canRestoreTo(ShortcutService s, PackageInfo target) {
+ if (!s.shouldBackupApp(target)) {
+ // "allowBackup" was true when backed up, but now false.
+ Slog.w(TAG, "Can't restore: package no longer allows backup");
+ return false;
+ }
+ if (target.versionCode < mVersionCode) {
+ Slog.w(TAG, String.format(
+ "Can't restore: package current version %d < backed up version %d",
+ target.versionCode, mVersionCode));
+ return false;
+ }
+ if (!BackupUtils.signaturesMatch(mSigHashes, target)) {
+ Slog.w(TAG, "Can't restore: Package signature mismatch");
+ return false;
+ }
+ return true;
+ }
+
+ public static ShortcutPackageInfo generateForInstalledPackage(
+ ShortcutService s, String packageName, @UserIdInt int packageUserId) {
+ final PackageInfo pi = s.getPackageInfoWithSignatures(packageName, packageUserId);
+ if (pi.signatures == null || pi.signatures.length == 0) {
+ Slog.e(TAG, "Can't get signatures: package=" + packageName);
+ return null;
+ }
+ final ShortcutPackageInfo ret = new ShortcutPackageInfo(pi.versionCode,
+ BackupUtils.hashSignatureArray(pi.signatures), /* shadow=*/ false);
+
+ return ret;
+ }
+
+ public void refresh(ShortcutService s, ShortcutPackageItem pkg) {
+ if (mIsShadow) {
+ s.wtf("Attempted to refresh package info for shadow package " + pkg.getPackageName()
+ + ", user=" + pkg.getOwnerUserId());
+ return;
+ }
+ // Note use mUserId here, rather than userId.
+ final PackageInfo pi = s.getPackageInfoWithSignatures(
+ pkg.getPackageName(), pkg.getPackageUserId());
+ if (pi == null) {
+ Slog.w(TAG, "Package not found: " + pkg.getPackageName());
+ return;
+ }
+ mVersionCode = pi.versionCode;
+ mSigHashes = BackupUtils.hashSignatureArray(pi.signatures);
+ }
+
+ public void saveToXml(XmlSerializer out) throws IOException {
+
+ out.startTag(null, TAG_ROOT);
+
+ ShortcutService.writeAttr(out, ATTR_VERSION, mVersionCode);
+ ShortcutService.writeAttr(out, ATTR_SHADOW, mIsShadow);
+
+ for (int i = 0; i < mSigHashes.size(); i++) {
+ out.startTag(null, TAG_SIGNATURE);
+ ShortcutService.writeAttr(out, ATTR_SIGNATURE_HASH, Base64.encode(mSigHashes.get(i)));
+ out.endTag(null, TAG_SIGNATURE);
+ }
+ out.endTag(null, TAG_ROOT);
+ }
+
+ public void loadFromXml(XmlPullParser parser, boolean fromBackup)
+ throws IOException, XmlPullParserException {
+
+ final int versionCode = ShortcutService.parseIntAttribute(parser, ATTR_VERSION);
+
+ // When restoring from backup, it's always shadow.
+ final boolean shadow =
+ fromBackup || ShortcutService.parseBooleanAttribute(parser, ATTR_SHADOW);
+
+ final ArrayList<byte[]> hashes = new ArrayList<>();
+
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+ final int depth = parser.getDepth();
+ final String tag = parser.getName();
+
+ if (depth == outerDepth + 1) {
+ switch (tag) {
+ case TAG_SIGNATURE: {
+ final String hash = ShortcutService.parseStringAttribute(
+ parser, ATTR_SIGNATURE_HASH);
+ hashes.add(Base64.decode(hash.getBytes()));
+ continue;
+ }
+ }
+ }
+ ShortcutService.warnForInvalidTag(depth, tag);
+ }
+
+ // Successfully loaded; replace the feilds.
+ mVersionCode = versionCode;
+ mIsShadow = shadow;
+ mSigHashes = hashes;
+ }
+
+ public void dump(ShortcutService s, PrintWriter pw, String prefix) {
+ pw.println();
+
+ pw.print(prefix);
+ pw.println("PackageInfo:");
+
+ pw.print(prefix);
+ pw.print(" IsShadow: ");
+ pw.print(mIsShadow);
+ pw.println();
+
+ pw.print(prefix);
+ pw.print(" Version: ");
+ pw.print(mVersionCode);
+ pw.println();
+
+ for (int i = 0; i < mSigHashes.size(); i++) {
+ pw.print(prefix);
+ pw.print(" ");
+ pw.print("SigHash: ");
+ pw.println(HexEncoding.encode(mSigHashes.get(i)));
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageItem.java b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
new file mode 100644
index 0000000..f31dd17
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.pm;
+
+import android.annotation.NonNull;
+import android.content.pm.PackageInfo;
+import android.util.Slog;
+
+import com.android.internal.util.Preconditions;
+
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+
+abstract class ShortcutPackageItem {
+ private static final String TAG = ShortcutService.TAG;
+
+ private final int mPackageUserId;
+ private final String mPackageName;
+
+ private final ShortcutPackageInfo mPackageInfo;
+
+ protected ShortcutPackageItem(int packageUserId, @NonNull String packageName,
+ @NonNull ShortcutPackageInfo packageInfo) {
+ mPackageUserId = packageUserId;
+ mPackageName = Preconditions.checkStringNotEmpty(packageName);
+ mPackageInfo = Preconditions.checkNotNull(packageInfo);
+ }
+
+ /**
+ * ID of the user who actually has this package running on. For {@link ShortcutPackage},
+ * this is the same thing as {@link #getOwnerUserId}, but if it's a {@link ShortcutLauncher} and
+ * {@link #getOwnerUserId} is of a work profile, then this ID could be the user who owns the
+ * profile.
+ */
+ public int getPackageUserId() {
+ return mPackageUserId;
+ }
+
+ /**
+ * ID of the user who sees the shortcuts from this instance.
+ */
+ public abstract int getOwnerUserId();
+
+ @NonNull
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public ShortcutPackageInfo getPackageInfo() {
+ return mPackageInfo;
+ }
+
+ public void refreshPackageInfoAndSave(ShortcutService s) {
+ if (mPackageInfo.isShadow()) {
+ return; // Don't refresh for shadow user.
+ }
+ mPackageInfo.refresh(s, this);
+ s.scheduleSaveUser(getOwnerUserId());
+ }
+
+ public void attemptToRestoreIfNeededAndSave(ShortcutService s) {
+ if (!mPackageInfo.isShadow()) {
+ return; // Already installed, nothing to do.
+ }
+ if (!s.isPackageInstalled(mPackageName, mPackageUserId)) {
+ if (ShortcutService.DEBUG) {
+ Slog.d(TAG, String.format("Package still not installed: %s user=%d",
+ mPackageName, mPackageUserId));
+ }
+ return; // Not installed, no need to restore yet.
+ }
+ if (!mPackageInfo.hasSignatures()) {
+ s.wtf("Attempted to restore package " + mPackageName + ", user=" + mPackageUserId
+ + " but signatures not found in the restore data.");
+ onRestoreBlocked(s);
+ return;
+ }
+
+ final PackageInfo pi = s.getPackageInfoWithSignatures(mPackageName, mPackageUserId);
+ if (!mPackageInfo.canRestoreTo(s, pi)) {
+ // Package is now installed, but can't restore. Let the subclass do the cleanup.
+ onRestoreBlocked(s);
+ return;
+ }
+ if (ShortcutService.DEBUG) {
+ Slog.d(TAG, String.format("Restored package: %s/%d on user %d", mPackageName,
+ mPackageUserId, getOwnerUserId()));
+ }
+
+ onRestored(s);
+
+ // Now the package is not shadow.
+ mPackageInfo.setShadow(false);
+
+ s.scheduleSaveUser(mPackageUserId);
+ }
+
+ /**
+ * Called when the new package can't be restored because it has a lower version number
+ * or different signatures.
+ */
+ protected abstract void onRestoreBlocked(ShortcutService s);
+
+ /**
+ * Called when the new package is successfully restored.
+ */
+ protected abstract void onRestored(ShortcutService s);
+
+ public abstract void saveToXml(@NonNull XmlSerializer out, boolean forBackup)
+ throws IOException, XmlPullParserException;
+}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 42954f5..5c1e7a8 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -19,15 +19,18 @@
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
+import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
import android.content.pm.IShortcutService;
import android.content.pm.LauncherApps;
import android.content.pm.LauncherApps.ShortcutQuery;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
@@ -43,6 +46,7 @@
import android.net.Uri;
import android.os.Binder;
import android.os.Environment;
+import android.os.FileUtils;
import android.os.Handler;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
@@ -55,7 +59,6 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
-import android.text.format.Formatter;
import android.text.format.Time;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -74,14 +77,18 @@
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.pm.ShortcutUser.PackageWithUser;
import libcore.io.IoUtils;
-import libcore.util.Objects;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -89,11 +96,13 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
+import java.util.function.Consumer;
import java.util.function.Predicate;
/**
@@ -101,21 +110,14 @@
*
* - Default launcher check does take a few ms. Worth caching.
*
- * - Allow non-default launcher to start pinned shortcuts. (but not dynamic.)
- *
- * - Extract the user/package/launcher classes to their own files. Maybe rename so they all have
- * the same "Shortcut" prefix.
- *
- * - Listen to PACKAGE_*, remove orphan info, update timestamp for icon res
- * -> Need to scan all packages when a user starts too.
- * -> Clear data -> remove all dynamic? but not the pinned?
+ * - Clear data -> remove all dynamic? but not the pinned?
*
* - Scan and remove orphan bitmaps (just in case).
*
- * - Backup & restore
- *
* - Detect when already registered instances are passed to APIs again, which might break
* internal bitmap handling.
+ *
+ * - Add more call stats.
*/
public class ShortcutService extends IShortcutService.Stub {
static final String TAG = "ShortcutService";
@@ -249,12 +251,38 @@
private int mSaveDelayMillis;
+ private final IPackageManager mIPackageManager;
private final PackageManagerInternal mPackageManagerInternal;
private final UserManager mUserManager;
@GuardedBy("mLock")
private List<Integer> mDirtyUserIds = new ArrayList<>();
+ private static final int PACKAGE_MATCH_FLAGS =
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.MATCH_UNINSTALLED_PACKAGES;
+
+ // Stats
+ @VisibleForTesting
+ interface Stats {
+ int GET_DEFAULT_HOME = 0;
+ int GET_PACKAGE_INFO = 1;
+ int GET_PACKAGE_INFO_WITH_SIG = 2;
+ int GET_APPLICATION_INFO = 3;
+ int LAUNCHER_PERMISSION_CHECK = 4;
+
+ int COUNT = LAUNCHER_PERMISSION_CHECK + 1;
+ }
+
+ final Object mStatLock = new Object();
+
+ @GuardedBy("mStatLock")
+ private final int[] mCountStats = new int[Stats.COUNT];
+
+ @GuardedBy("mStatLock")
+ private final long[] mDurationStats = new long[Stats.COUNT];
+
public ShortcutService(Context context) {
this(context, BackgroundThread.get().getLooper());
}
@@ -264,12 +292,20 @@
mContext = Preconditions.checkNotNull(context);
LocalServices.addService(ShortcutServiceInternal.class, new LocalService());
mHandler = new Handler(looper);
+ mIPackageManager = AppGlobals.getPackageManager();
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mUserManager = context.getSystemService(UserManager.class);
mPackageMonitor.register(context, looper, UserHandle.ALL, /* externalStorage= */ false);
}
+ void logDurationStat(int statId, long start) {
+ synchronized (mStatLock) {
+ mCountStats[statId]++;
+ mDurationStats[statId] += (System.currentTimeMillis() - start);
+ }
+ }
+
/**
* System service lifecycle.
*/
@@ -319,6 +355,8 @@
synchronized (mLock) {
// Preload
getUserShortcutsLocked(userId);
+
+ cleanupGonePackages(userId);
}
}
@@ -434,20 +472,32 @@
return parser.getAttributeValue(null, attribute);
}
+ static boolean parseBooleanAttribute(XmlPullParser parser, String attribute) {
+ return parseLongAttribute(parser, attribute) == 1;
+ }
+
static int parseIntAttribute(XmlPullParser parser, String attribute) {
return (int) parseLongAttribute(parser, attribute);
}
+ static int parseIntAttribute(XmlPullParser parser, String attribute, int def) {
+ return (int) parseLongAttribute(parser, attribute, def);
+ }
+
static long parseLongAttribute(XmlPullParser parser, String attribute) {
+ return parseLongAttribute(parser, attribute, 0);
+ }
+
+ static long parseLongAttribute(XmlPullParser parser, String attribute, long def) {
final String value = parseStringAttribute(parser, attribute);
if (TextUtils.isEmpty(value)) {
- return 0;
+ return def;
}
try {
return Long.parseLong(value);
} catch (NumberFormatException e) {
Slog.e(TAG, "Error parsing long " + value);
- return 0;
+ return def;
}
}
@@ -510,6 +560,12 @@
writeAttr(out, name, String.valueOf(value));
}
+ static void writeAttr(XmlSerializer out, String name, boolean value) throws IOException {
+ if (value) {
+ writeAttr(out, name, "1");
+ }
+ }
+
static void writeAttr(XmlSerializer out, String name, ComponentName comp) throws IOException {
if (comp == null) return;
writeAttr(out, name, comp.flattenToString());
@@ -607,31 +663,45 @@
}
path.mkdirs();
final AtomicFile file = new AtomicFile(path);
- FileOutputStream outs = null;
+ FileOutputStream os = null;
try {
- outs = file.startWrite();
+ os = file.startWrite();
- // Write to XML
- XmlSerializer out = new FastXmlSerializer();
- out.setOutput(outs, StandardCharsets.UTF_8.name());
- out.startDocument(null, true);
+ saveUserInternalLocked(userId, os, /* forBackup= */ false);
- getUserShortcutsLocked(userId).saveToXml(out);
-
- out.endDocument();
-
- // Close.
- file.finishWrite(outs);
- } catch (IOException|XmlPullParserException e) {
+ file.finishWrite(os);
+ } catch (XmlPullParserException|IOException e) {
Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
- file.failWrite(outs);
+ file.failWrite(os);
}
}
+ private void saveUserInternalLocked(@UserIdInt int userId, OutputStream os,
+ boolean forBackup) throws IOException, XmlPullParserException {
+
+ final BufferedOutputStream bos = new BufferedOutputStream(os);
+
+ // Write to XML
+ XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(bos, StandardCharsets.UTF_8.name());
+ out.startDocument(null, true);
+
+ getUserShortcutsLocked(userId).saveToXml(this, out, forBackup);
+
+ out.endDocument();
+
+ bos.flush();
+ os.flush();
+ }
+
static IOException throwForInvalidTag(int depth, String tag) throws IOException {
throw new IOException(String.format("Invalid tag '%s' found at depth %d", tag, depth));
}
+ static void warnForInvalidTag(int depth, String tag) throws IOException {
+ Slog.w(TAG, String.format("Invalid tag '%s' found at depth %d", tag, depth));
+ }
+
@Nullable
private ShortcutUser loadUserLocked(@UserIdInt int userId) {
final File path = new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES);
@@ -649,30 +719,8 @@
}
return null;
}
- ShortcutUser ret = null;
try {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(in, StandardCharsets.UTF_8.name());
-
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
- if (type != XmlPullParser.START_TAG) {
- continue;
- }
- final int depth = parser.getDepth();
-
- final String tag = parser.getName();
- if (DEBUG_LOAD) {
- Slog.d(TAG, String.format("depth=%d type=%d name=%s",
- depth, type, tag));
- }
- if ((depth == 1) && ShortcutUser.TAG_ROOT.equals(tag)) {
- ret = ShortcutUser.loadFromXml(parser, userId);
- continue;
- }
- throwForInvalidTag(depth, tag);
- }
- return ret;
+ return loadUserInternal(userId, in, /* forBackup= */ false);
} catch (IOException|XmlPullParserException e) {
Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
return null;
@@ -681,18 +729,48 @@
}
}
+ private ShortcutUser loadUserInternal(@UserIdInt int userId, InputStream is,
+ boolean fromBackup) throws XmlPullParserException, IOException {
+
+ final BufferedInputStream bis = new BufferedInputStream(is);
+
+ ShortcutUser ret = null;
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(bis, StandardCharsets.UTF_8.name());
+
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+ final int depth = parser.getDepth();
+
+ final String tag = parser.getName();
+ if (DEBUG_LOAD) {
+ Slog.d(TAG, String.format("depth=%d type=%d name=%s",
+ depth, type, tag));
+ }
+ if ((depth == 1) && ShortcutUser.TAG_ROOT.equals(tag)) {
+ ret = ShortcutUser.loadFromXml(this, parser, userId, fromBackup);
+ continue;
+ }
+ throwForInvalidTag(depth, tag);
+ }
+ return ret;
+ }
+
private void scheduleSaveBaseState() {
- scheduleSave(UserHandle.USER_NULL); // Special case -- use USER_NULL for base state.
+ scheduleSaveInner(UserHandle.USER_NULL); // Special case -- use USER_NULL for base state.
}
void scheduleSaveUser(@UserIdInt int userId) {
- scheduleSave(userId);
+ scheduleSaveInner(userId);
}
// In order to re-schedule, we need to reuse the same instance, so keep it in final.
private final Runnable mSaveDirtyInfoRunner = this::saveDirtyInfo;
- private void scheduleSave(@UserIdInt int userId) {
+ private void scheduleSaveInner(@UserIdInt int userId) {
if (DEBUG) {
Slog.d(TAG, "Scheduling to save for " + userId);
}
@@ -772,7 +850,7 @@
@GuardedBy("mLock")
@NonNull
- boolean isUserLoadedLocked(@UserIdInt int userId) {
+ private boolean isUserLoadedLocked(@UserIdInt int userId) {
return mUsers.get(userId) != null;
}
@@ -791,19 +869,27 @@
return userPackages;
}
+ void forEachLoadedUserLocked(@NonNull Consumer<ShortcutUser> c) {
+ for (int i = mUsers.size() - 1; i >= 0; i--) {
+ c.accept(mUsers.valueAt(i));
+ }
+ }
+
/** Return the per-user per-package state. */
@GuardedBy("mLock")
@NonNull
ShortcutPackage getPackageShortcutsLocked(
@NonNull String packageName, @UserIdInt int userId) {
- return getUserShortcutsLocked(userId).getPackageShortcuts(packageName);
+ return getUserShortcutsLocked(userId).getPackageShortcuts(this, packageName);
}
@GuardedBy("mLock")
@NonNull
- ShortcutLauncher getLauncherShortcuts(
- @NonNull String packageName, @UserIdInt int userId) {
- return getUserShortcutsLocked(userId).getLauncherShortcuts(packageName);
+ ShortcutLauncher getLauncherShortcutsLocked(
+ @NonNull String packageName, @UserIdInt int ownerUserId,
+ @UserIdInt int launcherUserId) {
+ return getUserShortcutsLocked(ownerUserId)
+ .getLauncherShortcuts(this, packageName, launcherUserId);
}
// === Caller validation ===
@@ -1016,6 +1102,10 @@
Preconditions.checkState(isCallerShell(), "Caller must be shell");
}
+ private void enforceSystem() {
+ Preconditions.checkState(isCallerSystem(), "Caller must be system");
+ }
+
private void verifyCaller(@NonNull String packageName, @UserIdInt int userId) {
Preconditions.checkStringNotEmpty(packageName, "packageName");
@@ -1035,19 +1125,6 @@
throw new SecurityException("Caller UID= doesn't own " + packageName);
}
- // Test overrides it.
- int injectGetPackageUid(@NonNull String packageName, @UserIdInt int userId) {
- try {
- return mContext.getPackageManager().getPackageUidAsUser(packageName,
- PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
- | PackageManager.MATCH_UNINSTALLED_PACKAGES,
- userId);
- } catch (NameNotFoundException e) {
- return -1;
- }
- }
-
void postToHandler(Runnable r) {
mHandler.post(r);
}
@@ -1308,8 +1385,7 @@
final ArrayList<ShortcutInfo> ret = new ArrayList<>();
- getPackageShortcutsLocked(packageName, userId).findAll(this, ret, query, cloneFlags,
- /* callingLauncher= */ null);
+ getPackageShortcutsLocked(packageName, userId).findAll(this, ret, query, cloneFlags);
return new ParceledListSlice<>(ret);
}
@@ -1376,18 +1452,17 @@
@VisibleForTesting
boolean hasShortcutHostPermissionInner(@NonNull String callingPackage, int userId) {
synchronized (mLock) {
- long start = 0;
- if (DEBUG) {
- start = System.currentTimeMillis();
- }
+ final long start = System.currentTimeMillis();
final ShortcutUser user = getUserShortcutsLocked(userId);
final List<ResolveInfo> allHomeCandidates = new ArrayList<>();
// Default launcher from package manager.
+ final long startGetHomeActivitiesAsUser = System.currentTimeMillis();
final ComponentName defaultLauncher = injectPackageManagerInternal()
.getHomeActivitiesAsUser(allHomeCandidates, userId);
+ logDurationStat(Stats.GET_DEFAULT_HOME, startGetHomeActivitiesAsUser);
ComponentName detected;
if (defaultLauncher != null) {
@@ -1430,10 +1505,8 @@
lastPriority = ri.priority;
}
}
- if (DEBUG) {
- long end = System.currentTimeMillis();
- Slog.v(TAG, String.format("hasShortcutPermission took %d ms", end - start));
- }
+ logDurationStat(Stats.LAUNCHER_PERMISSION_CHECK, start);
+
if (detected != null) {
if (DEBUG) {
Slog.v(TAG, "Detected launcher: " + detected);
@@ -1449,39 +1522,53 @@
// === House keeping ===
+ /**
+ * Remove all the information associated with a package. This will really remove all the
+ * information, including the restore information (i.e. it'll remove packages even if they're
+ * shadow).
+ */
@VisibleForTesting
- void cleanUpPackageLocked(String packageName, int userId) {
- final boolean wasUserLoaded = isUserLoadedLocked(userId);
+ void cleanUpPackageLocked(String packageName, int owningUserId, int packageUserId) {
+ if (isPackageInstalled(packageName, packageUserId)) {
+ wtf("Package " + packageName + " is still installed for user " + packageUserId);
+ return;
+ }
- final ShortcutUser mUser = getUserShortcutsLocked(userId);
+ final boolean wasUserLoaded = isUserLoadedLocked(owningUserId);
+
+ final ShortcutUser mUser = getUserShortcutsLocked(owningUserId);
boolean doNotify = false;
// First, remove the package from the package list (if the package is a publisher).
- if (mUser.getPackages().remove(packageName) != null) {
- doNotify = true;
+ if (packageUserId == owningUserId) {
+ if (mUser.removePackage(packageName) != null) {
+ doNotify = true;
+ }
}
+
// Also remove from the launcher list (if the package is a launcher).
- mUser.getLaunchers().remove(packageName);
+ mUser.removeLauncher(packageUserId, packageName);
// Then remove pinned shortcuts from all launchers.
- for (int i = mUser.getLaunchers().size() - 1; i >= 0; i--) {
- mUser.getLaunchers().valueAt(i).cleanUpPackage(packageName);
+ final ArrayMap<PackageWithUser, ShortcutLauncher> launchers = mUser.getAllLaunchers();
+ for (int i = launchers.size() - 1; i >= 0; i--) {
+ launchers.valueAt(i).cleanUpPackage(packageName, packageUserId);
}
// Now there may be orphan shortcuts because we removed pinned shortucts at the previous
// step. Remove them too.
- for (int i = mUser.getPackages().size() - 1; i >= 0; i--) {
- mUser.getPackages().valueAt(i).refreshPinnedFlags(this);
+ for (int i = mUser.getAllPackages().size() - 1; i >= 0; i--) {
+ mUser.getAllPackages().valueAt(i).refreshPinnedFlags(this);
}
- scheduleSaveUser(userId);
+ scheduleSaveUser(owningUserId);
if (doNotify) {
- notifyListeners(packageName, userId);
+ notifyListeners(packageName, owningUserId);
}
if (!wasUserLoaded) {
// Note this will execute the scheduled save.
- unloadUserLocked(userId);
+ unloadUserLocked(owningUserId);
}
}
@@ -1489,8 +1576,9 @@
* Entry point from {@link LauncherApps}.
*/
private class LocalService extends ShortcutServiceInternal {
+
@Override
- public List<ShortcutInfo> getShortcuts(
+ public List<ShortcutInfo> getShortcuts(int launcherUserId,
@NonNull String callingPackage, long changedSince,
@Nullable String packageName, @Nullable ComponentName componentName,
int queryFlags, int userId) {
@@ -1501,15 +1589,18 @@
: ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO;
synchronized (mLock) {
+ getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
+ .attemptToRestoreIfNeededAndSave(ShortcutService.this);
+
if (packageName != null) {
- getShortcutsInnerLocked(
+ getShortcutsInnerLocked(launcherUserId,
callingPackage, packageName, changedSince,
componentName, queryFlags, userId, ret, cloneFlag);
} else {
final ArrayMap<String, ShortcutPackage> packages =
- getUserShortcutsLocked(userId).getPackages();
+ getUserShortcutsLocked(userId).getAllPackages();
for (int i = packages.size() - 1; i >= 0; i--) {
- getShortcutsInnerLocked(
+ getShortcutsInnerLocked(launcherUserId,
callingPackage, packages.keyAt(i), changedSince,
componentName, queryFlags, userId, ret, cloneFlag);
}
@@ -1518,7 +1609,7 @@
return ret;
}
- private void getShortcutsInnerLocked(@NonNull String callingPackage,
+ private void getShortcutsInnerLocked(int launcherUserId, @NonNull String callingPackage,
@Nullable String packageName,long changedSince,
@Nullable ComponentName componentName, int queryFlags,
int userId, ArrayList<ShortcutInfo> ret, int cloneFlag) {
@@ -1538,11 +1629,11 @@
((queryFlags & ShortcutQuery.FLAG_GET_PINNED) != 0)
&& si.isPinned();
return matchDynamic || matchPinned;
- }, cloneFlag, callingPackage);
+ }, cloneFlag, callingPackage, launcherUserId);
}
@Override
- public List<ShortcutInfo> getShortcutInfo(
+ public List<ShortcutInfo> getShortcutInfo(int launcherUserId,
@NonNull String callingPackage,
@NonNull String packageName, @Nullable List<String> ids, int userId) {
// Calling permission must be checked by LauncherAppsImpl.
@@ -1551,40 +1642,86 @@
final ArrayList<ShortcutInfo> ret = new ArrayList<>(ids.size());
final ArraySet<String> idSet = new ArraySet<>(ids);
synchronized (mLock) {
+ getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
+ .attemptToRestoreIfNeededAndSave(ShortcutService.this);
+
getPackageShortcutsLocked(packageName, userId).findAll(
ShortcutService.this, ret,
(ShortcutInfo si) -> idSet.contains(si.getId()),
- ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER, callingPackage);
+ ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER, callingPackage, launcherUserId);
}
return ret;
}
@Override
- public void pinShortcuts(@NonNull String callingPackage, @NonNull String packageName,
+ public boolean isPinnedByCaller(int launcherUserId, @NonNull String callingPackage,
+ @NonNull String packageName, @NonNull String shortcutId, int userId) {
+ Preconditions.checkStringNotEmpty(packageName, "packageName");
+ Preconditions.checkStringNotEmpty(shortcutId, "shortcutId");
+
+ synchronized (mLock) {
+ getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
+ .attemptToRestoreIfNeededAndSave(ShortcutService.this);
+
+ final ShortcutInfo si = getShortcutInfoLocked(
+ launcherUserId, callingPackage, packageName, shortcutId, userId);
+ return si != null && si.isPinned();
+ }
+ }
+
+ private ShortcutInfo getShortcutInfoLocked(
+ int launcherUserId, @NonNull String callingPackage,
+ @NonNull String packageName, @NonNull String shortcutId, int userId) {
+ Preconditions.checkStringNotEmpty(packageName, "packageName");
+ Preconditions.checkStringNotEmpty(shortcutId, "shortcutId");
+
+ final ArrayList<ShortcutInfo> list = new ArrayList<>(1);
+ getPackageShortcutsLocked(packageName, userId).findAll(
+ ShortcutService.this, list,
+ (ShortcutInfo si) -> shortcutId.equals(si.getId()),
+ /* clone flags=*/ 0, callingPackage, launcherUserId);
+ return list.size() == 0 ? null : list.get(0);
+ }
+
+ @Override
+ public void pinShortcuts(int launcherUserId,
+ @NonNull String callingPackage, @NonNull String packageName,
@NonNull List<String> shortcutIds, int userId) {
// Calling permission must be checked by LauncherAppsImpl.
Preconditions.checkStringNotEmpty(packageName, "packageName");
Preconditions.checkNotNull(shortcutIds, "shortcutIds");
synchronized (mLock) {
- getLauncherShortcuts(callingPackage, userId).pinShortcuts(
- ShortcutService.this, packageName, shortcutIds);
+ final ShortcutLauncher launcher =
+ getLauncherShortcutsLocked(callingPackage, userId, launcherUserId);
+ launcher.attemptToRestoreIfNeededAndSave(ShortcutService.this);
+
+ launcher.pinShortcuts(
+ ShortcutService.this, userId, packageName, shortcutIds);
}
userPackageChanged(packageName, userId);
}
@Override
- public Intent createShortcutIntent(@NonNull String callingPackage,
+ public Intent createShortcutIntent(int launcherUserId,
+ @NonNull String callingPackage,
@NonNull String packageName, @NonNull String shortcutId, int userId) {
// Calling permission must be checked by LauncherAppsImpl.
Preconditions.checkStringNotEmpty(packageName, "packageName can't be empty");
Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty");
synchronized (mLock) {
- final ShortcutInfo fullShortcut =
- getPackageShortcutsLocked(packageName, userId)
- .findShortcutById(shortcutId);
- return fullShortcut == null ? null : fullShortcut.getIntent();
+ getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
+ .attemptToRestoreIfNeededAndSave(ShortcutService.this);
+
+ // Make sure the shortcut is actually visible to the launcher.
+ final ShortcutInfo si = getShortcutInfoLocked(
+ launcherUserId, callingPackage, packageName, shortcutId, userId);
+ // "si == null" should suffice here, but check the flags too just to make sure.
+ if (si == null || !(si.isDynamic() || si.isPinned())) {
+ return null;
+ }
+ return si.getIntent();
}
}
@@ -1596,11 +1733,15 @@
}
@Override
- public int getShortcutIconResId(@NonNull String callingPackage,
+ public int getShortcutIconResId(int launcherUserId,
+ @NonNull String callingPackage,
@NonNull ShortcutInfo shortcut, int userId) {
Preconditions.checkNotNull(shortcut, "shortcut");
synchronized (mLock) {
+ getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
+ .attemptToRestoreIfNeededAndSave(ShortcutService.this);
+
final ShortcutInfo shortcutInfo = getPackageShortcutsLocked(
shortcut.getPackageName(), userId).findShortcutById(shortcut.getId());
return (shortcutInfo != null && shortcutInfo.hasIconResource())
@@ -1609,11 +1750,15 @@
}
@Override
- public ParcelFileDescriptor getShortcutIconFd(@NonNull String callingPackage,
+ public ParcelFileDescriptor getShortcutIconFd(int launcherUserId,
+ @NonNull String callingPackage,
@NonNull ShortcutInfo shortcutIn, int userId) {
Preconditions.checkNotNull(shortcutIn, "shortcut");
synchronized (mLock) {
+ getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
+ .attemptToRestoreIfNeededAndSave(ShortcutService.this);
+
final ShortcutInfo shortcutInfo = getPackageShortcutsLocked(
shortcutIn.getPackageName(), userId).findShortcutById(shortcutIn.getId());
if (shortcutInfo == null || !shortcutInfo.hasIconFile()) {
@@ -1635,12 +1780,22 @@
}
@Override
- public boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId) {
- return ShortcutService.this.hasShortcutHostPermission(callingPackage, userId);
+ public boolean hasShortcutHostPermission(int launcherUserId,
+ @NonNull String callingPackage) {
+ return ShortcutService.this.hasShortcutHostPermission(callingPackage, launcherUserId);
}
}
- private PackageMonitor mPackageMonitor = new PackageMonitor() {
+ /**
+ * Package event callbacks.
+ */
+ @VisibleForTesting
+ final PackageMonitor mPackageMonitor = new PackageMonitor() {
+ @Override
+ public void onPackageAdded(String packageName, int uid) {
+ handlePackageAdded(packageName, getChangingUserId());
+ }
+
@Override
public void onPackageUpdateFinished(String packageName, int uid) {
handlePackageUpdateFinished(packageName, getChangingUserId());
@@ -1650,38 +1805,204 @@
public void onPackageRemoved(String packageName, int uid) {
handlePackageRemoved(packageName, getChangingUserId());
}
-
- @Override
- public void onPackageRemovedAllUsers(String packageName, int uid) {
- handlePackageRemovedAllUsers(packageName, getChangingUserId());
- }
};
- void handlePackageUpdateFinished(String packageName, @UserIdInt int userId) {
+ /**
+ * Called when a user is unlocked. Check all known packages still exist, and otherwise
+ * perform cleanup.
+ */
+ @VisibleForTesting
+ void cleanupGonePackages(@UserIdInt int ownerUserId) {
if (DEBUG) {
- Slog.d(TAG, "onPackageUpdateFinished() userId=" + userId);
+ Slog.d(TAG, "cleanupGonePackages() ownerUserId=" + ownerUserId);
}
- // TODO Update the version.
- }
+ final ArrayList<PackageWithUser> gonePackages = new ArrayList<>();
- void handlePackageRemoved(String packageName, @UserIdInt int userId) {
- if (DEBUG) {
- Slog.d(TAG, "onPackageRemoved() userId=" + userId);
- }
synchronized (mLock) {
- cleanUpPackageLocked(packageName, userId);
+ final ShortcutUser user = getUserShortcutsLocked(ownerUserId);
+
+ user.forAllPackageItems(spi -> {
+ if (spi.getPackageInfo().isShadow()) {
+ return; // Don't delete shadow information.
+ }
+ if (isPackageInstalled(spi.getPackageName(), spi.getPackageUserId())) {
+ return; // Package not gone.
+ }
+ gonePackages.add(PackageWithUser.of(spi));
+ });
+ if (gonePackages.size() > 0) {
+ for (int i = gonePackages.size() - 1; i >= 0; i--) {
+ final PackageWithUser pu = gonePackages.get(i);
+ cleanUpPackageLocked(pu.packageName, ownerUserId, pu.userId);
+ }
+ }
}
}
- void handlePackageRemovedAllUsers(String packageName, @UserIdInt int userId) {
+ private void handlePackageAdded(String packageName, @UserIdInt int userId) {
if (DEBUG) {
- Slog.d(TAG, "onPackageRemovedAllUsers() userId=" + userId);
+ Slog.d(TAG, String.format("handlePackageAdded: %s user=%d", packageName, userId));
}
synchronized (mLock) {
- cleanUpPackageLocked(packageName, userId);
+ forEachLoadedUserLocked(user ->
+ user.attemptToRestoreIfNeededAndSave(this, packageName, userId));
}
+ }
- // TODO Remove from all users, which we can't if the user is locked.
+ private void handlePackageUpdateFinished(String packageName, @UserIdInt int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, String.format("handlePackageUpdateFinished: %s user=%d",
+ packageName, userId));
+ }
+ synchronized (mLock) {
+ forEachLoadedUserLocked(user ->
+ user.attemptToRestoreIfNeededAndSave(this, packageName, userId));
+ }
+ }
+
+ private void handlePackageRemoved(String packageName, @UserIdInt int packageUserId) {
+ if (DEBUG) {
+ Slog.d(TAG, String.format("handlePackageRemoved: %s user=%d", packageName,
+ packageUserId));
+ }
+ synchronized (mLock) {
+ forEachLoadedUserLocked(user ->
+ cleanUpPackageLocked(packageName, user.getUserId(), packageUserId));
+ }
+ }
+
+ // === PackageManager interaction ===
+
+ PackageInfo getPackageInfoWithSignatures(String packageName, @UserIdInt int userId) {
+ return injectPackageInfo(packageName, userId, true);
+ }
+
+ int injectGetPackageUid(@NonNull String packageName, @UserIdInt int userId) {
+ final long token = injectClearCallingIdentity();
+ try {
+ return mIPackageManager.getPackageUid(packageName, PACKAGE_MATCH_FLAGS
+ , userId);
+ } catch (RemoteException e) {
+ // Shouldn't happen.
+ Slog.wtf(TAG, "RemoteException", e);
+ return -1;
+ } finally {
+ injectRestoreCallingIdentity(token);
+ }
+ }
+
+ @VisibleForTesting
+ PackageInfo injectPackageInfo(String packageName, @UserIdInt int userId,
+ boolean getSignatures) {
+ final long start = System.currentTimeMillis();
+ final long token = injectClearCallingIdentity();
+ try {
+ return mIPackageManager.getPackageInfo(packageName, PACKAGE_MATCH_FLAGS
+ | (getSignatures ? PackageManager.GET_SIGNATURES : 0)
+ , userId);
+ } catch (RemoteException e) {
+ // Shouldn't happen.
+ Slog.wtf(TAG, "RemoteException", e);
+ return null;
+ } finally {
+ injectRestoreCallingIdentity(token);
+
+ logDurationStat(
+ (getSignatures ? Stats.GET_PACKAGE_INFO_WITH_SIG : Stats.GET_PACKAGE_INFO),
+ start);
+ }
+ }
+
+ @VisibleForTesting
+ ApplicationInfo injectApplicationInfo(String packageName, @UserIdInt int userId) {
+ final long start = System.currentTimeMillis();
+ final long token = injectClearCallingIdentity();
+ try {
+ return mIPackageManager.getApplicationInfo(packageName, PACKAGE_MATCH_FLAGS, userId);
+ } catch (RemoteException e) {
+ // Shouldn't happen.
+ Slog.wtf(TAG, "RemoteException", e);
+ return null;
+ } finally {
+ injectRestoreCallingIdentity(token);
+
+ logDurationStat(Stats.GET_APPLICATION_INFO, start);
+ }
+ }
+
+ private boolean isApplicationFlagSet(String packageName, int userId, int flags) {
+ final ApplicationInfo ai = injectApplicationInfo(packageName, userId);
+ return (ai != null) && ((ai.flags & flags) == flags);
+ }
+
+ boolean isPackageInstalled(String packageName, int userId) {
+ return isApplicationFlagSet(packageName, userId, ApplicationInfo.FLAG_INSTALLED);
+ }
+
+ // === Backup & restore ===
+
+ boolean shouldBackupApp(String packageName, int userId) {
+ return isApplicationFlagSet(packageName, userId, ApplicationInfo.FLAG_ALLOW_BACKUP);
+ }
+
+ boolean shouldBackupApp(PackageInfo pi) {
+ return (pi.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0;
+ }
+
+ @Override
+ public byte[] getBackupPayload(@UserIdInt int userId) {
+ enforceSystem();
+ if (DEBUG) {
+ Slog.d(TAG, "Backing up user " + userId);
+ }
+ synchronized (mLock) {
+ final ShortcutUser user = getUserShortcutsLocked(userId);
+ if (user == null) {
+ Slog.w(TAG, "Can't backup: user not found: id=" + userId);
+ return null;
+ }
+
+ user.forAllPackageItems(spi -> spi.refreshPackageInfoAndSave(this));
+
+ // Then save.
+ final ByteArrayOutputStream os = new ByteArrayOutputStream(32 * 1024);
+ try {
+ saveUserInternalLocked(userId, os, /* forBackup */ true);
+ } catch (XmlPullParserException|IOException e) {
+ // Shouldn't happen.
+ Slog.w(TAG, "Backup failed.", e);
+ return null;
+ }
+ return os.toByteArray();
+ }
+ }
+
+ @Override
+ public void applyRestore(byte[] payload, @UserIdInt int userId) {
+ enforceSystem();
+ if (DEBUG) {
+ Slog.d(TAG, "Restoring user " + userId);
+ }
+ final ShortcutUser user;
+ final ByteArrayInputStream is = new ByteArrayInputStream(payload);
+ try {
+ user = loadUserInternal(userId, is, /* fromBackup */ true);
+ } catch (XmlPullParserException|IOException e) {
+ Slog.w(TAG, "Restoration failed.", e);
+ return;
+ }
+ synchronized (mLock) {
+ mUsers.put(userId, user);
+
+ // Then purge all the save images.
+ final File bitmapPath = getUserBitmapFilePath(userId);
+ final boolean success = FileUtils.deleteContents(bitmapPath);
+ if (!success) {
+ Slog.w(TAG, "Failed to delete " + bitmapPath);
+ }
+
+ saveUserLocked(userId);
+ }
}
// === Dump ===
@@ -1732,9 +2053,19 @@
pw.print(" Icon format: ");
pw.print(mIconPersistFormat);
pw.print(" Icon quality: ");
- pw.print(mIconPersistQuality);
+ pw.println(mIconPersistQuality);
pw.println();
+ pw.println(" Stats:");
+ synchronized (mStatLock) {
+ final String p = " ";
+ dumpStatLS(pw, p, Stats.GET_DEFAULT_HOME, "getHomeActivities()");
+ dumpStatLS(pw, p, Stats.LAUNCHER_PERMISSION_CHECK, "Launcher permission check");
+
+ dumpStatLS(pw, p, Stats.GET_PACKAGE_INFO, "getPackageInfo()");
+ dumpStatLS(pw, p, Stats.GET_PACKAGE_INFO_WITH_SIG, "getPackageInfo(SIG)");
+ dumpStatLS(pw, p, Stats.GET_APPLICATION_INFO, "getApplicationInfo");
+ }
for (int i = 0; i < mUsers.size(); i++) {
pw.println();
@@ -1749,6 +2080,15 @@
return tobj.format("%Y-%m-%d %H:%M:%S");
}
+ private void dumpStatLS(PrintWriter pw, String prefix, int statId, String label) {
+ pw.print(prefix);
+ final int count = mCountStats[statId];
+ final long dur = mDurationStats[statId];
+ pw.println(String.format("%s: count=%d, total=%dms, avg=%.1fms",
+ label, count, dur,
+ (count == 0 ? 0 : ((double) dur) / count)));
+ }
+
// === Shell support ===
@Override
@@ -1819,6 +2159,9 @@
case "refresh-default-launcher":
handleRefreshDefaultLauncher();
break;
+ case "unload-user":
+ handleUnloadUser();
+ break;
default:
return handleDefaultCommands(cmd);
}
@@ -1856,6 +2199,10 @@
pw.println("cmd shortcut refresh-default-launcher [--user USER_ID]");
pw.println(" Refresh the cached default launcher");
pw.println();
+ pw.println("cmd shortcut unload-user [--user USER_ID]");
+ pw.println(" Unload a user from the memory");
+ pw.println(" (This should not affect any observable behavior)");
+ pw.println();
}
private int handleResetThrottling() throws CommandException {
@@ -1927,6 +2274,12 @@
clearLauncher();
showLauncher();
}
+
+ private void handleUnloadUser() throws CommandException {
+ parseOptions(/* takeUser =*/ true);
+
+ ShortcutService.this.handleCleanupUser(mUserId);
+ }
}
// === Unit test support ===
@@ -1960,9 +2313,10 @@
}
final void wtf(String message) {
- Slog.wtf(TAG, message, /* exception= */ null);
+ wtf( message, /* exception= */ null);
}
+ // Injection point.
void wtf(String message, Exception e) {
Slog.wtf(TAG, message, e);
}
@@ -2033,7 +2387,7 @@
final ShortcutUser user = mUsers.get(userId);
if (user == null) return null;
- final ShortcutPackage pkg = user.getPackages().get(packageName);
+ final ShortcutPackage pkg = user.getAllPackages().get(packageName);
if (pkg == null) return null;
return pkg.findShortcutById(shortcutId);
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index 4a6b1e4..593f607 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -19,6 +19,9 @@
import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.util.ArrayMap;
+import android.util.Slog;
+
+import com.android.internal.util.Preconditions;
import libcore.util.Objects;
@@ -28,6 +31,7 @@
import java.io.IOException;
import java.io.PrintWriter;
+import java.util.function.Consumer;
/**
* User information used by {@link ShortcutService}.
@@ -40,12 +44,50 @@
private static final String ATTR_VALUE = "value";
+ static final class PackageWithUser {
+ final int userId;
+ final String packageName;
+
+ private PackageWithUser(int userId, String packageName) {
+ this.userId = userId;
+ this.packageName = Preconditions.checkNotNull(packageName);
+ }
+
+ public static PackageWithUser of(int userId, String packageName) {
+ return new PackageWithUser(userId, packageName);
+ }
+
+ public static PackageWithUser of(ShortcutPackageItem spi) {
+ return new PackageWithUser(spi.getPackageUserId(), spi.getPackageName());
+ }
+
+ @Override
+ public int hashCode() {
+ return packageName.hashCode() ^ userId;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof PackageWithUser)) {
+ return false;
+ }
+ final PackageWithUser that = (PackageWithUser) obj;
+
+ return userId == that.userId && packageName.equals(that.packageName);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{Package: %d, %s}", userId, packageName);
+ }
+ }
+
@UserIdInt
- final int mUserId;
+ private final int mUserId;
private final ArrayMap<String, ShortcutPackage> mPackages = new ArrayMap<>();
- private final ArrayMap<String, ShortcutLauncher> mLaunchers = new ArrayMap<>();
+ private final ArrayMap<PackageWithUser, ShortcutLauncher> mLaunchers = new ArrayMap<>();
private ComponentName mLauncherComponent;
@@ -53,53 +95,127 @@
mUserId = userId;
}
- public ArrayMap<String, ShortcutPackage> getPackages() {
+ public int getUserId() {
+ return mUserId;
+ }
+
+ public ArrayMap<String, ShortcutPackage> getAllPackages() {
return mPackages;
}
- public ArrayMap<String, ShortcutLauncher> getLaunchers() {
+ public ShortcutPackage removePackage(@NonNull String packageName) {
+ return mPackages.remove(packageName);
+ }
+
+ public ArrayMap<PackageWithUser, ShortcutLauncher> getAllLaunchers() {
return mLaunchers;
}
- public ShortcutPackage getPackageShortcuts(@NonNull String packageName) {
+ public void addLauncher(ShortcutLauncher launcher) {
+ mLaunchers.put(PackageWithUser.of(launcher.getPackageUserId(),
+ launcher.getPackageName()), launcher);
+ }
+
+ public ShortcutLauncher removeLauncher(
+ @UserIdInt int packageUserId, @NonNull String packageName) {
+ return mLaunchers.remove(PackageWithUser.of(packageUserId, packageName));
+ }
+
+ public ShortcutPackage getPackageShortcuts(ShortcutService s, @NonNull String packageName) {
ShortcutPackage ret = mPackages.get(packageName);
if (ret == null) {
ret = new ShortcutPackage(mUserId, packageName);
mPackages.put(packageName, ret);
+ } else {
+ ret.attemptToRestoreIfNeededAndSave(s);
}
return ret;
}
- public ShortcutLauncher getLauncherShortcuts(@NonNull String packageName) {
- ShortcutLauncher ret = mLaunchers.get(packageName);
+ public ShortcutLauncher getLauncherShortcuts(ShortcutService s, @NonNull String packageName,
+ @UserIdInt int launcherUserId) {
+ final PackageWithUser key = PackageWithUser.of(launcherUserId, packageName);
+ ShortcutLauncher ret = mLaunchers.get(key);
if (ret == null) {
- ret = new ShortcutLauncher(mUserId, packageName);
- mLaunchers.put(packageName, ret);
+ ret = new ShortcutLauncher(mUserId, packageName, launcherUserId);
+ mLaunchers.put(key, ret);
+ } else {
+ ret.attemptToRestoreIfNeededAndSave(s);
}
return ret;
}
- public void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
+ public void forAllPackageItems(Consumer<ShortcutPackageItem> callback) {
+ {
+ final int size = mLaunchers.size();
+ for (int i = 0; i < size; i++) {
+ callback.accept(mLaunchers.valueAt(i));
+ }
+ }
+ {
+ final int size = mPackages.size();
+ for (int i = 0; i < size; i++) {
+ callback.accept(mPackages.valueAt(i));
+ }
+ }
+ }
+
+ public void forPackageItem(@NonNull String packageName, @UserIdInt int packageUserId,
+ Consumer<ShortcutPackageItem> callback) {
+ forAllPackageItems(spi -> {
+ if ((spi.getPackageUserId() == packageUserId)
+ && spi.getPackageName().equals(packageName)) {
+ callback.accept(spi);
+ }
+ });
+ }
+
+ public void attemptToRestoreIfNeededAndSave(ShortcutService s, @NonNull String packageName,
+ @UserIdInt int packageUserId) {
+ forPackageItem(packageName, packageUserId, spi -> {
+ spi.attemptToRestoreIfNeededAndSave(s);
+ });
+ }
+
+ public void saveToXml(ShortcutService s, XmlSerializer out, boolean forBackup)
+ throws IOException, XmlPullParserException {
out.startTag(null, TAG_ROOT);
ShortcutService.writeTagValue(out, TAG_LAUNCHER,
mLauncherComponent);
- final int lsize = mLaunchers.size();
- for (int i = 0; i < lsize; i++) {
- mLaunchers.valueAt(i).saveToXml(out);
+ // Can't use forEachPackageItem due to the checked exceptions.
+ {
+ final int size = mLaunchers.size();
+ for (int i = 0; i < size; i++) {
+ saveShortcutPackageItem(s, out, mLaunchers.valueAt(i), forBackup);
+ }
}
-
- final int psize = mPackages.size();
- for (int i = 0; i < psize; i++) {
- mPackages.valueAt(i).saveToXml(out);
+ {
+ final int size = mPackages.size();
+ for (int i = 0; i < size; i++) {
+ saveShortcutPackageItem(s, out, mPackages.valueAt(i), forBackup);
+ }
}
out.endTag(null, TAG_ROOT);
}
- public static ShortcutUser loadFromXml(XmlPullParser parser, int userId)
- throws IOException, XmlPullParserException {
+ private void saveShortcutPackageItem(ShortcutService s, XmlSerializer out,
+ ShortcutPackageItem spi, boolean forBackup) throws IOException, XmlPullParserException {
+ if (forBackup) {
+ if (!s.shouldBackupApp(spi.getPackageName(), spi.getPackageUserId())) {
+ return; // Don't save.
+ }
+ if (spi.getPackageUserId() != spi.getOwnerUserId()) {
+ return; // Don't save cross-user information.
+ }
+ }
+ spi.saveToXml(out, forBackup);
+ }
+
+ public static ShortcutUser loadFromXml(ShortcutService s, XmlPullParser parser, int userId,
+ boolean fromBackup) throws IOException, XmlPullParserException {
final ShortcutUser ret = new ShortcutUser(userId);
final int outerDepth = parser.getDepth();
@@ -111,29 +227,30 @@
}
final int depth = parser.getDepth();
final String tag = parser.getName();
- switch (tag) {
- case TAG_LAUNCHER: {
- ret.mLauncherComponent = ShortcutService.parseComponentNameAttribute(
- parser, ATTR_VALUE);
- continue;
- }
- case ShortcutPackage.TAG_ROOT: {
- final ShortcutPackage shortcuts = ShortcutPackage.loadFromXml(parser, userId);
- // Don't use addShortcut(), we don't need to save the icon.
- ret.getPackages().put(shortcuts.mPackageName, shortcuts);
- continue;
- }
+ if (depth == outerDepth + 1) {
+ switch (tag) {
+ case TAG_LAUNCHER: {
+ ret.mLauncherComponent = ShortcutService.parseComponentNameAttribute(
+ parser, ATTR_VALUE);
+ continue;
+ }
+ case ShortcutPackage.TAG_ROOT: {
+ final ShortcutPackage shortcuts = ShortcutPackage.loadFromXml(
+ s, parser, userId, fromBackup);
- case ShortcutLauncher.TAG_ROOT: {
- final ShortcutLauncher shortcuts =
- ShortcutLauncher.loadFromXml(parser, userId);
+ // Don't use addShortcut(), we don't need to save the icon.
+ ret.mPackages.put(shortcuts.getPackageName(), shortcuts);
+ continue;
+ }
- ret.getLaunchers().put(shortcuts.mPackageName, shortcuts);
- continue;
+ case ShortcutLauncher.TAG_ROOT: {
+ ret.addLauncher(ShortcutLauncher.loadFromXml(parser, userId, fromBackup));
+ continue;
+ }
}
}
- throw ShortcutService.throwForInvalidTag(depth, tag);
+ ShortcutService.warnForInvalidTag(depth, tag);
}
return ret;
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index ac19e24..06a91fb 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -20,6 +20,7 @@
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
@@ -30,13 +31,13 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Debug;
import android.os.Environment;
@@ -122,6 +123,7 @@
private static final String ATTR_ID = "id";
private static final String ATTR_CREATION_TIME = "created";
private static final String ATTR_LAST_LOGGED_IN_TIME = "lastLoggedIn";
+ private static final String ATTR_LAST_LOGGED_IN_FINGERPRINT = "lastLoggedInFingerprint";
private static final String ATTR_SERIAL_NO = "serialNumber";
private static final String ATTR_NEXT_SERIAL_NO = "nextSerialNumber";
private static final String ATTR_PARTIAL = "partial";
@@ -380,8 +382,6 @@
removeUserState(ui.id);
}
- onUserForeground(UserHandle.USER_SYSTEM);
-
mAppOpsService = IAppOpsService.Stub.asInterface(
ServiceManager.getService(Context.APP_OPS_SERVICE));
@@ -500,6 +500,9 @@
if (mRemovingUserIds.get(profile.id)) {
continue;
}
+ if (profile.partial) {
+ continue;
+ }
users.add(userWithName(profile));
}
return users;
@@ -573,12 +576,26 @@
private void broadcastProfileAvailabilityChanges(UserHandle profileHandle,
UserHandle parentHandle, boolean inQuietMode) {
- Intent intent = new Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED);
+ Intent intent = new Intent();
+ if (inQuietMode) {
+ intent.setAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
+ } else {
+ intent.setAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
+ }
intent.putExtra(Intent.EXTRA_QUIET_MODE, inQuietMode);
intent.putExtra(Intent.EXTRA_USER, profileHandle);
intent.putExtra(Intent.EXTRA_USER_HANDLE, profileHandle.getIdentifier());
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mContext.sendBroadcastAsUser(intent, parentHandle);
+
+ //TODO: remove once Launcher3 is updated.
+ Intent oldIntent = new Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED);
+ oldIntent.putExtra(Intent.EXTRA_QUIET_MODE, inQuietMode);
+ oldIntent.putExtra(Intent.EXTRA_USER, profileHandle);
+ oldIntent.putExtra(Intent.EXTRA_USER_HANDLE, profileHandle.getIdentifier());
+ oldIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ mContext.sendBroadcastAsUser(oldIntent, parentHandle);
+
}
@Override
@@ -923,12 +940,12 @@
}
// Don't call them within the mRestrictionsLock.
synchronized (mPackagesLock) {
- if (globalChanged) {
- writeUserListLP();
- }
if (localChanged) {
writeUserLP(getUserDataNoChecks(userId));
}
+ if (globalChanged) {
+ writeUserListLP();
+ }
}
synchronized (mRestrictionsLock) {
@@ -1490,8 +1507,8 @@
updateUserIds();
initDefaultGuestRestrictions();
- writeUserListLP();
writeUserLP(userData);
+ writeUserListLP();
}
private String getOwnerName() {
@@ -1541,6 +1558,10 @@
serializer.attribute(null, ATTR_CREATION_TIME, Long.toString(userInfo.creationTime));
serializer.attribute(null, ATTR_LAST_LOGGED_IN_TIME,
Long.toString(userInfo.lastLoggedInTime));
+ if (userInfo.lastLoggedInFingerprint != null) {
+ serializer.attribute(null, ATTR_LAST_LOGGED_IN_FINGERPRINT,
+ userInfo.lastLoggedInFingerprint);
+ }
if (userInfo.iconPath != null) {
serializer.attribute(null, ATTR_ICON_PATH, userInfo.iconPath);
}
@@ -1596,7 +1617,7 @@
serializer.endDocument();
userFile.finishWrite(fos);
} catch (Exception ioe) {
- Slog.e(LOG_TAG, "Error writing user info " + userData.info.id + "\n" + ioe);
+ Slog.e(LOG_TAG, "Error writing user info " + userData.info.id, ioe);
userFile.failWrite(fos);
}
}
@@ -1671,6 +1692,7 @@
String iconPath = null;
long creationTime = 0L;
long lastLoggedInTime = 0L;
+ String lastLoggedInFingerprint = null;
int profileGroupId = UserInfo.NO_PROFILE_GROUP_ID;
int restrictedProfileParentId = UserInfo.NO_PROFILE_GROUP_ID;
boolean partial = false;
@@ -1711,6 +1733,8 @@
iconPath = parser.getAttributeValue(null, ATTR_ICON_PATH);
creationTime = readLongAttribute(parser, ATTR_CREATION_TIME, 0);
lastLoggedInTime = readLongAttribute(parser, ATTR_LAST_LOGGED_IN_TIME, 0);
+ lastLoggedInFingerprint = parser.getAttributeValue(null,
+ ATTR_LAST_LOGGED_IN_FINGERPRINT);
profileGroupId = readIntAttribute(parser, ATTR_PROFILE_GROUP_ID,
UserInfo.NO_PROFILE_GROUP_ID);
restrictedProfileParentId = readIntAttribute(parser,
@@ -1763,6 +1787,7 @@
userInfo.serialNumber = serialNumber;
userInfo.creationTime = creationTime;
userInfo.lastLoggedInTime = lastLoggedInTime;
+ userInfo.lastLoggedInFingerprint = lastLoggedInFingerprint;
userInfo.partial = partial;
userInfo.guestToRemove = guestToRemove;
userInfo.profileGroupId = profileGroupId;
@@ -1933,10 +1958,12 @@
long now = System.currentTimeMillis();
userInfo.creationTime = (now > EPOCH_PLUS_30_YEARS) ? now : 0;
userInfo.partial = true;
+ userInfo.lastLoggedInFingerprint = Build.FINGERPRINT;
userData = new UserData();
userData.info = userInfo;
mUsers.put(userId, userData);
}
+ writeUserLP(userData);
writeUserListLP();
if (parent != null) {
if (isManagedProfile) {
@@ -2189,7 +2216,13 @@
}
private void removeUserState(final int userHandle) {
- mContext.getSystemService(StorageManager.class).destroyUserKey(userHandle);
+ try {
+ mContext.getSystemService(StorageManager.class).destroyUserKey(userHandle);
+ } catch (IllegalStateException e) {
+ // This may be simply because the user was partially created.
+ Slog.i(LOG_TAG,
+ "Destroying key for user " + userHandle + " failed, continuing anyway", e);
+ }
// Cleanup package manager settings
mPm.cleanUpUser(this, userHandle);
@@ -2204,13 +2237,13 @@
mCachedEffectiveUserRestrictions.remove(userHandle);
mDevicePolicyLocalUserRestrictions.remove(userHandle);
}
- // Remove user file
- AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + XML_SUFFIX));
- userFile.delete();
// Update the user list
synchronized (mPackagesLock) {
writeUserListLP();
}
+ // Remove user file
+ AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + XML_SUFFIX));
+ userFile.delete();
updateUserIds();
File userDir = Environment.getUserSystemDirectory(userHandle);
File renamedUserDir = Environment.getUserSystemDirectory(UserHandle.USER_NULL - userHandle);
@@ -2266,6 +2299,7 @@
if (restrictions == null || restrictions.isEmpty()) {
cleanAppRestrictionsForPackage(packageName, userId);
} else {
+ restrictions.setDefusable(true);
// Write the restrictions to XML
writeApplicationRestrictionsLP(packageName, restrictions, userId);
}
@@ -2553,7 +2587,7 @@
* Called right before a user is unlocked. This gives us a chance to prepare
* app storage.
*/
- public void onBeforeUnlockUser(int userId) {
+ public void onBeforeUnlockUser(@UserIdInt int userId) {
final int userSerial = getUserSerialNumber(userId);
prepareUserStorage(userId, userSerial, StorageManager.FLAG_STORAGE_CE);
mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_CE);
@@ -2563,17 +2597,19 @@
* Make a note of the last started time of a user and do some cleanup.
* @param userId the user that was just foregrounded
*/
- public void onUserForeground(int userId) {
+ public void onUserLoggedIn(@UserIdInt int userId) {
UserData userData = getUserDataNoChecks(userId);
if (userData == null || userData.info.partial) {
Slog.w(LOG_TAG, "userForeground: unknown user #" + userId);
return;
}
- long now = System.currentTimeMillis();
+
+ final long now = System.currentTimeMillis();
if (now > EPOCH_PLUS_30_YEARS) {
userData.info.lastLoggedInTime = now;
- scheduleWriteUser(userData);
}
+ userData.info.lastLoggedInFingerprint = Build.FINGERPRINT;
+ scheduleWriteUser(userData);
}
/**
@@ -2838,6 +2874,8 @@
sb.append(" ago");
pw.println(sb);
}
+ pw.print(" Last logged in fingerprint: ");
+ pw.println(userInfo.lastLoggedInFingerprint);
pw.print(" Has profile owner: ");
pw.println(mIsUserManaged.get(userId));
pw.println(" Restrictions:");
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 4b355de62..364e9fa6 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -425,6 +425,18 @@
}
}
}
+ break;
+ case UserManager.DISALLOW_SAFE_BOOT:
+ // Unlike with the other restrictions, we want to propagate the new value to
+ // the system settings even if it is false. The other restrictions modify
+ // settings which could be manually changed by the user from the Settings app
+ // after the policies enforcing these restrictions have been revoked, so we
+ // leave re-setting of those settings to the user.
+ android.provider.Settings.Global.putInt(
+ context.getContentResolver(),
+ android.provider.Settings.Global.SAFE_BOOT_DISALLOWED,
+ newValue ? 1 : 0);
+ break;
}
} finally {
Binder.restoreCallingIdentity(id);
diff --git a/services/core/java/com/android/server/policy/EnableAccessibilityController.java b/services/core/java/com/android/server/policy/EnableAccessibilityController.java
index da9c001..6b203a9 100644
--- a/services/core/java/com/android/server/policy/EnableAccessibilityController.java
+++ b/services/core/java/com/android/server/policy/EnableAccessibilityController.java
@@ -16,7 +16,9 @@
package com.android.server.policy;
+import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -32,19 +34,25 @@
import android.os.UserManager;
import android.provider.Settings;
import android.speech.tts.TextToSpeech;
+import android.util.Log;
import android.util.MathUtils;
import android.view.IWindowManager;
import android.view.MotionEvent;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
+import android.view.WindowManagerInternal;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IAccessibilityManager;
import com.android.internal.R;
+import com.android.server.LocalServices;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class EnableAccessibilityController {
+ private static final String TAG = "EnableAccessibilityController";
private static final int SPEAK_WARNING_DELAY_MILLIS = 2000;
private static final int ENABLE_ACCESSIBILITY_DELAY_MILLIS = 6000;
@@ -75,9 +83,6 @@
}
};
- private final IWindowManager mWindowManager = IWindowManager.Stub.asInterface(
- ServiceManager.getService("window"));
-
private final IAccessibilityManager mAccessibilityManager = IAccessibilityManager
.Stub.asInterface(ServiceManager.getService("accessibility"));
@@ -132,7 +137,7 @@
&& !getInstalledSpeakingAccessibilityServices(context).isEmpty();
}
- private static List<AccessibilityServiceInfo> getInstalledSpeakingAccessibilityServices(
+ public static List<AccessibilityServiceInfo> getInstalledSpeakingAccessibilityServices(
Context context) {
List<AccessibilityServiceInfo> services = new ArrayList<AccessibilityServiceInfo>();
services.addAll(AccessibilityManager.getInstance(context)
@@ -213,71 +218,74 @@
}
private void enableAccessibility() {
- List<AccessibilityServiceInfo> services = getInstalledSpeakingAccessibilityServices(
- mContext);
- if (services.isEmpty()) {
+ if (enableAccessibility(mContext)) {
+ mOnAccessibilityEnabledCallback.run();
+ }
+ }
+
+ public static boolean enableAccessibility(Context context) {
+ final IAccessibilityManager accessibilityManager = IAccessibilityManager
+ .Stub.asInterface(ServiceManager.getService("accessibility"));
+ final WindowManagerInternal windowManager = LocalServices.getService(
+ WindowManagerInternal.class);
+ final UserManager userManager = (UserManager) context.getSystemService(
+ Context.USER_SERVICE);
+ ComponentName componentName = getInstalledSpeakingAccessibilityServiceComponent(context);
+ if (componentName == null) {
+ return false;
+ }
+
+ boolean keyguardLocked = windowManager.isKeyguardLocked();
+ final boolean hasMoreThanOneUser = userManager.getUsers().size() > 1;
+ try {
+ if (!keyguardLocked || !hasMoreThanOneUser) {
+ final int userId = ActivityManager.getCurrentUser();
+ accessibilityManager.enableAccessibilityService(componentName, userId);
+ } else if (keyguardLocked) {
+ accessibilityManager.temporaryEnableAccessibilityStateUntilKeyguardRemoved(
+ componentName, true /* enableTouchExploration */);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "cannot enable accessibilty: " + e);
+ }
+
+ return true;
+ }
+
+ public static void disableAccessibility(Context context) {
+ final IAccessibilityManager accessibilityManager = IAccessibilityManager
+ .Stub.asInterface(ServiceManager.getService("accessibility"));
+ ComponentName componentName = getInstalledSpeakingAccessibilityServiceComponent(context);
+ if (componentName == null) {
return;
}
- boolean keyguardLocked = false;
+
+ final int userId = ActivityManager.getCurrentUser();
try {
- keyguardLocked = mWindowManager.isKeyguardLocked();
- } catch (RemoteException re) {
- /* ignore */
+ accessibilityManager.disableAccessibilityService(componentName, userId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "cannot disable accessibility " + e);
+ }
+ }
+
+ public static boolean isAccessibilityEnabled(Context context) {
+ final AccessibilityManager accessibilityManager =
+ context.getSystemService(AccessibilityManager.class);
+ List enabledServices = accessibilityManager.getEnabledAccessibilityServiceList(
+ AccessibilityServiceInfo.FEEDBACK_SPOKEN);
+ return enabledServices != null && !enabledServices.isEmpty();
+ }
+
+ @Nullable
+ public static ComponentName getInstalledSpeakingAccessibilityServiceComponent(
+ Context context) {
+ List<AccessibilityServiceInfo> services =
+ getInstalledSpeakingAccessibilityServices(context);
+ if (services.isEmpty()) {
+ return null;
}
- final boolean hasMoreThanOneUser = mUserManager.getUsers().size() > 1;
-
- AccessibilityServiceInfo service = services.get(0);
- boolean enableTouchExploration = (service.flags
- & AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0;
- // Try to find a service supporting explore by touch.
- if (!enableTouchExploration) {
- final int serviceCount = services.size();
- for (int i = 1; i < serviceCount; i++) {
- AccessibilityServiceInfo candidate = services.get(i);
- if ((candidate.flags & AccessibilityServiceInfo
- .FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0) {
- enableTouchExploration = true;
- service = candidate;
- break;
- }
- }
- }
-
- ServiceInfo serviceInfo = service.getResolveInfo().serviceInfo;
- ComponentName componentName = new ComponentName(serviceInfo.packageName, serviceInfo.name);
- if (!keyguardLocked || !hasMoreThanOneUser) {
- final int userId = ActivityManager.getCurrentUser();
- String enabledServiceString = componentName.flattenToString();
- ContentResolver resolver = mContext.getContentResolver();
- // Enable one speaking accessibility service.
- Settings.Secure.putStringForUser(resolver,
- Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
- enabledServiceString, userId);
- // Allow the services we just enabled to toggle touch exploration.
- Settings.Secure.putStringForUser(resolver,
- Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
- enabledServiceString, userId);
- // Enable touch exploration.
- if (enableTouchExploration) {
- Settings.Secure.putIntForUser(resolver, Settings.Secure.TOUCH_EXPLORATION_ENABLED,
- 1, userId);
- }
- // Enable accessibility script injection (AndroidVox) for web content.
- Settings.Secure.putIntForUser(resolver, Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION,
- 1, userId);
- // Turn on accessibility mode last.
- Settings.Secure.putIntForUser(resolver, Settings.Secure.ACCESSIBILITY_ENABLED,
- 1, userId);
- } else if (keyguardLocked) {
- try {
- mAccessibilityManager.temporaryEnableAccessibilityStateUntilKeyguardRemoved(
- componentName, enableTouchExploration);
- } catch (RemoteException re) {
- /* ignore */
- }
- }
-
- mOnAccessibilityEnabledCallback.run();
+ ServiceInfo serviceInfo = services.get(0).getResolveInfo().serviceInfo;
+ return new ComponentName(serviceInfo.packageName, serviceInfo.name);
}
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index d92d942..fb56a0c 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -18,7 +18,6 @@
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.HOME_STACK_ID;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static android.content.pm.PackageManager.FEATURE_TELEVISION;
@@ -38,7 +37,6 @@
import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED;
import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
-import android.Manifest;
import android.app.ActivityManager;
import android.app.ActivityManager.StackId;
import android.app.ActivityManagerInternal;
@@ -71,6 +69,7 @@
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPlaybackClient;
import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback;
+import android.hardware.input.InputManagerInternal;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.AudioSystem;
@@ -109,6 +108,7 @@
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
+import android.util.MutableBoolean;
import android.util.Slog;
import android.util.SparseArray;
import android.util.LongSparseArray;
@@ -306,6 +306,7 @@
WindowManagerInternal mWindowManagerInternal;
PowerManager mPowerManager;
ActivityManagerInternal mActivityManagerInternal;
+ InputManagerInternal mInputManagerInternal;
DreamManagerInternal mDreamManagerInternal;
PowerManagerInternal mPowerManagerInternal;
IStatusBarService mStatusBarService;
@@ -398,6 +399,8 @@
volatile boolean mBeganFromNonInteractive;
volatile int mPowerKeyPressCounter;
volatile boolean mEndCallKeyHandled;
+ volatile boolean mCameraGestureTriggeredDuringGoingToSleep;
+ volatile boolean mGoingToSleep;
boolean mRecentsVisible;
int mRecentAppsHeldModifiers;
@@ -603,6 +606,9 @@
boolean mConsumeSearchKeyUp;
boolean mAssistKeyLongPressed;
boolean mPendingMetaAction;
+ boolean mPendingCapsLockToggle;
+ int mMetaState;
+ int mInitialMetaState;
boolean mForceShowSystemBars;
// support for activating the lock screen while the screen is on
@@ -687,6 +693,7 @@
= new LogDecelerateInterpolator(100, 0);
private boolean mForceWindowDrawsStatusBarBackground;
+ private final MutableBoolean mTmpBoolean = new MutableBoolean(false);
private static final int MSG_ENABLE_POINTER_LOCATION = 1;
private static final int MSG_DISABLE_POINTER_LOCATION = 2;
@@ -1034,7 +1041,11 @@
GestureLauncherService.class);
boolean gesturedServiceIntercepted = false;
if (gestureService != null) {
- gesturedServiceIntercepted = gestureService.interceptPowerKeyDown(event, interactive);
+ gesturedServiceIntercepted = gestureService.interceptPowerKeyDown(event, interactive,
+ mTmpBoolean);
+ if (mTmpBoolean.value && mGoingToSleep) {
+ mCameraGestureTriggeredDuringGoingToSleep = true;
+ }
}
// If the power key has still not yet been handled, then detect short
@@ -1488,6 +1499,7 @@
mWindowManagerFuncs = windowManagerFuncs;
mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
+ mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class);
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
@@ -1819,41 +1831,6 @@
}
}
- mStatusBarHeight =
- res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
-
- // Height of the navigation bar when presented horizontally at bottom
- mNavigationBarHeightForRotationDefault[mPortraitRotation] =
- mNavigationBarHeightForRotationDefault[mUpsideDownRotation] =
- res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height);
- mNavigationBarHeightForRotationDefault[mLandscapeRotation] =
- mNavigationBarHeightForRotationDefault[mSeascapeRotation] = res.getDimensionPixelSize(
- com.android.internal.R.dimen.navigation_bar_height_landscape);
-
- // Width of the navigation bar when presented vertically along one side
- mNavigationBarWidthForRotationDefault[mPortraitRotation] =
- mNavigationBarWidthForRotationDefault[mUpsideDownRotation] =
- mNavigationBarWidthForRotationDefault[mLandscapeRotation] =
- mNavigationBarWidthForRotationDefault[mSeascapeRotation] =
- res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width);
-
- // Height of the navigation bar when presented horizontally at bottom
- mNavigationBarHeightForRotationInCarMode[mPortraitRotation] =
- mNavigationBarHeightForRotationInCarMode[mUpsideDownRotation] =
- res.getDimensionPixelSize(
- com.android.internal.R.dimen.navigation_bar_height_car_mode);
- mNavigationBarHeightForRotationInCarMode[mLandscapeRotation] =
- mNavigationBarHeightForRotationInCarMode[mSeascapeRotation] = res.getDimensionPixelSize(
- com.android.internal.R.dimen.navigation_bar_height_landscape_car_mode);
-
- // Width of the navigation bar when presented vertically along one side
- mNavigationBarWidthForRotationInCarMode[mPortraitRotation] =
- mNavigationBarWidthForRotationInCarMode[mUpsideDownRotation] =
- mNavigationBarWidthForRotationInCarMode[mLandscapeRotation] =
- mNavigationBarWidthForRotationInCarMode[mSeascapeRotation] =
- res.getDimensionPixelSize(
- com.android.internal.R.dimen.navigation_bar_width_car_mode);
-
// SystemUI (status bar) layout policy
int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT / density;
int longSizeDp = longSize * DisplayMetrics.DENSITY_DEFAULT / density;
@@ -1862,6 +1839,7 @@
mNavigationBarCanMove = width != height && shortSizeDp < 600;
mHasNavigationBar = res.getBoolean(com.android.internal.R.bool.config_showNavigationBar);
+
// Allow a system property to override this. Used by the emulator.
// See also hasNavigationBar().
String navBarOverride = SystemProperties.get("qemu.hw.mainkeys");
@@ -2292,6 +2270,46 @@
}
}
+ @Override
+ public void onConfigurationChanged() {
+ final Resources res = mContext.getResources();
+
+ mStatusBarHeight =
+ res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
+
+ // Height of the navigation bar when presented horizontally at bottom
+ mNavigationBarHeightForRotationDefault[mPortraitRotation] =
+ mNavigationBarHeightForRotationDefault[mUpsideDownRotation] =
+ res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height);
+ mNavigationBarHeightForRotationDefault[mLandscapeRotation] =
+ mNavigationBarHeightForRotationDefault[mSeascapeRotation] = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.navigation_bar_height_landscape);
+
+ // Width of the navigation bar when presented vertically along one side
+ mNavigationBarWidthForRotationDefault[mPortraitRotation] =
+ mNavigationBarWidthForRotationDefault[mUpsideDownRotation] =
+ mNavigationBarWidthForRotationDefault[mLandscapeRotation] =
+ mNavigationBarWidthForRotationDefault[mSeascapeRotation] =
+ res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width);
+
+ // Height of the navigation bar when presented horizontally at bottom
+ mNavigationBarHeightForRotationInCarMode[mPortraitRotation] =
+ mNavigationBarHeightForRotationInCarMode[mUpsideDownRotation] =
+ res.getDimensionPixelSize(
+ com.android.internal.R.dimen.navigation_bar_height_car_mode);
+ mNavigationBarHeightForRotationInCarMode[mLandscapeRotation] =
+ mNavigationBarHeightForRotationInCarMode[mSeascapeRotation] = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.navigation_bar_height_landscape_car_mode);
+
+ // Width of the navigation bar when presented vertically along one side
+ mNavigationBarWidthForRotationInCarMode[mPortraitRotation] =
+ mNavigationBarWidthForRotationInCarMode[mUpsideDownRotation] =
+ mNavigationBarWidthForRotationInCarMode[mLandscapeRotation] =
+ mNavigationBarWidthForRotationInCarMode[mSeascapeRotation] =
+ res.getDimensionPixelSize(
+ com.android.internal.R.dimen.navigation_bar_width_car_mode);
+ }
+
/** {@inheritDoc} */
@Override
public int windowTypeToLayerLw(int type) {
@@ -2777,9 +2795,19 @@
int insets = mWindowManagerFuncs.getDockedDividerInsetsLw();
// If the divider is behind the navigation bar, don't animate.
- if (mNavigationBar != null
- && (win.getFrameLw().top + insets >= mNavigationBar.getFrameLw().top
- || win.getFrameLw().left + insets >= mNavigationBar.getFrameLw().left)) {
+ final Rect frame = win.getFrameLw();
+ final boolean behindNavBar = mNavigationBar != null
+ && ((mNavigationBarOnBottom
+ && frame.top + insets >= mNavigationBar.getFrameLw().top)
+ || (!mNavigationBarOnBottom
+ && frame.left + insets >= mNavigationBar.getFrameLw().left));
+ final boolean landscape = frame.height() > frame.width();
+ final boolean offscreenLandscape = landscape && (frame.right - insets <= 0
+ || frame.left + insets >= win.getDisplayFrameLw().right);
+ final boolean offscreenPortrait = !landscape && (frame.top - insets <= 0
+ || frame.bottom + insets >= win.getDisplayFrameLw().bottom);
+ final boolean offscreen = offscreenLandscape || offscreenPortrait;
+ if (behindNavBar || offscreen) {
return 0;
}
if (transit == TRANSIT_ENTER || transit == TRANSIT_SHOW) {
@@ -2946,6 +2974,10 @@
if (mPendingMetaAction && !KeyEvent.isMetaKey(keyCode)) {
mPendingMetaAction = false;
}
+ // Any key that is not Alt or Meta cancels Caps Lock combo tracking.
+ if (mPendingCapsLockToggle && !KeyEvent.isMetaKey(keyCode) && !KeyEvent.isAltKey(keyCode)) {
+ mPendingCapsLockToggle = false;
+ }
// First we always handle the home key here, so applications
// can never break it, although if keyguard is on, we do let
@@ -3099,10 +3131,8 @@
return -1;
}
} else if (keyCode == KeyEvent.KEYCODE_SLASH && event.isMetaPressed()) {
- if (down) {
- if (repeatCount == 0) {
- toggleKeyboardShortcutsMenu(event.getDeviceId());
- }
+ if (down && repeatCount == 0 && !isKeyguardLocked()) {
+ toggleKeyboardShortcutsMenu(event.getDeviceId());
}
} else if (keyCode == KeyEvent.KEYCODE_ASSIST) {
if (down) {
@@ -3196,6 +3226,38 @@
}
}
+ // Toggle Caps Lock on META-ALT.
+ boolean actionTriggered = false;
+ if (KeyEvent.isModifierKey(keyCode)) {
+ if (!mPendingCapsLockToggle) {
+ // Start tracking meta state for combo.
+ mInitialMetaState = mMetaState;
+ mPendingCapsLockToggle = true;
+ } else if (event.getAction() == KeyEvent.ACTION_UP) {
+ int altOnMask = mMetaState & KeyEvent.META_ALT_MASK;
+ int metaOnMask = mMetaState & KeyEvent.META_META_MASK;
+
+ // Check for Caps Lock toggle
+ if ((metaOnMask != 0) && (altOnMask != 0)) {
+ // Check if nothing else is pressed
+ if (mInitialMetaState == (mMetaState ^ (altOnMask | metaOnMask))) {
+ // Handle Caps Lock Toggle
+ mInputManagerInternal.toggleCapsLock(event.getDeviceId());
+ actionTriggered = true;
+ }
+ }
+
+ // Always stop tracking when key goes up.
+ mPendingCapsLockToggle = false;
+ }
+ }
+ // Store current meta state to be able to evaluate it later.
+ mMetaState = metaState;
+
+ if (actionTriggered) {
+ return -1;
+ }
+
if (KeyEvent.isMetaKey(keyCode)) {
if (down) {
mPendingMetaAction = true;
@@ -4637,7 +4699,7 @@
// TYPE_SYSTEM_ERROR is above the NavigationBar so it can't be allowed to extend over it.
// Also, we don't allow windows in multi-window mode to extend out of the screen.
if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0 && attrs.type != TYPE_SYSTEM_ERROR
- && !win.inMultiWindowMode()) {
+ && !win.isInMultiWindowMode()) {
df.left = df.top = -10000;
df.right = df.bottom = 10000;
if (attrs.type != TYPE_WALLPAPER) {
@@ -5942,6 +6004,8 @@
@Override
public void startedGoingToSleep(int why) {
if (DEBUG_WAKEUP) Slog.i(TAG, "Started going to sleep... (why=" + why + ")");
+ mCameraGestureTriggeredDuringGoingToSleep = false;
+ mGoingToSleep = true;
if (mKeyguardDelegate != null) {
mKeyguardDelegate.onStartedGoingToSleep(why);
}
@@ -5954,6 +6018,8 @@
if (DEBUG_WAKEUP) Slog.i(TAG, "Finished going to sleep... (why=" + why + ")");
MetricsLogger.histogram(mContext, "screen_timeout", mLockScreenTimeout / 1000);
+ mGoingToSleep = false;
+
// We must get this work done here because the power manager will drop
// the wake lock and let the system suspend once this function returns.
synchronized (mLock) {
@@ -5963,8 +6029,10 @@
updateLockScreenTimeout();
}
if (mKeyguardDelegate != null) {
- mKeyguardDelegate.onFinishedGoingToSleep(why);
+ mKeyguardDelegate.onFinishedGoingToSleep(why,
+ mCameraGestureTriggeredDuringGoingToSleep);
}
+ mCameraGestureTriggeredDuringGoingToSleep = false;
}
// Called on the PowerManager's Notifier thread.
@@ -6289,8 +6357,7 @@
@Override
public boolean isNavBarForcedShownLw(WindowState windowState) {
- return mForceShowSystemBars
- && !windowState.getFrameLw().equals(windowState.getDisplayFrameLw());
+ return mForceShowSystemBars;
}
@Override
diff --git a/services/core/java/com/android/server/policy/StatusBarController.java b/services/core/java/com/android/server/policy/StatusBarController.java
index 9d353c6..86d0468 100644
--- a/services/core/java/com/android/server/policy/StatusBarController.java
+++ b/services/core/java/com/android/server/policy/StatusBarController.java
@@ -29,6 +29,8 @@
import android.view.animation.TranslateAnimation;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.server.LocalServices;
+import com.android.server.statusbar.StatusBarManagerInternal;
import static android.view.WindowManagerInternal.*;
@@ -103,6 +105,20 @@
}
});
}
+
+ @Override
+ public void onAppTransitionFinishedLocked(IBinder token) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ StatusBarManagerInternal statusbar = LocalServices.getService(
+ StatusBarManagerInternal.class);
+ if (statusbar != null) {
+ statusbar.appTransitionFinished();
+ }
+ }
+ });
+ }
};
public StatusBarController() {
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 8d296d5..52e5880 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -294,9 +294,9 @@
mKeyguardState.interactiveState = INTERACTIVE_STATE_GOING_TO_SLEEP;
}
- public void onFinishedGoingToSleep(int why) {
+ public void onFinishedGoingToSleep(int why, boolean cameraGestureTriggered) {
if (mKeyguardService != null) {
- mKeyguardService.onFinishedGoingToSleep(why);
+ mKeyguardService.onFinishedGoingToSleep(why, cameraGestureTriggered);
}
mKeyguardState.interactiveState = INTERACTIVE_STATE_SLEEP;
}
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
index 429b188..dacdec0 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
@@ -117,9 +117,9 @@
}
@Override
- public void onFinishedGoingToSleep(int reason) {
+ public void onFinishedGoingToSleep(int reason, boolean cameraGestureTriggered) {
try {
- mService.onFinishedGoingToSleep(reason);
+ mService.onFinishedGoingToSleep(reason, cameraGestureTriggered);
} catch (RemoteException e) {
Slog.w(TAG , "Remote Exception", e);
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index ff5a0f9..8cd536d 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -53,6 +53,8 @@
import android.provider.Settings.Secure;
import android.provider.Settings.SettingNotFoundException;
import android.service.dreams.DreamManagerInternal;
+import android.service.vr.IVrManager;
+import android.service.vr.IVrStateCallbacks;
import android.util.EventLog;
import android.util.Slog;
import android.util.SparseIntArray;
@@ -72,6 +74,7 @@
import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;
import com.android.server.vr.VrManagerInternal;
+import com.android.server.vr.VrManagerService;
import com.android.server.vr.VrStateListener;
import libcore.util.Objects;
@@ -538,7 +541,10 @@
@Override
public void onBootPhase(int phase) {
synchronized (mLock) {
- if (phase == PHASE_BOOT_COMPLETED) {
+ if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
+ incrementBootCount();
+
+ } else if (phase == PHASE_BOOT_COMPLETED) {
final long now = SystemClock.uptimeMillis();
mBootCompleted = true;
mDirty |= DIRTY_BOOT_COMPLETED;
@@ -553,8 +559,6 @@
}
}
mBootCompletedRunnables = null;
-
- incrementBootCount();
}
}
}
@@ -657,7 +661,13 @@
resolver.registerContentObserver(Settings.Secure.getUriFor(
Secure.BRIGHTNESS_USE_TWILIGHT),
false, mSettingsObserver, UserHandle.USER_ALL);
- getLocalService(VrManagerInternal.class).registerListener(mVrStateListener);
+ IVrManager vrManager =
+ (IVrManager) getBinderService(VrManagerService.VR_MANAGER_BINDER_SERVICE);
+ try {
+ vrManager.registerListener(mVrStateCallbacks);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to register VR mode state listener: " + e);
+ }
// Go.
readConfigurationLocked();
updateSettingsLocked();
@@ -3006,7 +3016,7 @@
}
}
- private final VrStateListener mVrStateListener = new VrStateListener() {
+ private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
@Override
public void onVrStateChanged(boolean enabled) {
powerHintInternal(POWER_HINT_VR_MODE, enabled ? 1 : 0);
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 6bda4ed..9614417 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -34,4 +34,5 @@
void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
Rect fullscreenBounds, Rect dockedBounds, String cause);
void toggleSplitScreen();
+ void appTransitionFinished();
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 403a4c6..dbbaa5e 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -214,6 +214,15 @@
} catch (RemoteException ex) {}
}
}
+
+ public void appTransitionFinished() {
+ enforceStatusBarService();
+ if (mBar != null) {
+ try {
+ mBar.appTransitionFinished();
+ } catch (RemoteException ex) {}
+ }
+ }
};
// ================================================================================
@@ -310,7 +319,7 @@
*/
@Override
public void disable2(int what, IBinder token, String pkg) {
- disableForUser(what, token, pkg, mCurrentUserId);
+ disable2ForUser(what, token, pkg, mCurrentUserId);
}
/**
diff --git a/services/core/java/com/android/server/utils/ManagedApplicationService.java b/services/core/java/com/android/server/utils/ManagedApplicationService.java
index ad8acef0..0f251fd 100644
--- a/services/core/java/com/android/server/utils/ManagedApplicationService.java
+++ b/services/core/java/com/android/server/utils/ManagedApplicationService.java
@@ -62,8 +62,6 @@
private IInterface mBoundInterface;
private PendingEvent mPendingEvent;
-
-
private ManagedApplicationService(final Context context, final ComponentName component,
final int userId, int clientLabel, String settingsAction,
BinderChecker binderChecker) {
@@ -211,6 +209,7 @@
} else {
// Service connection wasn't pending, must have been disconnected
mContext.unbindService(this);
+ return;
}
try {
@@ -242,6 +241,8 @@
@Override
public void onServiceDisconnected(ComponentName componentName) {
Slog.w(TAG, "Service disconnected: " + intent);
+ mConnection = null;
+ mBoundInterface = null;
}
};
diff --git a/services/core/java/com/android/server/vr/EnabledComponentsObserver.java b/services/core/java/com/android/server/vr/EnabledComponentsObserver.java
index 1363fb9..eb926c1 100644
--- a/services/core/java/com/android/server/vr/EnabledComponentsObserver.java
+++ b/services/core/java/com/android/server/vr/EnabledComponentsObserver.java
@@ -227,10 +227,11 @@
return userIds;
}
- private ArraySet<ComponentName> loadComponentNamesForUser(int userId) {
+ public static ArraySet<ComponentName> loadComponentNames(PackageManager pm, int userId,
+ String serviceName, String permissionName) {
+
ArraySet<ComponentName> installed = new ArraySet<>();
- PackageManager pm = mContext.getPackageManager();
- Intent queryIntent = new Intent(mServiceName);
+ Intent queryIntent = new Intent(serviceName);
List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
queryIntent,
PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
@@ -241,10 +242,10 @@
ServiceInfo info = resolveInfo.serviceInfo;
ComponentName component = new ComponentName(info.packageName, info.name);
- if (!mServicePermission.equals(info.permission)) {
+ if (!permissionName.equals(info.permission)) {
Slog.w(TAG, "Skipping service " + info.packageName + "/" + info.name
+ ": it does not require the permission "
- + mServicePermission);
+ + permissionName);
continue;
}
installed.add(component);
@@ -253,6 +254,11 @@
return installed;
}
+ private ArraySet<ComponentName> loadComponentNamesForUser(int userId) {
+ return loadComponentNames(mContext.getPackageManager(), userId, mServiceName,
+ mServicePermission);
+ }
+
private ArraySet<ComponentName> loadComponentNamesFromSetting(String settingName,
int userId) {
final ContentResolver cr = mContext.getContentResolver();
diff --git a/services/core/java/com/android/server/vr/VrManagerInternal.java b/services/core/java/com/android/server/vr/VrManagerInternal.java
index 8316efa..1bbb9f5 100644
--- a/services/core/java/com/android/server/vr/VrManagerInternal.java
+++ b/services/core/java/com/android/server/vr/VrManagerInternal.java
@@ -31,11 +31,15 @@
public static final int NO_ERROR = 0;
/**
- * Return current VR mode state.
+ * Return {@code true} if the given package is the currently bound VrListenerService for the
+ * given user.
*
- * @return {@code true} if VR mode is enabled.
+ * @param packageName The package name to check.
+ * @param userId the user ID to check the package name for.
+ *
+ * @return {@code true} if the given package is the currently bound VrListenerService.
*/
- public abstract boolean isInVrMode();
+ public abstract boolean isCurrentVrListener(String packageName, int userId);
/**
* Set the current VR mode state.
@@ -48,22 +52,6 @@
public abstract void setVrMode(boolean enabled, @NonNull ComponentName packageName,
int userId, @NonNull ComponentName calling);
- /**
- * Add a listener for VR mode state changes.
- * <p>
- * This listener will immediately be called with the current VR mode state.
- * </p>
- * @param listener the listener instance to add.
- */
- public abstract void registerListener(@NonNull VrStateListener listener);
-
- /**
- * Remove the listener from the current set of listeners.
- *
- * @param listener the listener to remove.
- */
- public abstract void unregisterListener(@NonNull VrStateListener listener);
-
/**
* Return NO_ERROR if the given package is installed on the device and enabled as a
* VrListenerService for the given current user, or a negative error code indicating a failure.
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index 6bf949c..e6e5a2d 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -15,18 +15,30 @@
*/
package com.android.server.vr;
+import android.Manifest;
import android.app.AppOpsManager;
+import android.app.NotificationManager;
import android.annotation.NonNull;
-import android.content.Context;
import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.provider.Settings;
+import android.service.notification.NotificationListenerService;
import android.service.vr.IVrListener;
+import android.service.vr.IVrManager;
+import android.service.vr.IVrStateCallbacks;
import android.service.vr.VrListenerService;
import android.util.ArraySet;
import android.util.Slog;
@@ -38,7 +50,10 @@
import com.android.server.utils.ManagedApplicationService;
import com.android.server.utils.ManagedApplicationService.BinderChecker;
+import java.lang.StringBuilder;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Objects;
import java.util.Set;
@@ -53,8 +68,8 @@
* hardware/libhardware/modules/vr
* <p/>
* In general applications may enable or disable VR mode by calling
- * {@link android.app.Activity#setVrMode)}. An application may also implement a service to be run
- * while in VR mode by implementing {@link android.service.vr.VrListenerService}.
+ * {@link android.app.Activity#setVrModeEnabled)}. An application may also implement a service to
+ * be run while in VR mode by implementing {@link android.service.vr.VrListenerService}.
*
* @see {@link android.service.vr.VrListenerService}
* @see {@link com.android.server.vr.VrManagerInternal}
@@ -66,6 +81,8 @@
public static final String TAG = "VrManagerService";
+ public static final String VR_MANAGER_BINDER_SERVICE = "vrmanager";
+
private static native void initializeNative();
private static native void setVrModeNative(boolean enabled);
@@ -74,13 +91,44 @@
private final IBinder mOverlayToken = new Binder();
// State protected by mLock
- private boolean mVrModeEnabled = false;
- private final Set<VrStateListener> mListeners = new ArraySet<>();
+ private boolean mVrModeEnabled;
private EnabledComponentsObserver mComponentObserver;
private ManagedApplicationService mCurrentVrService;
private Context mContext;
private ComponentName mCurrentVrModeComponent;
private int mCurrentVrModeUser;
+ private boolean mWasDefaultGranted;
+ private boolean mGuard;
+ private final RemoteCallbackList<IVrStateCallbacks> mRemoteCallbacks =
+ new RemoteCallbackList<>();
+ private final ArraySet<String> mPreviousToggledListenerSettings = new ArraySet<>();
+ private String mPreviousNotificationPolicyAccessPackage;
+ private String mPreviousManageOverlayPackage;
+
+ private static final int MSG_VR_STATE_CHANGE = 0;
+
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch(msg.what) {
+ case MSG_VR_STATE_CHANGE : {
+ boolean state = (msg.arg1 == 1);
+ int i = mRemoteCallbacks.beginBroadcast();
+ while (i > 0) {
+ i--;
+ try {
+ mRemoteCallbacks.getBroadcastItem(i).onVrStateChanged(state);
+ } catch (RemoteException e) {
+ // Noop
+ }
+ }
+ mRemoteCallbacks.finishBroadcast();
+ } break;
+ default :
+ throw new IllegalStateException("Unknown message type: " + msg.what);
+ }
+ }
+ };
private static final BinderChecker sBinderChecker = new BinderChecker() {
@Override
@@ -111,29 +159,55 @@
}
}
+ private final IVrManager mVrManager = new IVrManager.Stub() {
+
+ @Override
+ public void registerListener(IVrStateCallbacks cb) {
+ enforceCallerPermission(Manifest.permission.ACCESS_VR_MANAGER);
+ if (cb == null) {
+ throw new IllegalArgumentException("Callback binder object is null.");
+ }
+
+ VrManagerService.this.addStateCallback(cb);
+ }
+
+ @Override
+ public void unregisterListener(IVrStateCallbacks cb) {
+ enforceCallerPermission(Manifest.permission.ACCESS_VR_MANAGER);
+ if (cb == null) {
+ throw new IllegalArgumentException("Callback binder object is null.");
+ }
+
+ VrManagerService.this.removeStateCallback(cb);
+ }
+
+ @Override
+ public boolean getVrModeState() {
+ return VrManagerService.this.getVrMode();
+ }
+
+ };
+
+ private void enforceCallerPermission(String permission) {
+ if (mContext.checkCallingOrSelfPermission(permission)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Caller does not hold the permission " + permission);
+ }
+ }
+
/**
* Implementation of VrManagerInternal. Callable only from system services.
*/
private final class LocalService extends VrManagerInternal {
@Override
- public boolean isInVrMode() {
- return VrManagerService.this.getVrMode();
- }
-
- @Override
public void setVrMode(boolean enabled, ComponentName packageName, int userId,
ComponentName callingPackage) {
VrManagerService.this.setVrMode(enabled, packageName, userId, callingPackage);
}
@Override
- public void registerListener(VrStateListener listener) {
- VrManagerService.this.addListener(listener);
- }
-
- @Override
- public void unregisterListener(VrStateListener listener) {
- VrManagerService.this.removeListener(listener);
+ public boolean isCurrentVrListener(String packageName, int userId) {
+ return VrManagerService.this.isCurrentVrListener(packageName, userId);
}
@Override
@@ -154,6 +228,7 @@
}
publishLocalService(VrManagerInternal.class, new LocalService());
+ publishBinderService(VR_MANAGER_BINDER_SERVICE, mVrManager.asBinder());
}
@Override
@@ -234,62 +309,271 @@
*
* @return {@code true} if the component/user combination specified is valid.
*/
- private boolean updateCurrentVrServiceLocked(boolean enabled,
- @NonNull ComponentName component, int userId, ComponentName calling) {
+ private boolean updateCurrentVrServiceLocked(boolean enabled, @NonNull ComponentName component,
+ int userId, ComponentName calling) {
boolean sendUpdatedCaller = false;
+ final long identity = Binder.clearCallingIdentity();
+ try {
- boolean validUserComponent = (mComponentObserver.isValid(component, userId) ==
- EnabledComponentsObserver.NO_ERROR);
+ boolean validUserComponent = (mComponentObserver.isValid(component, userId) ==
+ EnabledComponentsObserver.NO_ERROR);
- // Always send mode change events.
- changeVrModeLocked(enabled, (enabled && validUserComponent) ? component : null);
+ // Always send mode change events.
+ changeVrModeLocked(enabled, (enabled && validUserComponent) ? component : null);
- if (!enabled || !validUserComponent) {
- // Unbind whatever is running
- if (mCurrentVrService != null) {
- Slog.i(TAG, "Disconnecting " + mCurrentVrService.getComponent() + " for user " +
- mCurrentVrService.getUserId());
- mCurrentVrService.disconnect();
- mCurrentVrService = null;
- }
- } else {
- if (mCurrentVrService != null) {
- // Unbind any running service that doesn't match the component/user selection
- if (mCurrentVrService.disconnectIfNotMatching(component, userId)) {
+ if (!enabled || !validUserComponent) {
+ // Unbind whatever is running
+ if (mCurrentVrService != null) {
Slog.i(TAG, "Disconnecting " + mCurrentVrService.getComponent() + " for user " +
- mCurrentVrService.getUserId());
+ mCurrentVrService.getUserId());
+ mCurrentVrService.disconnect();
+ disableImpliedPermissionsLocked(mCurrentVrService.getComponent(),
+ new UserHandle(mCurrentVrService.getUserId()));
+ mCurrentVrService = null;
+ }
+ } else {
+ if (mCurrentVrService != null) {
+ // Unbind any running service that doesn't match the component/user selection
+ if (mCurrentVrService.disconnectIfNotMatching(component, userId)) {
+ Slog.i(TAG, "Disconnecting " + mCurrentVrService.getComponent() +
+ " for user " + mCurrentVrService.getUserId());
+ disableImpliedPermissionsLocked(mCurrentVrService.getComponent(),
+ new UserHandle(mCurrentVrService.getUserId()));
+ createAndConnectService(component, userId);
+ enableImpliedPermissionsLocked(mCurrentVrService.getComponent(),
+ new UserHandle(mCurrentVrService.getUserId()));
+ sendUpdatedCaller = true;
+ }
+ // The service with the correct component/user is bound
+ } else {
+ // Nothing was previously running, bind a new service
createAndConnectService(component, userId);
+ enableImpliedPermissionsLocked(mCurrentVrService.getComponent(),
+ new UserHandle(mCurrentVrService.getUserId()));
sendUpdatedCaller = true;
}
- // The service with the correct component/user is bound
- } else {
- // Nothing was previously running, bind a new service
- createAndConnectService(component, userId);
+ }
+
+ if (calling != null && !Objects.equals(calling, mCurrentVrModeComponent)) {
+ mCurrentVrModeComponent = calling;
+ mCurrentVrModeUser = userId;
sendUpdatedCaller = true;
}
+
+ if (mCurrentVrService != null && sendUpdatedCaller) {
+ final ComponentName c = mCurrentVrModeComponent;
+ mCurrentVrService.sendEvent(new PendingEvent() {
+ @Override
+ public void runEvent(IInterface service) throws RemoteException {
+ IVrListener l = (IVrListener) service;
+ l.focusedActivityChanged(c);
+ }
+ });
+ }
+
+ return validUserComponent;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Enable the permission given in {@link #IMPLIED_VR_LISTENER_PERMISSIONS} for the given
+ * component package and user.
+ *
+ * @param component the component whose package should be enabled.
+ * @param userId the user that owns the given component.
+ */
+ private void enableImpliedPermissionsLocked(ComponentName component, UserHandle userId) {
+ if (mGuard) {
+ // Impossible
+ throw new IllegalStateException("Enabling permissions without disabling.");
+ }
+ mGuard = true;
+
+ PackageManager pm = mContext.getPackageManager();
+
+ String pName = component.getPackageName();
+ if (pm == null) {
+ Slog.e(TAG, "Couldn't set implied permissions for " + pName +
+ ", PackageManager isn't running");
+ return;
+ }
+
+ ApplicationInfo info = null;
+ try {
+ info = pm.getApplicationInfo(pName, PackageManager.GET_META_DATA);
+ } catch (NameNotFoundException e) {
+ }
+
+ if (info == null) {
+ Slog.e(TAG, "Couldn't set implied permissions for " + pName + ", no such package.");
+ return;
+ }
+
+ if (!(info.isSystemApp() || info.isUpdatedSystemApp())) {
+ return; // Application is not pre-installed, avoid setting implied permissions
+ }
+
+ mWasDefaultGranted = true;
+
+ grantOverlayAccess(pName, userId);
+ grantNotificationPolicyAccess(pName);
+ grantNotificationListenerAccess(pName, userId);
+ }
+
+ /**
+ * Disable the permission given in {@link #IMPLIED_VR_LISTENER_PERMISSIONS} for the given
+ * component package and user.
+ *
+ * @param component the component whose package should be disabled.
+ * @param userId the user that owns the given component.
+ */
+ private void disableImpliedPermissionsLocked(ComponentName component, UserHandle userId) {
+ if (!mGuard) {
+ // Impossible
+ throw new IllegalStateException("Disabling permissions without enabling.");
+ }
+ mGuard = false;
+
+ PackageManager pm = mContext.getPackageManager();
+
+ if (pm == null) {
+ Slog.e(TAG, "Couldn't remove implied permissions for " + component +
+ ", PackageManager isn't running");
+ return;
+ }
+
+ String pName = component.getPackageName();
+ if (mWasDefaultGranted) {
+ revokeOverlayAccess(userId);
+ revokeNotificationPolicyAccess(pName);
+ revokeNotificiationListenerAccess();
+ mWasDefaultGranted = false;
+ }
+
+ }
+
+ private void grantOverlayAccess(String pkg, UserHandle userId) {
+ PackageManager pm = mContext.getPackageManager();
+ boolean prev = (PackageManager.PERMISSION_GRANTED ==
+ pm.checkPermission(android.Manifest.permission.SYSTEM_ALERT_WINDOW, pkg));
+ mPreviousManageOverlayPackage = null;
+ if (!prev) {
+ pm.grantRuntimePermission(pkg, android.Manifest.permission.SYSTEM_ALERT_WINDOW,
+ userId);
+ mPreviousManageOverlayPackage = pkg;
+ }
+ }
+
+ private void revokeOverlayAccess(UserHandle userId) {
+ PackageManager pm = mContext.getPackageManager();
+ if (mPreviousManageOverlayPackage != null) {
+ pm.revokeRuntimePermission(mPreviousManageOverlayPackage,
+ android.Manifest.permission.SYSTEM_ALERT_WINDOW, userId);
+ mPreviousManageOverlayPackage = null;
+ }
+ }
+
+
+ private void grantNotificationPolicyAccess(String pkg) {
+ NotificationManager nm = mContext.getSystemService(NotificationManager.class);
+ boolean prev = nm.isNotificationPolicyAccessGrantedForPackage(pkg);
+ mPreviousNotificationPolicyAccessPackage = null;
+ if (!prev) {
+ mPreviousNotificationPolicyAccessPackage = pkg;
+ nm.setNotificationPolicyAccessGranted(pkg, true);
+ }
+ }
+
+ private void revokeNotificationPolicyAccess(String pkg) {
+ NotificationManager nm = mContext.getSystemService(NotificationManager.class);
+ if (mPreviousNotificationPolicyAccessPackage != null) {
+ nm.setNotificationPolicyAccessGranted(mPreviousNotificationPolicyAccessPackage, false);
+ mPreviousNotificationPolicyAccessPackage = null;
+ }
+ }
+
+ private void grantNotificationListenerAccess(String pkg, UserHandle userId) {
+ PackageManager pm = mContext.getPackageManager();
+ ArraySet<ComponentName> possibleServices = EnabledComponentsObserver.loadComponentNames(pm,
+ userId.getIdentifier(), NotificationListenerService.SERVICE_INTERFACE,
+ android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE);
+ ContentResolver resolver = mContext.getContentResolver();
+
+ ArraySet<String> current = getCurrentNotifListeners(resolver);
+
+ mPreviousToggledListenerSettings.clear();
+
+ for (ComponentName c : possibleServices) {
+ String flatName = c.flattenToString();
+ if (Objects.equals(c.getPackageName(), pkg)
+ && !current.contains(flatName)) {
+ mPreviousToggledListenerSettings.add(flatName);
+ current.add(flatName);
+ }
}
- if (calling != null && !Objects.equals(calling, mCurrentVrModeComponent)) {
- mCurrentVrModeComponent = calling;
- mCurrentVrModeUser = userId;
- sendUpdatedCaller = true;
+ if (current.size() > 0) {
+ String flatSettings = formatSettings(current);
+ Settings.Secure.putString(resolver, Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
+ flatSettings);
}
-
- if (mCurrentVrService != null && sendUpdatedCaller) {
- final ComponentName c = mCurrentVrModeComponent;
- mCurrentVrService.sendEvent(new PendingEvent() {
- @Override
- public void runEvent(IInterface service) throws RemoteException {
- IVrListener l = (IVrListener) service;
- l.focusedActivityChanged(c);
- }
- });
- }
-
- return validUserComponent;
}
+ private void revokeNotificiationListenerAccess() {
+ if (mPreviousToggledListenerSettings.isEmpty()) {
+ return;
+ }
+
+ ContentResolver resolver = mContext.getContentResolver();
+ ArraySet<String> current = getCurrentNotifListeners(resolver);
+
+ current.removeAll(mPreviousToggledListenerSettings);
+ mPreviousToggledListenerSettings.clear();
+
+ String flatSettings = formatSettings(current);
+ Settings.Secure.putString(resolver, Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
+ flatSettings);
+ }
+
+ private ArraySet<String> getCurrentNotifListeners(ContentResolver resolver) {
+ String flat = Settings.Secure.getString(resolver,
+ Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
+
+ ArraySet<String> current = new ArraySet<>();
+ if (flat != null) {
+ String[] allowed = flat.split(":");
+ for (String s : allowed) {
+ current.add(s);
+ }
+ }
+ return current;
+ }
+
+ private static String formatSettings(Collection<String> c) {
+ if (c == null || c.isEmpty()) {
+ return "";
+ }
+
+ StringBuilder b = new StringBuilder();
+ boolean start = true;
+ for (String s : c) {
+ if ("".equals(s)) {
+ continue;
+ }
+ if (!start) {
+ b.append(':');
+ }
+ b.append(s);
+ start = false;
+ }
+ return b.toString();
+ }
+
+
+
private void createAndConnectService(@NonNull ComponentName component, int userId) {
mCurrentVrService = VrManagerService.create(mContext, component, userId);
mCurrentVrService.connect();
@@ -323,9 +607,8 @@
* Note: Must be called while holding {@code mLock}.
*/
private void onVrModeChangedLocked() {
- for (VrStateListener l : mListeners) {
- l.onVrStateChanged(mVrModeEnabled);
- }
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_VR_STATE_CHANGE,
+ (mVrModeEnabled) ? 1 : 0, 0));
}
/**
@@ -349,27 +632,37 @@
}
}
- private boolean getVrMode() {
- synchronized (mLock) {
- return mVrModeEnabled;
- }
- }
-
- private void addListener(VrStateListener listener) {
- synchronized (mLock) {
- mListeners.add(listener);
- }
- }
-
- private void removeListener(VrStateListener listener) {
- synchronized (mLock) {
- mListeners.remove(listener);
- }
- }
-
private int hasVrPackage(@NonNull ComponentName targetPackageName, int userId) {
synchronized (mLock) {
return mComponentObserver.isValid(targetPackageName, userId);
}
}
+
+ private boolean isCurrentVrListener(String packageName, int userId) {
+ synchronized (mLock) {
+ if (mCurrentVrService == null) {
+ return false;
+ }
+ return mCurrentVrService.getComponent().getPackageName().equals(packageName) &&
+ userId == mCurrentVrService.getUserId();
+ }
+ }
+
+ /*
+ * Implementation of IVrManager calls.
+ */
+
+ private void addStateCallback(IVrStateCallbacks cb) {
+ mRemoteCallbacks.register(cb);
+ }
+
+ private void removeStateCallback(IVrStateCallbacks cb) {
+ mRemoteCallbacks.unregister(cb);
+ }
+
+ private boolean getVrMode() {
+ synchronized (mLock) {
+ return mVrModeEnabled;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index fb9b1ce..fb3c6ec 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -16,8 +16,8 @@
package com.android.server.wallpaper;
-import static android.app.WallpaperManager.FLAG_SET_SYSTEM;
-import static android.app.WallpaperManager.FLAG_SET_LOCK;
+import static android.app.WallpaperManager.FLAG_SYSTEM;
+import static android.app.WallpaperManager.FLAG_LOCK;
import static android.os.ParcelFileDescriptor.*;
import android.app.ActivityManager;
@@ -231,7 +231,7 @@
false, wallpaper, null);
}
if (lockWallpaperChanged
- || (wallpaper.whichPending & FLAG_SET_LOCK) != 0) {
+ || (wallpaper.whichPending & FLAG_LOCK) != 0) {
if (DEBUG) {
Slog.i(TAG, "Lock-relevant wallpaper changed");
}
@@ -505,7 +505,7 @@
&& mWallpaper.lastDiedTime + MIN_WALLPAPER_CRASH_TIME
> SystemClock.uptimeMillis()) {
Slog.w(TAG, "Reverting to built-in wallpaper!");
- clearWallpaperLocked(true, FLAG_SET_SYSTEM, mWallpaper.userId, null);
+ clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null);
} else {
mWallpaper.lastDiedTime = SystemClock.uptimeMillis();
}
@@ -584,7 +584,7 @@
if (!bindWallpaperComponentLocked(comp, false, false,
wallpaper, null)) {
Slog.w(TAG, "Wallpaper no longer available; reverting to default");
- clearWallpaperLocked(false, FLAG_SET_SYSTEM, wallpaper.userId, null);
+ clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
}
}
}
@@ -664,7 +664,7 @@
if (doit) {
Slog.w(TAG, "Wallpaper uninstalled, removing: "
+ wallpaper.wallpaperComponent);
- clearWallpaperLocked(false, FLAG_SET_SYSTEM, wallpaper.userId, null);
+ clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
}
}
}
@@ -684,7 +684,7 @@
} catch (NameNotFoundException e) {
Slog.w(TAG, "Wallpaper component gone, removing: "
+ wallpaper.wallpaperComponent);
- clearWallpaperLocked(false, FLAG_SET_SYSTEM, wallpaper.userId, null);
+ clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
}
}
if (wallpaper.nextWallpaperComponent != null
@@ -746,7 +746,7 @@
if (DEBUG) {
Slog.i(TAG, "Unable to regenerate crop; resetting");
}
- clearWallpaperLocked(false, FLAG_SET_SYSTEM, UserHandle.USER_SYSTEM, null);
+ clearWallpaperLocked(false, FLAG_SYSTEM, UserHandle.USER_SYSTEM, null);
}
} else {
if (DEBUG) {
@@ -842,7 +842,7 @@
void switchUser(int userId, IRemoteCallback reply) {
synchronized (mLock) {
mCurrentUserId = userId;
- WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SET_SYSTEM);
+ WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
// Not started watching yet, in case wallpaper data was loaded for other reasons.
if (wallpaper.wallpaperObserver == null) {
wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper);
@@ -865,7 +865,7 @@
e = e1;
}
Slog.w(TAG, "Failure starting previous wallpaper", e);
- clearWallpaperLocked(false, FLAG_SET_SYSTEM, wallpaper.userId, reply);
+ clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, reply);
}
}
@@ -885,12 +885,12 @@
}
void clearWallpaperLocked(boolean defaultFailed, int which, int userId, IRemoteCallback reply) {
- if (which != FLAG_SET_SYSTEM && which != FLAG_SET_LOCK) {
+ if (which != FLAG_SYSTEM && which != FLAG_LOCK) {
throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to read");
}
WallpaperData wallpaper = null;
- if (which == FLAG_SET_LOCK) {
+ if (which == FLAG_LOCK) {
wallpaper = mLockWallpaperMap.get(userId);
if (wallpaper == null) {
// It's already gone; we're done.
@@ -916,7 +916,7 @@
if (wallpaper.wallpaperFile.exists()) {
wallpaper.wallpaperFile.delete();
wallpaper.cropFile.delete();
- if (which == FLAG_SET_LOCK) {
+ if (which == FLAG_LOCK) {
mLockWallpaperMap.remove(userId);
final IWallpaperManagerCallback cb = mKeyguardListener;
if (cb != null) {
@@ -1008,7 +1008,7 @@
}
synchronized (mLock) {
int userId = UserHandle.getCallingUserId();
- WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SET_SYSTEM);
+ WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
if (width <= 0 || height <= 0) {
throw new IllegalArgumentException("width and height must be > 0");
}
@@ -1070,7 +1070,7 @@
}
synchronized (mLock) {
int userId = UserHandle.getCallingUserId();
- WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SET_SYSTEM);
+ WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
if (padding.left < 0 || padding.top < 0 || padding.right < 0 || padding.bottom < 0) {
throw new IllegalArgumentException("padding must be positive: " + padding);
}
@@ -1103,13 +1103,13 @@
wallpaperUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), wallpaperUserId, false, true, "getWallpaper", null);
- if (which != FLAG_SET_SYSTEM && which != FLAG_SET_LOCK) {
+ if (which != FLAG_SYSTEM && which != FLAG_LOCK) {
throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to read");
}
synchronized (mLock) {
final SparseArray<WallpaperData> whichSet =
- (which == FLAG_SET_LOCK) ? mLockWallpaperMap : mWallpaperMap;
+ (which == FLAG_LOCK) ? mLockWallpaperMap : mWallpaperMap;
WallpaperData wallpaper = whichSet.get(wallpaperUserId);
if (wallpaper == null) {
// common case, this is the first lookup post-boot of the system or
@@ -1157,12 +1157,12 @@
userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId, false, true, "getWallpaperIdForUser", null);
- if (which != FLAG_SET_SYSTEM && which != FLAG_SET_LOCK) {
+ if (which != FLAG_SYSTEM && which != FLAG_LOCK) {
throw new IllegalArgumentException("Must specify exactly one kind of wallpaper");
}
final SparseArray<WallpaperData> map =
- (which == FLAG_SET_LOCK) ? mLockWallpaperMap : mWallpaperMap;
+ (which == FLAG_LOCK) ? mLockWallpaperMap : mWallpaperMap;
synchronized (mLock) {
WallpaperData wallpaper = map.get(userId);
if (wallpaper != null) {
@@ -1186,7 +1186,7 @@
Rect cropHint, Bundle extras, int which, IWallpaperManagerCallback completion) {
checkPermission(android.Manifest.permission.SET_WALLPAPER);
- if ((which & (FLAG_SET_LOCK|FLAG_SET_SYSTEM)) == 0) {
+ if ((which & (FLAG_LOCK|FLAG_SYSTEM)) == 0) {
Slog.e(TAG, "Must specify a valid wallpaper category to set");
return null;
}
@@ -1262,6 +1262,7 @@
return null;
}
+ @Override
public void setWallpaperComponentChecked(ComponentName name, String callingPackage) {
if (isWallpaperSupported(callingPackage) && isWallpaperSettingAllowed(callingPackage)) {
setWallpaperComponent(name);
@@ -1269,6 +1270,7 @@
}
// ToDo: Remove this version of the function
+ @Override
public void setWallpaperComponent(ComponentName name) {
checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT);
synchronized (mLock) {
@@ -1281,7 +1283,10 @@
final long ident = Binder.clearCallingIdentity();
try {
wallpaper.imageWallpaperPending = false;
- bindWallpaperComponentLocked(name, false, true, wallpaper, null);
+ if (bindWallpaperComponentLocked(name, false, true, wallpaper, null)) {
+ wallpaper.wallpaperId = makeWallpaperIdLocked();
+ notifyCallbacksLocked(wallpaper);
+ }
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -1633,7 +1638,7 @@
// Combined or just-system operations use the 'system' WallpaperData
// for this use; lock-only operations use the dedicated one.
final SparseArray<WallpaperData> whichSet =
- (which == FLAG_SET_LOCK) ? mLockWallpaperMap : mWallpaperMap;
+ (which == FLAG_LOCK) ? mLockWallpaperMap : mWallpaperMap;
WallpaperData wallpaper = whichSet.get(userId);
if (wallpaper == null) {
// common case, this is the first lookup post-boot of the system or
@@ -1644,7 +1649,7 @@
// yet a lock-only wallpaper set for this user, so we need to establish
// it now.
if (wallpaper == null) {
- if (which == FLAG_SET_LOCK) {
+ if (which == FLAG_LOCK) {
wallpaper = new WallpaperData(userId,
WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
mLockWallpaperMap.put(userId, wallpaper);
diff --git a/services/core/java/com/android/server/webkit/WebViewUtilityImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java
similarity index 61%
rename from services/core/java/com/android/server/webkit/WebViewUtilityImpl.java
rename to services/core/java/com/android/server/webkit/SystemImpl.java
index 4dbd02d..9486cfd 100644
--- a/services/core/java/com/android/server/webkit/WebViewUtilityImpl.java
+++ b/services/core/java/com/android/server/webkit/SystemImpl.java
@@ -19,15 +19,22 @@
import android.app.ActivityManagerNative;
import android.app.AppGlobals;
import android.content.Context;
+import android.content.pm.IPackageDeleteObserver;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.UserInfo;
import android.content.res.XmlResourceParser;
+import android.os.Build;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings.Global;
import android.provider.Settings;
import android.util.AndroidRuntimeException;
import android.util.Log;
-import android.webkit.WebViewFactory;
import android.webkit.WebViewFactory.MissingWebViewPackageException;
+import android.webkit.WebViewFactory;
import android.webkit.WebViewProviderInfo;
import com.android.internal.util.XmlUtils;
@@ -42,8 +49,8 @@
* Default implementation for the WebView preparation Utility interface.
* @hide
*/
-public class WebViewUtilityImpl implements WebViewUtilityInterface {
- private static final String TAG = WebViewUtilityImpl.class.getSimpleName();
+public class SystemImpl implements SystemInterface {
+ private static final String TAG = SystemImpl.class.getSimpleName();
private static final String TAG_START = "webviewproviders";
private static final String TAG_WEBVIEW_PROVIDER = "webviewprovider";
private static final String TAG_PACKAGE_NAME = "packageName";
@@ -87,10 +94,10 @@
parser.getAttributeValue(null, TAG_AVAILABILITY));
boolean isFallback = "true".equals(
parser.getAttributeValue(null, TAG_FALLBACK));
- WebViewProviderInfo currentProvider =
- new WebViewProviderInfo(packageName, description, availableByDefault,
- isFallback, readSignatures(parser));
- if (currentProvider.isFallbackPackage()) {
+ WebViewProviderInfo currentProvider = new WebViewProviderInfo(
+ packageName, description, availableByDefault, isFallback,
+ readSignatures(parser));
+ if (currentProvider.isFallback) {
numFallbackPackages++;
if (numFallbackPackages > 1) {
throw new AndroidRuntimeException(
@@ -103,9 +110,7 @@
Log.e(TAG, "Found an element that is not a webview provider");
}
}
- } catch(XmlPullParserException e) {
- throw new MissingWebViewPackageException("Error when parsing WebView meta data " + e);
- } catch(IOException e) {
+ } catch (XmlPullParserException | IOException e) {
throw new MissingWebViewPackageException("Error when parsing WebView meta data " + e);
} finally {
if (parser != null) parser.close();
@@ -113,6 +118,11 @@
return webViewProviders.toArray(new WebViewProviderInfo[webViewProviders.size()]);
}
+ public int getFactoryPackageVersion(String packageName) throws NameNotFoundException {
+ PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
+ return pm.getPackageInfo(packageName, PackageManager.MATCH_FACTORY_ONLY).versionCode;
+ }
+
/**
* Reads all signatures at the current depth (within the current provider) from the XML parser.
*/
@@ -158,4 +168,70 @@
} catch (RemoteException e) {
}
}
+
+ @Override
+ public boolean isFallbackLogicEnabled() {
+ // Note that this is enabled by default (i.e. if the setting hasn't been set).
+ return Settings.Global.getInt(AppGlobals.getInitialApplication().getContentResolver(),
+ Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED, 1) == 1;
+ }
+
+ @Override
+ public void enableFallbackLogic(boolean enable) {
+ Settings.Global.putInt(AppGlobals.getInitialApplication().getContentResolver(),
+ Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED, enable ? 1 : 0);
+ }
+
+ @Override
+ public void uninstallAndDisablePackageForAllUsers(Context context, String packageName) {
+ enablePackageForAllUsers(context, packageName, false);
+ try {
+ PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
+ if (pm.getApplicationInfo(packageName, 0).isUpdatedSystemApp()) {
+ pm.deletePackage(packageName, new IPackageDeleteObserver.Stub() {
+ public void packageDeleted(String packageName, int returnCode) {
+ enablePackageForAllUsers(context, packageName, false);
+ }
+ }, PackageManager.DELETE_SYSTEM_APP | PackageManager.DELETE_ALL_USERS);
+ }
+ } catch (NameNotFoundException e) {
+ }
+ }
+
+ @Override
+ public void enablePackageForAllUsers(Context context, String packageName, boolean enable) {
+ UserManager userManager = (UserManager)context.getSystemService(Context.USER_SERVICE);
+ for(UserInfo userInfo : userManager.getUsers()) {
+ enablePackageForUser(packageName, enable, userInfo.id);
+ }
+ }
+
+ @Override
+ public void enablePackageForUser(String packageName, boolean enable, int userId) {
+ try {
+ AppGlobals.getPackageManager().setApplicationEnabledSetting(
+ packageName,
+ enable ? PackageManager.COMPONENT_ENABLED_STATE_DEFAULT :
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER, 0,
+ userId, null);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Tried to disable " + packageName + " for user " + userId + ": " + e);
+ }
+ }
+
+ @Override
+ public boolean systemIsDebuggable() {
+ return Build.IS_DEBUGGABLE;
+ }
+
+ @Override
+ public PackageInfo getPackageInfoForProvider(WebViewProviderInfo configInfo)
+ throws NameNotFoundException {
+ PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
+ return pm.getPackageInfo(configInfo.packageName, PACKAGE_FLAGS);
+ }
+
+ // flags declaring we want extra info from the package manager for webview providers
+ private final static int PACKAGE_FLAGS = PackageManager.GET_META_DATA
+ | PackageManager.GET_SIGNATURES | PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
}
diff --git a/services/core/java/com/android/server/webkit/WebViewUtilityInterface.java b/services/core/java/com/android/server/webkit/SystemInterface.java
similarity index 64%
rename from services/core/java/com/android/server/webkit/WebViewUtilityInterface.java
rename to services/core/java/com/android/server/webkit/SystemInterface.java
index 1919f40..7bde37a 100644
--- a/services/core/java/com/android/server/webkit/WebViewUtilityInterface.java
+++ b/services/core/java/com/android/server/webkit/SystemInterface.java
@@ -16,22 +16,36 @@
package com.android.server.webkit;
-import android.webkit.WebViewProviderInfo;
import android.content.Context;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.webkit.WebViewProviderInfo;
/**
- * Utility interface for the WebViewUpdateService.
+ * System interface for the WebViewUpdateService.
* This interface provides a way to test the WebView preparation mechanism - during normal use this
* interface is implemented using calls to the Android framework, but by providing an alternative
* implementation we can test the WebView preparation logic without reaching other framework code.
+ *
* @hide
*/
-public interface WebViewUtilityInterface {
+public interface SystemInterface {
public WebViewProviderInfo[] getWebViewPackages();
public int onWebViewProviderChanged(PackageInfo packageInfo);
+ public int getFactoryPackageVersion(String packageName) throws NameNotFoundException;
public String getUserChosenWebViewProvider(Context context);
public void updateUserSetting(Context context, String newProviderName);
public void killPackageDependents(String packageName);
+
+ public boolean isFallbackLogicEnabled();
+ public void enableFallbackLogic(boolean enable);
+
+ public void uninstallAndDisablePackageForAllUsers(Context context, String packageName);
+ public void enablePackageForAllUsers(Context context, String packageName, boolean enable);
+ public void enablePackageForUser(String packageName, boolean enable, int userId);
+
+ public boolean systemIsDebuggable();
+ public PackageInfo getPackageInfoForProvider(WebViewProviderInfo configInfo)
+ throws NameNotFoundException;
}
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index 50699f8..ebec445 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -16,28 +16,21 @@
package com.android.server.webkit;
-import android.app.ActivityManagerNative;
-import android.app.AppGlobals;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageDeleteObserver;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
-import android.content.pm.UserInfo;
import android.os.Binder;
import android.os.PatternMatcher;
import android.os.Process;
-import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings.Global;
-import android.provider.Settings;
-import android.util.AndroidRuntimeException;
+import android.util.Base64;
import android.util.Slog;
import android.webkit.IWebViewUpdateService;
import android.webkit.WebViewFactory;
@@ -49,7 +42,6 @@
import java.io.FileDescriptor;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Iterator;
import java.util.List;
/**
@@ -59,28 +51,21 @@
public class WebViewUpdateService extends SystemService {
private static final String TAG = "WebViewUpdateService";
- private static final int WAIT_TIMEOUT_MS = 4500; // KEY_DISPATCHING_TIMEOUT is 5000.
-
- // Keeps track of the number of running relro creations
- private int mNumRelroCreationsStarted = 0;
- private int mNumRelroCreationsFinished = 0;
- // Implies that we need to rerun relro creation because we are using an out-of-date package
- private boolean mWebViewPackageDirty = false;
- private boolean mAnyWebViewInstalled = false;
-
- private int NUMBER_OF_RELROS_UNKNOWN = Integer.MAX_VALUE;
-
- // The WebView package currently in use (or the one we are preparing).
- private PackageInfo mCurrentWebViewPackage = null;
- // The WebView providers that are currently available.
- private WebViewProviderInfo[] mCurrentValidWebViewPackages = null;
private BroadcastReceiver mWebViewUpdatedReceiver;
- private WebViewUtilityInterface mWebViewUtility;
+ private SystemInterface mSystemInterface;
+
+ static final int PACKAGE_CHANGED = 0;
+ static final int PACKAGE_ADDED = 1;
+ static final int PACKAGE_ADDED_REPLACED = 2;
+ static final int PACKAGE_REMOVED = 3;
+
+ private WebViewUpdater mWebViewUpdater;
public WebViewUpdateService(Context context) {
super(context);
- mWebViewUtility = new WebViewUtilityImpl();
+ mSystemInterface = new SystemImpl();
+ mWebViewUpdater = new WebViewUpdater(getContext(), mSystemInterface);
}
@Override
@@ -88,77 +73,34 @@
mWebViewUpdatedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- // When a package is replaced we will receive two intents, one representing
- // the removal of the old package and one representing the addition of the
- // new package.
- // In the case where we receive an intent to remove the old version of the
- // package that is being replaced we early-out here so that we don't run the
- // update-logic twice.
- if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)
- && intent.getExtras().getBoolean(Intent.EXTRA_REPLACING)) {
- return;
- }
-
- // Ensure that we only heed PACKAGE_CHANGED intents if they change an entire
- // package, not just a component
- if (intent.getAction().equals(Intent.ACTION_PACKAGE_CHANGED)) {
- if (!WebViewFactory.entirePackageChanged(intent)) {
- return;
- }
- }
-
- if (intent.getAction().equals(Intent.ACTION_USER_ADDED)) {
- int userId =
- intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
- handleNewUser(userId);
- return;
- }
-
- updateFallbackState(context, intent);
-
- for (WebViewProviderInfo provider : mWebViewUtility.getWebViewPackages()) {
- String webviewPackage = "package:" + provider.packageName;
-
- if (webviewPackage.equals(intent.getDataString())) {
- boolean updateWebView = false;
- boolean removedOrChangedOldPackage = false;
- String oldProviderName = null;
- PackageInfo newPackage = null;
- synchronized(WebViewUpdateService.this) {
- try {
- updateValidWebViewPackages();
- newPackage = findPreferredWebViewPackage();
- if (mCurrentWebViewPackage != null)
- oldProviderName = mCurrentWebViewPackage.packageName;
- // Only trigger update actions if the updated package is the one
- // that will be used, or the one that was in use before the
- // update, or if we haven't seen a valid WebView package before.
- updateWebView =
- provider.packageName.equals(newPackage.packageName)
- || provider.packageName.equals(oldProviderName)
- || mCurrentWebViewPackage == null;
- // We removed the old package if we received an intent to remove
- // or replace the old package.
- removedOrChangedOldPackage =
- provider.packageName.equals(oldProviderName);
- if (updateWebView) {
- onWebViewProviderChanged(newPackage);
- }
- } catch (WebViewFactory.MissingWebViewPackageException e) {
- Slog.e(TAG, "Could not find valid WebView package to create " +
- "relro with " + e);
- }
+ switch (intent.getAction()) {
+ case Intent.ACTION_PACKAGE_REMOVED:
+ // When a package is replaced we will receive two intents, one
+ // representing the removal of the old package and one representing the
+ // addition of the new package.
+ // In the case where we receive an intent to remove the old version of
+ // the package that is being replaced we early-out here so that we don't
+ // run the update-logic twice.
+ if (intent.getExtras().getBoolean(Intent.EXTRA_REPLACING)) return;
+ packageStateChanged(packageNameFromIntent(intent), PACKAGE_REMOVED);
+ break;
+ case Intent.ACTION_PACKAGE_CHANGED:
+ // Ensure that we only heed PACKAGE_CHANGED intents if they change an
+ // entire package, not just a component
+ if (entirePackageChanged(intent)) {
+ packageStateChanged(packageNameFromIntent(intent), PACKAGE_CHANGED);
}
- if(updateWebView && !removedOrChangedOldPackage
- && oldProviderName != null) {
- // If the provider change is the result of adding or replacing a
- // package that was not the previous provider then we must kill
- // packages dependent on the old package ourselves. The framework
- // only kills dependents of packages that are being removed.
- mWebViewUtility.killPackageDependents(oldProviderName);
- }
- return;
- }
+ break;
+ case Intent.ACTION_PACKAGE_ADDED:
+ packageStateChanged(packageNameFromIntent(intent),
+ (intent.getExtras().getBoolean(Intent.EXTRA_REPLACING)
+ ? PACKAGE_ADDED_REPLACED : PACKAGE_ADDED));
+ break;
+ case Intent.ACTION_USER_ADDED:
+ int userId =
+ intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+ handleNewUser(userId);
+ break;
}
}
};
@@ -168,7 +110,7 @@
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addDataScheme("package");
// Make sure we only receive intents for WebView packages from our config file.
- for (WebViewProviderInfo provider : mWebViewUtility.getWebViewPackages()) {
+ for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
filter.addDataSchemeSpecificPart(provider.packageName, PatternMatcher.PATTERN_LITERAL);
}
getContext().registerReceiver(mWebViewUpdatedReceiver, filter);
@@ -180,42 +122,54 @@
publishBinderService("webviewupdate", new BinderService(), true /*allowIsolated*/);
}
- private static boolean existsValidNonFallbackProvider(WebViewProviderInfo[] providers) {
+ private void packageStateChanged(String packageName, int changedState) {
+ updateFallbackState(packageName, changedState);
+ mWebViewUpdater.packageStateChanged(packageName, changedState);
+ }
+
+ public void prepareWebViewInSystemServer() {
+ updateFallbackStateOnBoot();
+ mWebViewUpdater.prepareWebViewInSystemServer();
+ }
+
+ private static String packageNameFromIntent(Intent intent) {
+ return intent.getDataString().substring("package:".length());
+ }
+
+ private boolean existsValidNonFallbackProvider(WebViewProviderInfo[] providers) {
for (WebViewProviderInfo provider : providers) {
- if (provider.isAvailableByDefault() && provider.isEnabled()
- && provider.isValidProvider() && !provider.isFallbackPackage()) {
- return true;
+ if (provider.availableByDefault && !provider.isFallback) {
+ try {
+ PackageInfo packageInfo = mSystemInterface.getPackageInfoForProvider(provider);
+ if (isEnabledPackage(packageInfo)
+ && mWebViewUpdater.isValidProvider(provider, packageInfo)) {
+ return true;
+ }
+ } catch (NameNotFoundException e) {
+ // A non-existent provider is neither valid nor enabled
+ }
}
}
return false;
}
- private static void enablePackageForUser(String packageName, boolean enable, int userId) {
- try {
- AppGlobals.getPackageManager().setApplicationEnabledSetting(
- packageName,
- enable ? PackageManager.COMPONENT_ENABLED_STATE_DEFAULT :
- PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER, 0,
- userId, null);
- } catch (RemoteException e) {
- Slog.w(TAG, "Tried to disable " + packageName + " for user " + userId + ": " + e);
- }
- }
-
/**
* Called when a new user has been added to update the state of its fallback package.
*/
void handleNewUser(int userId) {
- if (!isFallbackLogicEnabled()) return;
+ if (!mSystemInterface.isFallbackLogicEnabled()) return;
- WebViewProviderInfo[] webviewProviders = mWebViewUtility.getWebViewPackages();
+ WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders);
if (fallbackProvider == null) return;
- boolean existsValidNonFallbackProvider =
- existsValidNonFallbackProvider(webviewProviders);
- enablePackageForUser(fallbackProvider.packageName, !existsValidNonFallbackProvider,
- userId);
+ mSystemInterface.enablePackageForUser(fallbackProvider.packageName,
+ !existsValidNonFallbackProvider(webviewProviders), userId);
+ }
+
+ public void updateFallbackStateOnBoot() {
+ WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
+ updateFallbackState(webviewProviders, true);
}
/**
@@ -223,273 +177,473 @@
* package that is valid (and available by default) then disable the fallback package,
* otherwise, enable the fallback package.
*/
- void updateFallbackState(final Context context, final Intent intent) {
- if (!isFallbackLogicEnabled()) return;
+ public void updateFallbackState(String changedPackage, int changedState) {
+ if (!mSystemInterface.isFallbackLogicEnabled()) return;
- WebViewProviderInfo[] webviewProviders = mWebViewUtility.getWebViewPackages();
+ WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
- if (intent != null && (intent.getAction().equals(Intent.ACTION_PACKAGE_ADDED)
- || intent.getAction().equals(Intent.ACTION_PACKAGE_CHANGED))) {
- // A package was changed / updated / downgraded, early out if it is not one of the
- // webview packages that are available by default.
- String changedPackage = null;
- for (WebViewProviderInfo provider : webviewProviders) {
- String webviewPackage = "package:" + provider.packageName;
- if (webviewPackage.equals(intent.getDataString())) {
- if (provider.isAvailableByDefault()) {
- changedPackage = provider.packageName;
- }
- break;
+ // A package was changed / updated / downgraded, early out if it is not one of the
+ // webview packages that are available by default.
+ boolean changedPackageAvailableByDefault = false;
+ for (WebViewProviderInfo provider : webviewProviders) {
+ if (provider.packageName.equals(changedPackage)) {
+ if (provider.availableByDefault) {
+ changedPackageAvailableByDefault = true;
}
+ break;
}
- if (changedPackage == null) return;
}
+ if (!changedPackageAvailableByDefault) return;
+ updateFallbackState(webviewProviders, false);
+ }
+ private void updateFallbackState(WebViewProviderInfo[] webviewProviders, boolean isBoot) {
// If there exists a valid and enabled non-fallback package - disable the fallback
// package, otherwise, enable it.
WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders);
if (fallbackProvider == null) return;
boolean existsValidNonFallbackProvider = existsValidNonFallbackProvider(webviewProviders);
+ boolean isFallbackEnabled = false;
+ try {
+ isFallbackEnabled = isEnabledPackage(
+ mSystemInterface.getPackageInfoForProvider(fallbackProvider));
+ } catch (NameNotFoundException e) {
+ }
+
if (existsValidNonFallbackProvider
// During an OTA the primary user's WebView state might differ from other users', so
// ignore the state of that user during boot.
- && (fallbackProvider.isEnabled() || intent == null)) {
- // Uninstall and disable fallback package for all users.
- context.getPackageManager().deletePackage(fallbackProvider.packageName,
- new IPackageDeleteObserver.Stub() {
- public void packageDeleted(String packageName, int returnCode) {
- // Ignore returnCode since the deletion could fail, e.g. we might be trying
- // to delete a non-updated system-package (and we should still disable the
- // package)
- UserManager userManager =
- (UserManager)context.getSystemService(Context.USER_SERVICE);
- // Disable the fallback package for all users.
- for(UserInfo userInfo : userManager.getUsers()) {
- enablePackageForUser(packageName, false, userInfo.id);
- }
- }
- }, PackageManager.DELETE_SYSTEM_APP | PackageManager.DELETE_ALL_USERS);
+ && (isFallbackEnabled || isBoot)) {
+ mSystemInterface.uninstallAndDisablePackageForAllUsers(getContext(),
+ fallbackProvider.packageName);
} else if (!existsValidNonFallbackProvider
// During an OTA the primary user's WebView state might differ from other users', so
// ignore the state of that user during boot.
- && (!fallbackProvider.isEnabled() || intent==null)) {
+ && (!isFallbackEnabled || isBoot)) {
// Enable the fallback package for all users.
- UserManager userManager =
- (UserManager)context.getSystemService(Context.USER_SERVICE);
- for(UserInfo userInfo : userManager.getUsers()) {
- enablePackageForUser(fallbackProvider.packageName, true, userInfo.id);
- }
+ mSystemInterface.enablePackageForAllUsers(getContext(),
+ fallbackProvider.packageName, true);
}
}
- private static boolean isFallbackLogicEnabled() {
- // Note that this is enabled by default (i.e. if the setting hasn't been set).
- return Settings.Global.getInt(AppGlobals.getInitialApplication().getContentResolver(),
- Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED, 1) == 1;
- }
-
- private static void enableFallbackLogic(boolean enable) {
- Settings.Global.putInt(AppGlobals.getInitialApplication().getContentResolver(),
- Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED, enable ? 1 : 0);
- }
-
/**
- * Returns the only fallback provider, or null if there is none.
+ * Returns the only fallback provider in the set of given packages, or null if there is none.
*/
private static WebViewProviderInfo getFallbackProvider(WebViewProviderInfo[] webviewPackages) {
for (WebViewProviderInfo provider : webviewPackages) {
- if (provider.isFallbackPackage()) {
+ if (provider.isFallback) {
return provider;
}
}
return null;
}
- private static boolean containsAvailableNonFallbackProvider(
- WebViewProviderInfo[] webviewPackages) {
- for (WebViewProviderInfo provider : webviewPackages) {
- if (provider.isAvailableByDefault() && provider.isEnabled()
- && provider.isValidProvider() && !provider.isFallbackPackage()) {
- return true;
- }
- }
- return false;
- }
-
private boolean isFallbackPackage(String packageName) {
- if (packageName == null || !isFallbackLogicEnabled()) return false;
+ if (packageName == null || !mSystemInterface.isFallbackLogicEnabled()) return false;
- WebViewProviderInfo[] webviewPackages = mWebViewUtility.getWebViewPackages();
+ WebViewProviderInfo[] webviewPackages = mSystemInterface.getWebViewPackages();
WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewPackages);
return (fallbackProvider != null
&& packageName.equals(fallbackProvider.packageName));
}
/**
- * Perform any WebView loading preparations that must happen at boot from the system server,
- * after the package manager has started or after an update to the webview is installed.
- * This must be called in the system server.
- * Currently, this means spawning the child processes which will create the relro files.
+ * Class that decides what WebView implementation to use and prepares that implementation for
+ * use.
*/
- public void prepareWebViewInSystemServer() {
- updateFallbackState(getContext(), null);
- try {
- synchronized(this) {
- updateValidWebViewPackages();
- mCurrentWebViewPackage = findPreferredWebViewPackage();
- onWebViewProviderChanged(mCurrentWebViewPackage);
- }
- } catch (Throwable t) {
- // Log and discard errors at this stage as we must not crash the system server.
- Slog.e(TAG, "error preparing webview provider from system server", t);
+ private static class WebViewUpdater {
+ private Context mContext;
+ private SystemInterface mSystemInterface;
+ private int mMinimumVersionCode = -1;
+
+ public WebViewUpdater(Context context, SystemInterface systemInterface) {
+ mContext = context;
+ mSystemInterface = systemInterface;
}
- }
+ private static final int WAIT_TIMEOUT_MS = 4500; // KEY_DISPATCHING_TIMEOUT is 5000.
- /**
- * Change WebView provider and provider setting and kill packages using the old provider.
- * Return the new provider (in case we are in the middle of creating relro files this new
- * provider will not be in use directly, but will when the relros are done).
- */
- private String changeProviderAndSetting(String newProviderName) {
- PackageInfo oldPackage = null;
- PackageInfo newPackage = null;
- synchronized(this) {
- oldPackage = mCurrentWebViewPackage;
- mWebViewUtility.updateUserSetting(getContext(), newProviderName);
+ // Keeps track of the number of running relro creations
+ private int mNumRelroCreationsStarted = 0;
+ private int mNumRelroCreationsFinished = 0;
+ // Implies that we need to rerun relro creation because we are using an out-of-date package
+ private boolean mWebViewPackageDirty = false;
+ private boolean mAnyWebViewInstalled = false;
- try {
- newPackage = findPreferredWebViewPackage();
- if (oldPackage != null && newPackage.packageName.equals(oldPackage.packageName)) {
- // If we don't perform the user change, revert the settings change.
- mWebViewUtility.updateUserSetting(getContext(), newPackage.packageName);
- return newPackage.packageName;
+ private int NUMBER_OF_RELROS_UNKNOWN = Integer.MAX_VALUE;
+
+ // The WebView package currently in use (or the one we are preparing).
+ private PackageInfo mCurrentWebViewPackage = null;
+
+ private Object mLock = new Object();
+
+ public void packageStateChanged(String packageName, int changedState) {
+ for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
+ String webviewPackage = provider.packageName;
+
+ if (webviewPackage.equals(packageName)) {
+ boolean updateWebView = false;
+ boolean removedOrChangedOldPackage = false;
+ String oldProviderName = null;
+ PackageInfo newPackage = null;
+ synchronized(mLock) {
+ try {
+ newPackage = findPreferredWebViewPackage();
+ if (mCurrentWebViewPackage != null)
+ oldProviderName = mCurrentWebViewPackage.packageName;
+ // Only trigger update actions if the updated package is the one
+ // that will be used, or the one that was in use before the
+ // update, or if we haven't seen a valid WebView package before.
+ updateWebView =
+ provider.packageName.equals(newPackage.packageName)
+ || provider.packageName.equals(oldProviderName)
+ || mCurrentWebViewPackage == null;
+ // We removed the old package if we received an intent to remove
+ // or replace the old package.
+ removedOrChangedOldPackage =
+ provider.packageName.equals(oldProviderName);
+ if (updateWebView) {
+ onWebViewProviderChanged(newPackage);
+ }
+ } catch (WebViewFactory.MissingWebViewPackageException e) {
+ Slog.e(TAG, "Could not find valid WebView package to create " +
+ "relro with " + e);
+ }
+ }
+ if(updateWebView && !removedOrChangedOldPackage
+ && oldProviderName != null) {
+ // If the provider change is the result of adding or replacing a
+ // package that was not the previous provider then we must kill
+ // packages dependent on the old package ourselves. The framework
+ // only kills dependents of packages that are being removed.
+ mSystemInterface.killPackageDependents(oldProviderName);
+ }
+ return;
}
- } catch (WebViewFactory.MissingWebViewPackageException e) {
- Slog.e(TAG, "Tried to change WebView provider but failed to fetch WebView package "
- + e);
- // If we don't perform the user change but don't have an installed WebView package,
- // we will have changed the setting and it will be used when a package is available.
- return newProviderName;
- }
- onWebViewProviderChanged(newPackage);
- }
- // Kill apps using the old provider
- if (oldPackage != null) {
- mWebViewUtility.killPackageDependents(oldPackage.packageName);
- }
- return newPackage.packageName;
- }
-
- /**
- * This is called when we change WebView provider, either when the current provider is updated
- * or a new provider is chosen / takes precedence.
- */
- private void onWebViewProviderChanged(PackageInfo newPackage) {
- synchronized(this) {
- mAnyWebViewInstalled = true;
- if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
- mCurrentWebViewPackage = newPackage;
- mWebViewUtility.updateUserSetting(getContext(), newPackage.packageName);
-
- // The relro creations might 'finish' (not start at all) before
- // WebViewFactory.onWebViewProviderChanged which means we might not know the number
- // of started creations before they finish.
- mNumRelroCreationsStarted = NUMBER_OF_RELROS_UNKNOWN;
- mNumRelroCreationsFinished = 0;
- mNumRelroCreationsStarted = mWebViewUtility.onWebViewProviderChanged(newPackage);
- // If the relro creations finish before we know the number of started creations we
- // will have to do any cleanup/notifying here.
- checkIfRelrosDoneLocked();
- } else {
- mWebViewPackageDirty = true;
- }
- }
- }
-
- /**
- * Updates the currently valid WebView provider packages.
- * Should be used when a provider has been installed or removed.
- * @hide
- * */
- private void updateValidWebViewPackages() {
- List<WebViewProviderInfo> webViewProviders =
- new ArrayList<WebViewProviderInfo>(Arrays.asList(mWebViewUtility.getWebViewPackages()));
- Iterator<WebViewProviderInfo> it = webViewProviders.iterator();
- // remove non-valid packages
- while(it.hasNext()) {
- WebViewProviderInfo current = it.next();
- if (!current.isValidProvider())
- it.remove();
- }
- synchronized(this) {
- mCurrentValidWebViewPackages =
- webViewProviders.toArray(new WebViewProviderInfo[webViewProviders.size()]);
- }
- }
-
- /**
- * Returns either the package info of the WebView provider determined in the following way:
- * If the user has chosen a provider then use that if it is valid,
- * otherwise use the first package in the webview priority list that is valid.
- *
- * @hide
- */
- private PackageInfo findPreferredWebViewPackage() {
- WebViewProviderInfo[] providers = mCurrentValidWebViewPackages;
-
- String userChosenProvider = mWebViewUtility.getUserChosenWebViewProvider(getContext());
-
- // If the user has chosen provider, use that
- for (WebViewProviderInfo provider : providers) {
- if (provider.packageName.equals(userChosenProvider) && provider.isEnabled()) {
- return provider.getPackageInfo();
}
}
- // User did not choose, or the choice failed; use the most stable provider that is
- // enabled and available by default (not through user choice).
- for (WebViewProviderInfo provider : providers) {
- if (provider.isAvailableByDefault() && provider.isEnabled()) {
- return provider.getPackageInfo();
+ public void prepareWebViewInSystemServer() {
+ try {
+ synchronized(mLock) {
+ mCurrentWebViewPackage = findPreferredWebViewPackage();
+ onWebViewProviderChanged(mCurrentWebViewPackage);
+ }
+ } catch (Throwable t) {
+ // Log and discard errors at this stage as we must not crash the system server.
+ Slog.e(TAG, "error preparing webview provider from system server", t);
}
}
- // Could not find any enabled package either, use the most stable provider.
- for (WebViewProviderInfo provider : providers) {
- return provider.getPackageInfo();
- }
+ /**
+ * Change WebView provider and provider setting and kill packages using the old provider.
+ * Return the new provider (in case we are in the middle of creating relro files this new
+ * provider will not be in use directly, but will when the relros are done).
+ */
+ public String changeProviderAndSetting(String newProviderName) {
+ PackageInfo oldPackage = null;
+ PackageInfo newPackage = null;
+ synchronized(mLock) {
+ oldPackage = mCurrentWebViewPackage;
+ mSystemInterface.updateUserSetting(mContext, newProviderName);
- mAnyWebViewInstalled = false;
- throw new WebViewFactory.MissingWebViewPackageException(
- "Could not find a loadable WebView package");
- }
-
- /**
- * Returns whether WebView is ready and is not going to go through its preparation phase again
- * directly.
- */
- private boolean webViewIsReadyLocked() {
- return !mWebViewPackageDirty
- && (mNumRelroCreationsStarted == mNumRelroCreationsFinished)
- // The current package might be replaced though we haven't received an intent declaring
- // this yet, the following flag makes anyone loading WebView to wait in this case.
- && mAnyWebViewInstalled;
- }
-
- private void checkIfRelrosDoneLocked() {
- if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
- if (mWebViewPackageDirty) {
- mWebViewPackageDirty = false;
- // If we have changed provider since we started the relro creation we need to
- // redo the whole process using the new package instead.
- PackageInfo newPackage = findPreferredWebViewPackage();
+ try {
+ newPackage = findPreferredWebViewPackage();
+ if (oldPackage != null
+ && newPackage.packageName.equals(oldPackage.packageName)) {
+ // If we don't perform the user change, revert the settings change.
+ mSystemInterface.updateUserSetting(mContext, newPackage.packageName);
+ return newPackage.packageName;
+ }
+ } catch (WebViewFactory.MissingWebViewPackageException e) {
+ Slog.e(TAG, "Tried to change WebView provider but failed to fetch WebView " +
+ "package " + e);
+ // If we don't perform the user change but don't have an installed WebView
+ // package, we will have changed the setting and it will be used when a package
+ // is available.
+ return newProviderName;
+ }
onWebViewProviderChanged(newPackage);
- } else {
- this.notifyAll();
+ }
+ // Kill apps using the old provider
+ if (oldPackage != null) {
+ mSystemInterface.killPackageDependents(oldPackage.packageName);
+ }
+ return newPackage.packageName;
+ }
+
+ /**
+ * This is called when we change WebView provider, either when the current provider is
+ * updated or a new provider is chosen / takes precedence.
+ */
+ private void onWebViewProviderChanged(PackageInfo newPackage) {
+ synchronized(mLock) {
+ mAnyWebViewInstalled = true;
+ if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
+ mCurrentWebViewPackage = newPackage;
+ mSystemInterface.updateUserSetting(mContext, newPackage.packageName);
+
+ // The relro creations might 'finish' (not start at all) before
+ // WebViewFactory.onWebViewProviderChanged which means we might not know the
+ // number of started creations before they finish.
+ mNumRelroCreationsStarted = NUMBER_OF_RELROS_UNKNOWN;
+ mNumRelroCreationsFinished = 0;
+ mNumRelroCreationsStarted =
+ mSystemInterface.onWebViewProviderChanged(newPackage);
+ // If the relro creations finish before we know the number of started creations
+ // we will have to do any cleanup/notifying here.
+ checkIfRelrosDoneLocked();
+ } else {
+ mWebViewPackageDirty = true;
+ }
}
}
+
+ private ProviderAndPackageInfo[] getValidWebViewPackagesAndInfos() {
+ WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages();
+ List<ProviderAndPackageInfo> providers = new ArrayList<>();
+ for(int n = 0; n < allProviders.length; n++) {
+ try {
+ PackageInfo packageInfo =
+ mSystemInterface.getPackageInfoForProvider(allProviders[n]);
+ if (isValidProvider(allProviders[n], packageInfo)) {
+ providers.add(new ProviderAndPackageInfo(allProviders[n], packageInfo));
+ }
+ } catch (NameNotFoundException e) {
+ // Don't add non-existent packages
+ }
+ }
+ return providers.toArray(new ProviderAndPackageInfo[providers.size()]);
+ }
+
+ /**
+ * Fetch only the currently valid WebView packages.
+ **/
+ public WebViewProviderInfo[] getValidWebViewPackages() {
+ ProviderAndPackageInfo[] providersAndPackageInfos = getValidWebViewPackagesAndInfos();
+ WebViewProviderInfo[] providers =
+ new WebViewProviderInfo[providersAndPackageInfos.length];
+ for(int n = 0; n < providersAndPackageInfos.length; n++) {
+ providers[n] = providersAndPackageInfos[n].provider;
+ }
+ return providers;
+ }
+
+
+ private class ProviderAndPackageInfo {
+ public final WebViewProviderInfo provider;
+ public final PackageInfo packageInfo;
+
+ public ProviderAndPackageInfo(WebViewProviderInfo provider, PackageInfo packageInfo) {
+ this.provider = provider;
+ this.packageInfo = packageInfo;
+ }
+ }
+
+ /**
+ * Returns either the package info of the WebView provider determined in the following way:
+ * If the user has chosen a provider then use that if it is valid,
+ * otherwise use the first package in the webview priority list that is valid.
+ *
+ */
+ private PackageInfo findPreferredWebViewPackage() {
+ ProviderAndPackageInfo[] providers = getValidWebViewPackagesAndInfos();
+
+ String userChosenProvider = mSystemInterface.getUserChosenWebViewProvider(mContext);
+
+ // If the user has chosen provider, use that
+ for (ProviderAndPackageInfo providerAndPackage : providers) {
+ if (providerAndPackage.provider.packageName.equals(userChosenProvider)
+ && isEnabledPackage(providerAndPackage.packageInfo)) {
+ return providerAndPackage.packageInfo;
+ }
+ }
+
+ // User did not choose, or the choice failed; use the most stable provider that is
+ // enabled and available by default (not through user choice).
+ for (ProviderAndPackageInfo providerAndPackage : providers) {
+ if (providerAndPackage.provider.availableByDefault
+ && isEnabledPackage(providerAndPackage.packageInfo)) {
+ return providerAndPackage.packageInfo;
+ }
+ }
+
+ // Could not find any enabled package either, use the most stable provider.
+ for (ProviderAndPackageInfo providerAndPackage : providers) {
+ return providerAndPackage.packageInfo;
+ }
+
+ mAnyWebViewInstalled = false;
+ throw new WebViewFactory.MissingWebViewPackageException(
+ "Could not find a loadable WebView package");
+ }
+
+ public void notifyRelroCreationCompleted() {
+ synchronized (mLock) {
+ mNumRelroCreationsFinished++;
+ checkIfRelrosDoneLocked();
+ }
+ }
+
+ public WebViewProviderResponse waitForAndGetProvider() {
+ PackageInfo webViewPackage = null;
+ final long NS_PER_MS = 1000000;
+ final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS;
+ boolean webViewReady = false;
+ int webViewStatus = WebViewFactory.LIBLOAD_SUCCESS;
+ synchronized (mLock) {
+ webViewReady = webViewIsReadyLocked();
+ while (!webViewReady) {
+ final long timeNowMs = System.nanoTime() / NS_PER_MS;
+ if (timeNowMs >= timeoutTimeMs) break;
+ try {
+ mLock.wait(timeoutTimeMs - timeNowMs);
+ } catch (InterruptedException e) {}
+ webViewReady = webViewIsReadyLocked();
+ }
+ // Make sure we return the provider that was used to create the relro file
+ webViewPackage = mCurrentWebViewPackage;
+ if (webViewReady) {
+ } else if (!mAnyWebViewInstalled) {
+ webViewStatus = WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES;
+ } else {
+ // Either the current relro creation isn't done yet, or the new relro creatioin
+ // hasn't kicked off yet (the last relro creation used an out-of-date WebView).
+ webViewStatus = WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO;
+ }
+ }
+ if (!webViewReady) Slog.w(TAG, "creating relro file timed out");
+ return new WebViewProviderResponse(webViewPackage, webViewStatus);
+ }
+
+ public String getCurrentWebViewPackageName() {
+ synchronized(mLock) {
+ if (mCurrentWebViewPackage == null)
+ return null;
+ return mCurrentWebViewPackage.packageName;
+ }
+ }
+
+ /**
+ * Returns whether WebView is ready and is not going to go through its preparation phase
+ * again directly.
+ */
+ private boolean webViewIsReadyLocked() {
+ return !mWebViewPackageDirty
+ && (mNumRelroCreationsStarted == mNumRelroCreationsFinished)
+ // The current package might be replaced though we haven't received an intent
+ // declaring this yet, the following flag makes anyone loading WebView to wait in
+ // this case.
+ && mAnyWebViewInstalled;
+ }
+
+ private void checkIfRelrosDoneLocked() {
+ if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
+ if (mWebViewPackageDirty) {
+ mWebViewPackageDirty = false;
+ // If we have changed provider since we started the relro creation we need to
+ // redo the whole process using the new package instead.
+ PackageInfo newPackage = findPreferredWebViewPackage();
+ onWebViewProviderChanged(newPackage);
+ } else {
+ mLock.notifyAll();
+ }
+ }
+ }
+
+ /**
+ * Returns whether this provider is valid for use as a WebView provider.
+ */
+ public boolean isValidProvider(WebViewProviderInfo configInfo,
+ PackageInfo packageInfo) {
+ if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0
+ && packageInfo.versionCode < getMinimumVersionCode()
+ && !mSystemInterface.systemIsDebuggable()) {
+ // Non-system package webview providers may be downgraded arbitrarily low, prevent
+ // that by enforcing minimum version code. This check is only enforced for user
+ // builds.
+ return false;
+ }
+ if (providerHasValidSignature(configInfo, packageInfo, mSystemInterface) &&
+ WebViewFactory.getWebViewLibrary(packageInfo.applicationInfo) != null) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Gets the minimum version code allowed for a valid provider. It is the minimum versionCode
+ * of all available-by-default and non-fallback WebView provider packages. If there is no
+ * such WebView provider package on the system, then return -1, which means all positive
+ * versionCode WebView packages are accepted.
+ */
+ private int getMinimumVersionCode() {
+ if (mMinimumVersionCode > 0) {
+ return mMinimumVersionCode;
+ }
+
+ for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
+ if (provider.availableByDefault && !provider.isFallback) {
+ try {
+ int versionCode =
+ mSystemInterface.getFactoryPackageVersion(provider.packageName);
+ if (mMinimumVersionCode < 0 || versionCode < mMinimumVersionCode) {
+ mMinimumVersionCode = versionCode;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // Safe to ignore.
+ }
+ }
+ }
+
+ return mMinimumVersionCode;
+ }
+ }
+
+ private static boolean providerHasValidSignature(WebViewProviderInfo provider,
+ PackageInfo packageInfo, SystemInterface systemInterface) {
+ if (systemInterface.systemIsDebuggable()) {
+ return true;
+ }
+ Signature[] packageSignatures;
+ // If no signature is declared, instead check whether the package is included in the
+ // system.
+ if (provider.signatures == null || provider.signatures.length == 0) {
+ return packageInfo.applicationInfo.isSystemApp();
+ }
+ packageSignatures = packageInfo.signatures;
+ if (packageSignatures.length != 1)
+ return false;
+
+ final byte[] packageSignature = packageSignatures[0].toByteArray();
+ // Return whether the package signature matches any of the valid signatures
+ for (String signature : provider.signatures) {
+ final byte[] validSignature = Base64.decode(signature, Base64.DEFAULT);
+ if (Arrays.equals(packageSignature, validSignature))
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns whether the given package is enabled.
+ * This state can be changed by the user from Settings->Apps
+ */
+ private static boolean isEnabledPackage(PackageInfo packageInfo) {
+ return packageInfo.applicationInfo.enabled;
+ }
+
+ /**
+ * Returns whether the entire package from an ACTION_PACKAGE_CHANGED intent was changed (rather
+ * than just one of its components).
+ * @hide
+ */
+ public static boolean entirePackageChanged(Intent intent) {
+ String[] componentList =
+ intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
+ return Arrays.asList(componentList).contains(
+ intent.getDataString().substring("package:".length()));
}
private class BinderService extends IWebViewUpdateService.Stub {
@@ -519,10 +673,7 @@
long callingId = Binder.clearCallingIdentity();
try {
- synchronized (WebViewUpdateService.this) {
- mNumRelroCreationsFinished++;
- checkIfRelrosDoneLocked();
- }
+ WebViewUpdateService.this.mWebViewUpdater.notifyRelroCreationCompleted();
} finally {
Binder.restoreCallingIdentity(callingId);
}
@@ -542,34 +693,7 @@
throw new IllegalStateException("Cannot create a WebView from the SystemServer");
}
- PackageInfo webViewPackage = null;
- final long NS_PER_MS = 1000000;
- final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS;
- boolean webViewReady = false;
- int webViewStatus = WebViewFactory.LIBLOAD_SUCCESS;
- synchronized (WebViewUpdateService.this) {
- webViewReady = WebViewUpdateService.this.webViewIsReadyLocked();
- while (!webViewReady) {
- final long timeNowMs = System.nanoTime() / NS_PER_MS;
- if (timeNowMs >= timeoutTimeMs) break;
- try {
- WebViewUpdateService.this.wait(timeoutTimeMs - timeNowMs);
- } catch (InterruptedException e) {}
- webViewReady = WebViewUpdateService.this.webViewIsReadyLocked();
- }
- // Make sure we return the provider that was used to create the relro file
- webViewPackage = WebViewUpdateService.this.mCurrentWebViewPackage;
- if (webViewReady) {
- } else if (!mAnyWebViewInstalled) {
- webViewStatus = WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES;
- } else {
- // Either the current relro creation isn't done yet, or the new relro creatioin
- // hasn't kicked off yet (the last relro creation used an out-of-date WebView).
- webViewStatus = WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO;
- }
- }
- if (!webViewReady) Slog.w(TAG, "creating relro file timed out");
- return new WebViewProviderResponse(webViewPackage, webViewStatus);
+ return WebViewUpdateService.this.mWebViewUpdater.waitForAndGetProvider();
}
/**
@@ -588,28 +712,28 @@
throw new SecurityException(msg);
}
- return WebViewUpdateService.this.changeProviderAndSetting(newProvider);
+ long callingId = Binder.clearCallingIdentity();
+ try {
+ return WebViewUpdateService.this.mWebViewUpdater.changeProviderAndSetting(
+ newProvider);
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
+ }
}
@Override // Binder call
public WebViewProviderInfo[] getValidWebViewPackages() {
- synchronized(WebViewUpdateService.this) {
- return mCurrentValidWebViewPackages;
- }
+ return WebViewUpdateService.this.mWebViewUpdater.getValidWebViewPackages();
}
@Override // Binder call
public WebViewProviderInfo[] getAllWebViewPackages() {
- return WebViewUpdateService.this.mWebViewUtility.getWebViewPackages();
+ return WebViewUpdateService.this.mSystemInterface.getWebViewPackages();
}
@Override // Binder call
public String getCurrentWebViewPackageName() {
- synchronized(WebViewUpdateService.this) {
- if (WebViewUpdateService.this.mCurrentWebViewPackage == null)
- return null;
- return WebViewUpdateService.this.mCurrentWebViewPackage.packageName;
- }
+ return WebViewUpdateService.this.mWebViewUpdater.getCurrentWebViewPackageName();
}
@Override // Binder call
@@ -630,7 +754,7 @@
throw new SecurityException(msg);
}
- WebViewUpdateService.enableFallbackLogic(enable);
+ WebViewUpdateService.this.mSystemInterface.enableFallbackLogic(enable);
}
}
}
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceShellCommand.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceShellCommand.java
index a9461e8..68448f3 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceShellCommand.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceShellCommand.java
@@ -36,12 +36,13 @@
final PrintWriter pw = getOutPrintWriter();
try {
- // TODO(gsennton) add command for changing WebView provider
switch(cmd) {
case "enable-redundant-packages":
return enableFallbackLogic(false);
case "disable-redundant-packages":
return enableFallbackLogic(true);
+ case "set-webview-implementation":
+ return setWebViewImplementation();
default:
return handleDefaultCommands(cmd);
}
@@ -58,6 +59,21 @@
return 0;
}
+ private int setWebViewImplementation() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ String shellChosenPackage = getNextArg();
+ String newPackage = mInterface.changeProviderAndSetting(shellChosenPackage);
+ if (shellChosenPackage.equals(newPackage)) {
+ pw.println("Success");
+ return 0;
+ } else {
+ pw.println(String.format(
+ "Failed to switch to %s, the WebView implementation is now provided by %s.",
+ shellChosenPackage, newPackage));
+ return 1;
+ }
+ }
+
@Override
public void onHelp() {
PrintWriter pw = getOutPrintWriter();
@@ -72,6 +88,8 @@
pw.println(" disable-redundant-packages");
pw.println(" Disallow installing and enabling fallback packages when a more-preferred");
pw.println(" package is available.");
+ pw.println(" set-webview-implementation PACKAGE");
+ pw.println(" Set the WebView implementation to the specified package.");
pw.println();
}
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 2c15818..bae628a 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -21,6 +21,7 @@
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
+import android.annotation.NonNull;
import android.app.Service;
import android.content.Context;
import android.graphics.Canvas;
@@ -38,6 +39,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
@@ -122,6 +124,12 @@
}
}
+ public void getMagnificationRegionsLocked(Region outMagnified, Region outAvailable) {
+ if (mDisplayMagnifier != null) {
+ mDisplayMagnifier.getMagnificationRegionsLocked(outMagnified, outAvailable);
+ }
+ }
+
public void onRectangleOnScreenRequestedLocked(Rect rectangle) {
if (mDisplayMagnifier != null) {
mDisplayMagnifier.onRectangleOnScreenRequestedLocked(rectangle);
@@ -392,6 +400,10 @@
return spec;
}
+ public void getMagnificationRegionsLocked(Region outMagnified, Region outAvailable) {
+ mMagnifedViewport.getBoundsLocked(outMagnified, outAvailable);
+ }
+
public void destroyLocked() {
mMagnifedViewport.destroyWindow();
}
@@ -413,6 +425,7 @@
private final Matrix mTempMatrix = new Matrix();
private final Region mMagnifiedBounds = new Region();
+ private final Region mAvailableBounds = new Region();
private final Region mOldMagnifiedBounds = new Region();
private final Region mOldAvailableBounds = new Region();
@@ -450,6 +463,12 @@
recomputeBoundsLocked();
}
+ public void getBoundsLocked(@NonNull Region outMagnified,
+ @NonNull Region outAvailable) {
+ outMagnified.set(mMagnifiedBounds);
+ outAvailable.set(mAvailableBounds);
+ }
+
public void updateMagnificationSpecLocked(MagnificationSpec spec) {
if (spec != null) {
mMagnificationSpec.initialize(spec.scale, spec.offsetX, spec.offsetY);
@@ -469,14 +488,11 @@
final int screenWidth = mTempPoint.x;
final int screenHeight = mTempPoint.y;
- Region magnifiedBounds = mMagnifiedBounds;
- magnifiedBounds.set(0, 0, 0, 0);
-
- Region availableBounds = mTempRegion1;
- availableBounds.set(0, 0, screenWidth, screenHeight);
+ mMagnifiedBounds.set(0, 0, 0, 0);
+ mAvailableBounds.set(0, 0, screenWidth, screenHeight);
if (mCircularPath != null) {
- availableBounds.setPath(mCircularPath, availableBounds);
+ mAvailableBounds.setPath(mCircularPath, mAvailableBounds);
}
Region nonMagnifiedBounds = mTempRegion4;
@@ -494,36 +510,37 @@
continue;
}
- Region windowBounds = mTempRegion2;
+ // Consider the touchable portion of the window
Matrix matrix = mTempMatrix;
populateTransformationMatrixLocked(windowState, matrix);
+ Region touchableRegion = mTempRegion3;
+ windowState.getTouchableRegion(touchableRegion);
+ Rect touchableFrame = mTempRect1;
+ touchableRegion.getBounds(touchableFrame);
RectF windowFrame = mTempRectF;
+ windowFrame.set(touchableFrame);
+ windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top);
+ matrix.mapRect(windowFrame);
+ Region windowBounds = mTempRegion2;
+ windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
+ (int) windowFrame.right, (int) windowFrame.bottom);
+ // Only update new regions
+ Region portionOfWindowAlreadyAccountedFor = mTempRegion3;
+ portionOfWindowAlreadyAccountedFor.set(mMagnifiedBounds);
+ portionOfWindowAlreadyAccountedFor.op(nonMagnifiedBounds, Region.Op.UNION);
+ windowBounds.op(portionOfWindowAlreadyAccountedFor, Region.Op.DIFFERENCE);
if (mWindowManagerService.mPolicy.canMagnifyWindow(windowState.mAttrs.type)) {
- windowFrame.set(windowState.mFrame);
- windowFrame.offset(-windowFrame.left, -windowFrame.top);
- matrix.mapRect(windowFrame);
- windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
- (int) windowFrame.right, (int) windowFrame.bottom);
- magnifiedBounds.op(windowBounds, Region.Op.UNION);
- magnifiedBounds.op(availableBounds, Region.Op.INTERSECT);
+ mMagnifiedBounds.op(windowBounds, Region.Op.UNION);
+ mMagnifiedBounds.op(mAvailableBounds, Region.Op.INTERSECT);
} else {
- Region touchableRegion = mTempRegion3;
- windowState.getTouchableRegion(touchableRegion);
- Rect touchableFrame = mTempRect1;
- touchableRegion.getBounds(touchableFrame);
- windowFrame.set(touchableFrame);
- windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top);
- matrix.mapRect(windowFrame);
- windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
- (int) windowFrame.right, (int) windowFrame.bottom);
nonMagnifiedBounds.op(windowBounds, Region.Op.UNION);
- windowBounds.op(magnifiedBounds, Region.Op.DIFFERENCE);
- availableBounds.op(windowBounds, Region.Op.DIFFERENCE);
+ mAvailableBounds.op(windowBounds, Region.Op.DIFFERENCE);
}
+ // Update accounted bounds
Region accountedBounds = mTempRegion2;
- accountedBounds.set(magnifiedBounds);
+ accountedBounds.set(mMagnifiedBounds);
accountedBounds.op(nonMagnifiedBounds, Region.Op.UNION);
accountedBounds.op(0, 0, screenWidth, screenHeight, Region.Op.INTERSECT);
@@ -539,15 +556,15 @@
visibleWindows.clear();
- magnifiedBounds.op(mDrawBorderInset, mDrawBorderInset,
+ mMagnifiedBounds.op(mDrawBorderInset, mDrawBorderInset,
screenWidth - mDrawBorderInset, screenHeight - mDrawBorderInset,
Region.Op.INTERSECT);
- final boolean magnifiedChanged = !mOldMagnifiedBounds.equals(magnifiedBounds);
- final boolean availableChanged = !mOldAvailableBounds.equals(availableBounds);
+ final boolean magnifiedChanged = !mOldMagnifiedBounds.equals(mMagnifiedBounds);
+ final boolean availableChanged = !mOldAvailableBounds.equals(mAvailableBounds);
if (magnifiedChanged || availableChanged) {
if (magnifiedChanged) {
- mWindow.setBounds(magnifiedBounds);
+ mWindow.setBounds(mMagnifiedBounds);
Rect dirtyRect = mTempRect1;
if (mFullRedrawNeeded) {
mFullRedrawNeeded = false;
@@ -557,23 +574,23 @@
mWindow.invalidate(dirtyRect);
} else {
Region dirtyRegion = mTempRegion3;
- dirtyRegion.set(magnifiedBounds);
+ dirtyRegion.set(mMagnifiedBounds);
dirtyRegion.op(mOldMagnifiedBounds, Region.Op.UNION);
dirtyRegion.op(nonMagnifiedBounds, Region.Op.INTERSECT);
dirtyRegion.getBounds(dirtyRect);
mWindow.invalidate(dirtyRect);
}
- mOldMagnifiedBounds.set(magnifiedBounds);
+ mOldMagnifiedBounds.set(mMagnifiedBounds);
}
if (availableChanged) {
- mOldAvailableBounds.set(availableBounds);
+ mOldAvailableBounds.set(mAvailableBounds);
}
final SomeArgs args = SomeArgs.obtain();
- args.arg1 = Region.obtain(magnifiedBounds);
- args.arg2 = Region.obtain(availableBounds);
+ args.arg1 = Region.obtain(mMagnifiedBounds);
+ args.arg2 = Region.obtain(mAvailableBounds);
mHandler.obtainMessage(
MyHandler.MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED, args).sendToTarget();
}
@@ -1197,6 +1214,8 @@
window.type = windowState.mAttrs.type;
window.layer = windowState.mLayer;
window.token = windowState.mClient.asBinder();
+ window.title = windowState.mAttrs.getTitle();
+ window.accessibilityIdOfAnchor = windowState.mAttrs.accessibilityIdOfAnchor;
WindowState attachedWindow = windowState.mAttachedWindow;
if (attachedWindow != null) {
@@ -1269,6 +1288,12 @@
&& !oldWindow.childTokens.equals(newWindow.childTokens)) {
return true;
}
+ if (!TextUtils.equals(oldWindow.title, newWindow.title)) {
+ return true;
+ }
+ if (oldWindow.accessibilityIdOfAnchor != newWindow.accessibilityIdOfAnchor) {
+ return true;
+ }
return false;
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 5cb7099..11b01cc 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -43,10 +43,14 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
+import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.res.Configuration;
import android.graphics.Bitmap;
+import android.graphics.Path;
import android.graphics.Rect;
import android.os.Debug;
import android.os.IBinder;
@@ -73,6 +77,7 @@
import com.android.server.wm.WindowManagerService.H;
import com.android.server.wm.animation.ClipRectLRAnimation;
import com.android.server.wm.animation.ClipRectTBAnimation;
+import com.android.server.wm.animation.CurvedTranslateAnimation;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -127,6 +132,8 @@
public static final int TRANSIT_TASK_IN_PLACE = 17;
/** An activity is being relaunched (e.g. due to configuration change). */
public static final int TRANSIT_ACTIVITY_RELAUNCH = 18;
+ /** A task is being docked from recents. */
+ public static final int TRANSIT_DOCK_TASK_FROM_RECENTS = 19;
/** Fraction of animation at which the recents thumbnail stays completely transparent */
private static final float RECENTS_THUMBNAIL_FADEIN_FRACTION = 0.5f;
@@ -139,13 +146,15 @@
static final Interpolator TOUCH_RESPONSE_INTERPOLATOR =
new PathInterpolator(0.3f, 0f, 0.1f, 1f);
+ private static final Interpolator THUMBNAIL_DOCK_INTERPOLATOR =
+ new PathInterpolator(0.85f, 0f, 1f, 1f);
+
/**
* Maximum duration for the clip reveal animation. This is used when there is a lot of movement
* involved, to make it more understandable.
*/
private static final int MAX_CLIP_REVEAL_TRANSITION_DURATION = 420;
private static final int THUMBNAIL_APP_TRANSITION_DURATION = 336;
- private static final int THUMBNAIL_APP_TRANSITION_ALPHA_DURATION = 336;
private static final long APP_TRANSITION_TIMEOUT_MS = 5000;
private final Context mContext;
@@ -206,6 +215,7 @@
private final Interpolator mThumbnailFadeOutInterpolator;
private final Interpolator mLinearOutSlowInInterpolator;
private final Interpolator mFastOutLinearInInterpolator;
+ private final Interpolator mFastOutSlowInInterpolator;
private final Interpolator mClipHorizontalInterpolator = new PathInterpolator(0, 0, 0.4f, 1f);
private final int mClipRevealTranslationY;
@@ -226,6 +236,8 @@
com.android.internal.R.interpolator.linear_out_slow_in);
mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
com.android.internal.R.interpolator.fast_out_linear_in);
+ mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.fast_out_slow_in);
mConfigShortAnimTime = context.getResources().getInteger(
com.android.internal.R.integer.config_shortAnimTime);
mDecelerateInterpolator = AnimationUtils.loadInterpolator(context,
@@ -812,12 +824,14 @@
* Prepares the specified animation with a standard duration, interpolator, etc.
*/
Animation prepareThumbnailAnimationWithDuration(Animation a, int appWidth, int appHeight,
- int duration, Interpolator interpolator) {
+ long duration, Interpolator interpolator) {
if (duration > 0) {
a.setDuration(duration);
}
a.setFillAfter(true);
- a.setInterpolator(interpolator);
+ if (interpolator != null) {
+ a.setInterpolator(interpolator);
+ }
a.initialize(appWidth, appHeight, appWidth, appHeight);
return a;
}
@@ -866,55 +880,95 @@
* This animation runs for the thumbnail that gets cross faded with the enter/exit activity
* when a thumbnail is specified with the pending animation override.
*/
- Animation createThumbnailAspectScaleAnimationLocked(Rect appRect, Bitmap thumbnailHeader,
- final int taskId) {
+ Animation createThumbnailAspectScaleAnimationLocked(Rect appRect, @Nullable Rect contentInsets,
+ Bitmap thumbnailHeader, final int taskId, int uiMode, int orientation) {
Animation a;
final int thumbWidthI = thumbnailHeader.getWidth();
final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
final int thumbHeightI = thumbnailHeader.getHeight();
- final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
final int appWidth = appRect.width();
float scaleW = appWidth / thumbWidth;
- float unscaledHeight = thumbHeight * scaleW;
getNextAppTransitionStartRect(taskId, mTmpRect);
- final float unscaledStartY = mTmpRect.top - (unscaledHeight - thumbHeight) / 2f;
- final float toY = appRect.top + -unscaledStartY;
+ final float fromX;
+ final float fromY;
+ final float toX;
+ final float toY;
+ final float pivotX;
+ final float pivotY;
+ if (isTvUiMode(uiMode) || orientation == Configuration.ORIENTATION_PORTRAIT) {
+ fromX = mTmpRect.left;
+ fromY = mTmpRect.top;
+
+ // For the curved translate animation to work, the pivot points needs to be at the
+ // same absolute position as the one from the real surface.
+ toX = mTmpRect.width() / 2 * (scaleW - 1f) + appRect.left;
+ toY = appRect.height() / 2 * (1 - 1 / scaleW) + appRect.top;
+ pivotX = mTmpRect.width() / 2;
+ pivotY = appRect.height() / 2 / scaleW;
+ } else {
+ pivotX = 0;
+ pivotY = 0;
+ fromX = mTmpRect.left;
+ fromY = mTmpRect.top;
+ toX = appRect.left;
+ toY = appRect.top;
+ }
+ final long duration = getAspectScaleDuration();
+ final Interpolator interpolator = getAspectScaleInterpolator();
if (mNextAppTransitionScaleUp) {
// Animation up from the thumbnail to the full screen
- Animation scale = new ScaleAnimation(1f, scaleW, 1f, scaleW,
- mTmpRect.left + (thumbWidth / 2f), mTmpRect.top + (thumbHeight / 2f));
- scale.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
- scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+ Animation scale = new ScaleAnimation(1f, scaleW, 1f, scaleW, pivotX, pivotY);
+ scale.setInterpolator(interpolator);
+ scale.setDuration(duration);
Animation alpha = new AlphaAnimation(1f, 0f);
- alpha.setInterpolator(mThumbnailFadeOutInterpolator);
- alpha.setDuration(THUMBNAIL_APP_TRANSITION_ALPHA_DURATION);
- final float toX = appRect.left + appRect.width() / 2 -
- (mTmpRect.left + thumbWidth / 2);
- Animation translate = new TranslateAnimation(0, toX, 0, toY);
- translate.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
- translate.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+ alpha.setInterpolator(mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS
+ ? THUMBNAIL_DOCK_INTERPOLATOR : mThumbnailFadeOutInterpolator);
+ alpha.setDuration(mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS
+ ? duration / 2
+ : duration);
+ Animation translate = createCurvedMotion(fromX, toX, fromY, toY);
+ translate.setInterpolator(interpolator);
+ translate.setDuration(duration);
+
+ mTmpFromClipRect.set(0, 0, thumbWidthI, thumbHeightI);
+ mTmpToClipRect.set(appRect);
+
+ // Containing frame is in screen space, but we need the clip rect in the
+ // app space.
+ mTmpToClipRect.offsetTo(0, 0);
+ mTmpToClipRect.right = (int) (mTmpToClipRect.right / scaleW);
+ mTmpToClipRect.bottom = (int) (mTmpToClipRect.bottom / scaleW);
+
+ if (contentInsets != null) {
+ mTmpToClipRect.inset((int) (-contentInsets.left * scaleW),
+ (int) (-contentInsets.top * scaleW),
+ (int) (-contentInsets.right * scaleW),
+ (int) (-contentInsets.bottom * scaleW));
+ }
+
+ Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
+ clipAnim.setInterpolator(interpolator);
+ clipAnim.setDuration(duration);
// This AnimationSet uses the Interpolators assigned above.
AnimationSet set = new AnimationSet(false);
set.addAnimation(scale);
set.addAnimation(alpha);
set.addAnimation(translate);
+ set.addAnimation(clipAnim);
a = set;
} else {
// Animation down from the full screen to the thumbnail
- Animation scale = new ScaleAnimation(scaleW, 1f, scaleW, 1f,
- mTmpRect.left + (thumbWidth / 2f), mTmpRect.top + (thumbHeight / 2f));
- scale.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
- scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+ Animation scale = new ScaleAnimation(scaleW, 1f, scaleW, 1f, pivotX, pivotY);
+ scale.setInterpolator(interpolator);
+ scale.setDuration(duration);
Animation alpha = new AlphaAnimation(0f, 1f);
alpha.setInterpolator(mThumbnailFadeInInterpolator);
- alpha.setDuration(THUMBNAIL_APP_TRANSITION_ALPHA_DURATION);
- final float toX = appRect.left + appRect.width() / 2 -
- (mTmpRect.left + thumbWidth / 2);
- Animation translate = new TranslateAnimation(toX, 0, toY, 0);
- translate.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
- translate.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+ alpha.setDuration(duration);
+ Animation translate = createCurvedMotion(toX, fromX, toY, fromY);
+ translate.setInterpolator(interpolator);
+ translate.setDuration(duration);
// This AnimationSet uses the Interpolators assigned above.
AnimationSet set = new AnimationSet(false);
@@ -925,7 +979,48 @@
}
return prepareThumbnailAnimationWithDuration(a, appWidth, appRect.height(), 0,
- TOUCH_RESPONSE_INTERPOLATOR);
+ null);
+ }
+
+ private Animation createCurvedMotion(float fromX, float toX, float fromY, float toY) {
+
+ // Almost no x-change - use linear animation
+ if (Math.abs(toX - fromX) < 1f || mNextAppTransition != TRANSIT_DOCK_TASK_FROM_RECENTS) {
+ return new TranslateAnimation(fromX, toX, fromY, toY);
+ } else {
+ final Path path = createCurvedPath(fromX, toX, fromY, toY);
+ return new CurvedTranslateAnimation(path);
+ }
+ }
+
+ private Path createCurvedPath(float fromX, float toX, float fromY, float toY) {
+ final Path path = new Path();
+ path.moveTo(fromX, fromY);
+
+ if (fromY > toY) {
+ // If the object needs to go up, move it in horizontal direction first, then vertical.
+ path.cubicTo(fromX, fromY, toX, 0.9f * fromY + 0.1f * toY, toX, toY);
+ } else {
+ // If the object needs to go down, move it in vertical direction first, then horizontal.
+ path.cubicTo(fromX, fromY, fromX, 0.1f * fromY + 0.9f * toY, toX, toY);
+ }
+ return path;
+ }
+
+ private long getAspectScaleDuration() {
+ if (mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS) {
+ return (long) (THUMBNAIL_APP_TRANSITION_DURATION * 1.35f);
+ } else {
+ return THUMBNAIL_APP_TRANSITION_DURATION;
+ }
+ }
+
+ private Interpolator getAspectScaleInterpolator() {
+ if (mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS) {
+ return mFastOutSlowInInterpolator;
+ } else {
+ return TOUCH_RESPONSE_INTERPOLATOR;
+ }
}
/**
@@ -933,7 +1028,7 @@
* activity that is leaving, and the activity that is entering.
*/
Animation createAspectScaledThumbnailEnterExitAnimationLocked(int thumbTransitState,
- int orientation, int transit, Rect containingFrame, Rect contentInsets,
+ int uiMode, int orientation, int transit, Rect containingFrame, Rect contentInsets,
@Nullable Rect surfaceInsets, boolean freeform, int taskId) {
Animation a;
final int appWidth = containingFrame.width();
@@ -943,17 +1038,23 @@
final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
final int thumbHeightI = mTmpRect.height();
final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
-
- // Used for the ENTER_SCALE_UP and EXIT_SCALE_DOWN transitions
- float scale = 1f;
- int scaledTopDecor = 0;
+ final int thumbStartX = mTmpRect.left - containingFrame.left;
+ final int thumbStartY = mTmpRect.top - containingFrame.top;
switch (thumbTransitState) {
- case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: {
- if (freeform) {
+ case THUMBNAIL_TRANSITION_ENTER_SCALE_UP:
+ case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
+ final boolean scaleUp = thumbTransitState == THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
+ if (freeform && scaleUp) {
a = createAspectScaledThumbnailEnterFreeformAnimationLocked(
containingFrame, surfaceInsets, taskId);
+ } else if (freeform) {
+ a = createAspectScaledThumbnailExitFreeformAnimationLocked(
+ containingFrame, surfaceInsets, taskId);
} else {
+ AnimationSet set = new AnimationSet(true);
+
+ // In portrait, we scale to fit the width
mTmpFromClipRect.set(containingFrame);
mTmpToClipRect.set(containingFrame);
@@ -964,26 +1065,60 @@
// Exclude insets region from the source clip.
mTmpFromClipRect.inset(contentInsets);
-
- // We scale the width and clip to the top/left square
- scale = thumbWidth / (appWidth - contentInsets.left - contentInsets.right);
- scaledTopDecor = (int) (scale * contentInsets.top);
- int unscaledThumbHeight = (int) (thumbHeight / scale);
- mTmpFromClipRect.bottom = mTmpFromClipRect.top + unscaledThumbHeight;
-
mNextAppTransitionInsets.set(contentInsets);
- Animation scaleAnim = new ScaleAnimation(scale, 1, scale, 1,
- computePivot(mTmpRect.left - containingFrame.left, scale),
- computePivot(mTmpRect.top - containingFrame.top, scale));
- Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
- Animation translateAnim = new TranslateAnimation(0, 0, -scaledTopDecor, 0);
+ if (isTvUiMode(uiMode) || orientation == Configuration.ORIENTATION_PORTRAIT) {
+ // We scale the width and clip to the top/left square
+ float scale = thumbWidth /
+ (appWidth - contentInsets.left - contentInsets.right);
+ int unscaledThumbHeight = (int) (thumbHeight / scale);
+ mTmpFromClipRect.bottom = mTmpFromClipRect.top + unscaledThumbHeight;
- AnimationSet set = new AnimationSet(true);
- set.addAnimation(clipAnim);
- set.addAnimation(scaleAnim);
- set.addAnimation(translateAnim);
+ mNextAppTransitionInsets.set(contentInsets);
+
+ Animation scaleAnim = new ScaleAnimation(
+ scaleUp ? scale : 1, scaleUp ? 1 : scale,
+ scaleUp ? scale : 1, scaleUp ? 1 : scale,
+ containingFrame.width() / 2f,
+ containingFrame.height() / 2f + contentInsets.top);
+ final float targetX = (mTmpRect.left - containingFrame.left);
+ final float x = containingFrame.width() / 2f
+ - containingFrame.width() / 2f * scale;
+ final float targetY = (mTmpRect.top - containingFrame.top);
+ final float y = containingFrame.height() / 2f
+ - containingFrame.height() / 2f * scale;
+ final float startX = targetX - x;
+ final float startY = targetY - y;
+ Animation clipAnim = scaleUp
+ ? new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect)
+ : new ClipRectAnimation(mTmpToClipRect, mTmpFromClipRect);
+ Animation translateAnim = scaleUp
+ ? createCurvedMotion(startX, 0, startY - contentInsets.top, 0)
+ : createCurvedMotion(0, startX, 0, startY - contentInsets.top);
+
+ set.addAnimation(clipAnim);
+ set.addAnimation(scaleAnim);
+ set.addAnimation(translateAnim);
+
+ } else {
+ // In landscape, we don't scale at all and only crop
+ mTmpFromClipRect.bottom = mTmpFromClipRect.top + thumbHeightI;
+ mTmpFromClipRect.right = mTmpFromClipRect.left + thumbWidthI;
+
+ Animation clipAnim = scaleUp
+ ? new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect)
+ : new ClipRectAnimation(mTmpToClipRect, mTmpFromClipRect);
+ Animation translateAnim = scaleUp
+ ? createCurvedMotion(thumbStartX, 0,
+ thumbStartY - contentInsets.top, 0)
+ : createCurvedMotion(0, thumbStartX, 0,
+ thumbStartY - contentInsets.top);
+
+ set.addAnimation(clipAnim);
+ set.addAnimation(translateAnim);
+ }
a = set;
+ a.setZAdjustment(Animation.ZORDER_TOP);
}
break;
}
@@ -1009,55 +1144,12 @@
}
break;
}
- case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
- // App window scaling down from full screen
- if (freeform) {
- a = createAspectScaledThumbnailExitFreeformAnimationLocked(
- containingFrame, surfaceInsets, taskId);
- } else {
- mTmpFromClipRect.set(containingFrame);
- mTmpToClipRect.set(containingFrame);
-
- // Containing frame is in screen space, but we need the clip rect in the
- // app space.
- mTmpFromClipRect.offsetTo(0, 0);
- mTmpToClipRect.offsetTo(0, 0);
-
- // Exclude insets region from the target clip.
- mTmpToClipRect.inset(contentInsets);
-
- // We scale the width and clip to the top/left square
- scale = thumbWidth / (appWidth - contentInsets.left - contentInsets.right);
- scaledTopDecor = (int) (scale * contentInsets.top);
- int unscaledThumbHeight = (int) (thumbHeight / scale);
- mTmpToClipRect.bottom = mTmpToClipRect.top + unscaledThumbHeight;
-
- mNextAppTransitionInsets.set(contentInsets);
-
- Animation scaleAnim = new ScaleAnimation(1, scale, 1, scale,
- computePivot(mTmpRect.left - containingFrame.left, scale),
- computePivot(mTmpRect.top - containingFrame.top, scale));
- Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
- Animation translateAnim = new TranslateAnimation(0, 0, 0, -scaledTopDecor);
-
- AnimationSet set = new AnimationSet(true);
- set.addAnimation(clipAnim);
- set.addAnimation(scaleAnim);
- set.addAnimation(translateAnim);
-
- a = set;
- a.setZAdjustment(Animation.ZORDER_TOP);
- }
- break;
- }
default:
throw new RuntimeException("Invalid thumbnail transition state");
}
- int duration = Math.max(THUMBNAIL_APP_TRANSITION_ALPHA_DURATION,
- THUMBNAIL_APP_TRANSITION_DURATION);
- return prepareThumbnailAnimationWithDuration(a, appWidth, appHeight, duration,
- TOUCH_RESPONSE_INTERPOLATOR);
+ return prepareThumbnailAnimationWithDuration(a, appWidth, appHeight,
+ getAspectScaleDuration(), getAspectScaleInterpolator());
}
private Animation createAspectScaledThumbnailEnterFreeformAnimationLocked(Rect frame,
@@ -1308,7 +1400,7 @@
* to the recents thumbnail and hence need to account for the surface being
* bigger.
*/
- Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
+ Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter, int uiMode,
int orientation, Rect frame, Rect displayFrame, Rect insets,
@Nullable Rect surfaceInsets, boolean isVoiceInteraction, boolean freeform,
int taskId) {
@@ -1388,7 +1480,7 @@
mNextAppTransitionScaleUp =
(mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP);
a = createAspectScaledThumbnailEnterExitAnimationLocked(
- getThumbnailTransitionState(enter), orientation, transit, frame,
+ getThumbnailTransitionState(enter), uiMode, orientation, transit, frame,
insets, surfaceInsets, freeform, taskId);
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
String animName = mNextAppTransitionScaleUp ?
@@ -1411,6 +1503,7 @@
? WindowAnimation_activityCloseEnterAnimation
: WindowAnimation_activityCloseExitAnimation;
break;
+ case TRANSIT_DOCK_TASK_FROM_RECENTS:
case TRANSIT_TASK_OPEN:
animAttr = enter
? WindowAnimation_taskOpenEnterAnimation
@@ -1467,6 +1560,14 @@
return a;
}
+ int getAppStackClipMode() {
+ return mNextAppTransition == TRANSIT_ACTIVITY_RELAUNCH
+ || mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS
+ || mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL
+ ? STACK_CLIP_NONE
+ : STACK_CLIP_AFTER_ANIM;
+ }
+
void postAnimationCallback() {
if (mNextAppTransitionCallback != null) {
mService.mH.sendMessage(mService.mH.obtainMessage(H.DO_ANIMATION_CALLBACK,
@@ -1686,6 +1787,9 @@
case TRANSIT_ACTIVITY_RELAUNCH: {
return "TRANSIT_ACTIVITY_RELAUNCH";
}
+ case TRANSIT_DOCK_TASK_FROM_RECENTS: {
+ return "TRANSIT_DOCK_TASK_FROM_RECENTS";
+ }
default: {
return "<UNKNOWN>";
}
@@ -1817,4 +1921,11 @@
}
return prepared;
}
+
+ /**
+ * @return whether the specified {@param uiMode} is the TV mode.
+ */
+ private boolean isTvUiMode(int uiMode) {
+ return (uiMode & Configuration.UI_MODE_TYPE_TELEVISION) > 0;
+ }
}
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index 3a5dec9..aae52e8 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -23,6 +23,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
+import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
import android.graphics.Matrix;
import android.util.Slog;
@@ -76,8 +77,6 @@
// requires that the duration of the two animations are the same.
SurfaceControl thumbnail;
int thumbnailTransactionSeq;
- int thumbnailX;
- int thumbnailY;
int thumbnailLayer;
int thumbnailForceAboveLayer;
Animation thumbnailAnimation;
@@ -106,6 +105,7 @@
boolean usingTransferredAnimation = false;
private boolean mSkipFirstFrame = false;
+ private int mStackClip = STACK_CLIP_BEFORE_ANIM;
static final Animation sDummyAnimation = new DummyAnimation();
@@ -115,7 +115,8 @@
mAnimator = mService.mAnimator;
}
- public void setAnimation(Animation anim, int width, int height, boolean skipFirstFrame) {
+ public void setAnimation(Animation anim, int width, int height, boolean skipFirstFrame,
+ int stackClip) {
if (WindowManagerService.localLOGV) Slog.v(TAG, "Setting animation in " + mAppToken
+ ": " + anim + " wxh=" + width + "x" + height
+ " isVisible=" + mAppToken.isVisible());
@@ -142,6 +143,7 @@
transformation.clear();
transformation.setAlpha(mAppToken.isVisible() ? 1 : 0);
hasTransformation = true;
+ mStackClip = stackClip;
this.mSkipFirstFrame = skipFirstFrame;
@@ -186,6 +188,7 @@
mAppToken.allDrawn = false;
mAppToken.deferClearAllDrawn = false;
}
+ mStackClip = STACK_CLIP_BEFORE_ANIM;
}
public boolean isAnimating() {
@@ -201,6 +204,10 @@
deferThumbnailDestruction = false;
}
+ int getStackClip() {
+ return mStackClip;
+ }
+
void transferCurrentAnimation(
AppWindowAnimator toAppAnimator, WindowStateAnimator transferWinAnimator) {
@@ -245,7 +252,6 @@
thumbnailTransformation.clear();
final long animationFrameTime = getAnimationFrameTime(thumbnailAnimation, currentTime);
thumbnailAnimation.getTransformation(animationFrameTime, thumbnailTransformation);
- thumbnailTransformation.getMatrix().preTranslate(thumbnailX, thumbnailY);
ScreenRotationAnimation screenRotationAnimation =
mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY);
@@ -279,6 +285,7 @@
}
thumbnail.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y],
tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]);
+ thumbnail.setWindowCrop(thumbnailTransformation.getClipRect());
}
/**
@@ -452,8 +459,6 @@
}
if (thumbnail != null) {
pw.print(prefix); pw.print("thumbnail="); pw.print(thumbnail);
- pw.print(" x="); pw.print(thumbnailX);
- pw.print(" y="); pw.print(thumbnailY);
pw.print(" layer="); pw.println(thumbnailLayer);
pw.print(prefix); pw.print("thumbnailAnimation="); pw.println(thumbnailAnimation);
pw.print(prefix); pw.print("thumbnailTransformation=");
diff --git a/services/core/java/com/android/server/wm/DimLayerController.java b/services/core/java/com/android/server/wm/DimLayerController.java
index 97d0ae0..3ec02b9 100644
--- a/services/core/java/com/android/server/wm/DimLayerController.java
+++ b/services/core/java/com/android/server/wm/DimLayerController.java
@@ -183,7 +183,12 @@
for (int i = mState.size() - 1; i >= 0; i--) {
DimLayer.DimLayerUser user = mState.keyAt(i);
- if (user.isFullscreen()) {
+ DimLayerState state = mState.valueAt(i);
+ // We have to check that we are acutally the shared fullscreen layer
+ // for this path. If we began as non fullscreen and became fullscreen
+ // (e.g. Docked stack closing), then we may not be the shared layer
+ // and we have to make sure we always animate the layer.
+ if (user.isFullscreen() && state.dimLayer == mSharedFullScreenDimLayer) {
fullScreen = i;
if (mState.valueAt(i).continueDimming) {
fullScreenAndDimming = i;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 5212211..28379f4 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -429,6 +429,7 @@
}
if (getDockedStackVisibleForUserLocked() != null) {
mDividerControllerLocked.getTouchRegion(mTmpRect);
+ mTmpRegion.set(mTmpRect);
mTouchExcludeRegion.op(mTmpRegion, Op.UNION);
}
if (mTapDetector != null) {
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 36e8bbb..6ac71c7 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -41,6 +41,8 @@
import com.android.server.wm.DimLayer.DimLayerUser;
+import java.util.ArrayList;
+
/**
* Keeps information about the docked stack divider.
*/
@@ -74,8 +76,8 @@
private final WindowManagerService mService;
private final DisplayContent mDisplayContent;
- private final int mDividerWindowWidth;
- private final int mDividerInsets;
+ private int mDividerWindowWidth;
+ private int mDividerInsets;
private boolean mResizing;
private WindowState mWindow;
private final Rect mTmpRect = new Rect();
@@ -87,7 +89,7 @@
private final DimLayer mDimLayer;
private boolean mMinimizedDock;
- private boolean mAnimating;
+ private boolean mAnimatingForMinimizedDockedStack;
private boolean mAnimationStarted;
private long mAnimationStartTime;
private float mAnimationStart;
@@ -96,19 +98,30 @@
private final Interpolator mMinimizedDockInterpolator;
private float mMaximizeMeetFraction;
private final Rect mTouchRegion = new Rect();
+ private boolean mAnimatingForIme;
+ private boolean mAdjustedForIme;
DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) {
mService = service;
mDisplayContent = displayContent;
final Context context = service.mContext;
- mDividerWindowWidth = context.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.docked_stack_divider_thickness);
- mDividerInsets = context.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.docked_stack_divider_insets);
mDimLayer = new DimLayer(displayContent.mService, this, displayContent.getDisplayId(),
"DockedStackDim");
mMinimizedDockInterpolator = AnimationUtils.loadInterpolator(
context, android.R.interpolator.fast_out_slow_in);
+ loadDimens();
+ }
+
+ private void loadDimens() {
+ final Context context = mService.mContext;
+ mDividerWindowWidth = context.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.docked_stack_divider_thickness);
+ mDividerInsets = context.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.docked_stack_divider_insets);
+ }
+
+ void onConfigurationChanged() {
+ loadDimens();
}
boolean isResizing() {
@@ -173,6 +186,13 @@
return mLastVisibility;
}
+ void setAdjustedForIme(boolean adjusted, boolean animate) {
+ if (mAdjustedForIme != adjusted) {
+ mAnimatingForIme = animate;
+ mAdjustedForIme = adjusted;
+ }
+ }
+
void positionDockedStackedDivider(Rect frame) {
TaskStack stack = mDisplayContent.getDockedStackLocked();
if (stack == null) {
@@ -232,6 +252,9 @@
}
}
mDockedStackListeners.finishBroadcast();
+ if (!exists) {
+ setMinimizedDockedStack(false);
+ }
}
void notifyDockedStackMinimizedChanged(boolean minimizedDock, long animDuration) {
@@ -252,6 +275,7 @@
notifyDockedDividerVisibilityChanged(wasVisible());
notifyDockedStackExistsChanged(
mDisplayContent.mService.mStackIdToStack.get(DOCKED_STACK_ID) != null);
+ notifyDockedStackMinimizedChanged(mMinimizedDock, 0 /* animDuration */);
}
void setResizeDimLayer(boolean visible, int targetStackId, float alpha) {
@@ -327,12 +351,14 @@
* @param animate Whether to animate the change.
*/
private void setMinimizedDockedStack(boolean minimizedDock, boolean animate) {
- if (minimizedDock == mMinimizedDock
+ final boolean wasMinimized = mMinimizedDock;
+ mMinimizedDock = minimizedDock;
+ if (minimizedDock == wasMinimized
|| mDisplayContent.getDockedStackVisibleForUserLocked() == null) {
return;
}
- mMinimizedDock = minimizedDock;
+ mAnimatingForIme = false;
if (minimizedDock) {
if (animate) {
startAdjustAnimation(0f, 1f);
@@ -349,7 +375,7 @@
}
private void startAdjustAnimation(float from, float to) {
- mAnimating = true;
+ mAnimatingForMinimizedDockedStack = true;
mAnimationStarted = false;
mAnimationStart = from;
mAnimationTarget = to;
@@ -357,13 +383,13 @@
private void setMinimizedDockedStack(boolean minimized) {
final TaskStack stack = mDisplayContent.getDockedStackVisibleForUserLocked();
+ notifyDockedStackMinimizedChanged(minimized, 0);
if (stack == null) {
return;
}
if (stack.setAdjustedForMinimizedDock(minimized ? 1f : 0f)) {
mService.mWindowPlacerLocked.performSurfacePlacement();
}
- notifyDockedStackMinimizedChanged(minimized, 0);
}
private boolean isAnimationMaximizing() {
@@ -371,10 +397,45 @@
}
public boolean animate(long now) {
- if (!mAnimating) {
+ if (mAnimatingForMinimizedDockedStack) {
+ return animateForMinimizedDockedStack(now);
+ } else if (mAnimatingForIme) {
+ return animateForIme();
+ } else {
return false;
}
+ }
+ private boolean animateForIme() {
+ boolean updated = false;
+ boolean animating = false;
+
+ final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
+ for (int i = stacks.size() - 1; i >= 0; --i) {
+ final TaskStack stack = stacks.get(i);
+ if (stack != null && stack.isAdjustedForIme()) {
+ updated |= stack.updateAdjustForIme();
+ animating |= stack.isAnimatingForIme();
+ }
+ }
+
+ if (updated) {
+ mService.mWindowPlacerLocked.performSurfacePlacement();
+ }
+
+ if (!animating) {
+ mAnimatingForIme = false;
+ for (int i = stacks.size() - 1; i >= 0; --i) {
+ final TaskStack stack = stacks.get(i);
+ if (stack != null) {
+ stack.clearImeGoingAway();
+ }
+ }
+ }
+ return animating;
+ }
+
+ private boolean animateForMinimizedDockedStack(long now) {
final TaskStack stack = mDisplayContent.getDockedStackVisibleForUserLocked();
if (!mAnimationStarted) {
mAnimationStarted = true;
@@ -397,7 +458,7 @@
}
}
if (t >= 1.0f) {
- mAnimating = false;
+ mAnimatingForMinimizedDockedStack = false;
return false;
} else {
return true;
diff --git a/services/core/java/com/android/server/wm/DragResizeMode.java b/services/core/java/com/android/server/wm/DragResizeMode.java
new file mode 100644
index 0000000..08acf9d
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DragResizeMode.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.HOME_STACK_ID;
+
+/**
+ * Describes the mode in which a window is drag resizing.
+ */
+class DragResizeMode {
+
+ /**
+ * Freeform mode: Client surface is fullscreen, and client is responsible to draw window at
+ * the correct position.
+ */
+ static final int DRAG_RESIZE_MODE_FREEFORM = 0;
+
+ /**
+ * Mode for resizing the docked (and adjacent) stack: Client surface is fullscreen, but window
+ * is drawn at (0, 0), window manager is responsible for positioning the surface when draging.
+ */
+ static final int DRAG_RESIZE_MODE_DOCKED_DIVIDER = 1;
+
+ static boolean isModeAllowedForStack(int stackId, int mode) {
+ switch (mode) {
+ case DRAG_RESIZE_MODE_FREEFORM:
+ return stackId == FREEFORM_WORKSPACE_STACK_ID;
+ case DRAG_RESIZE_MODE_DOCKED_DIVIDER:
+ return stackId == DOCKED_STACK_ID
+ || stackId == FULLSCREEN_WORKSPACE_STACK_ID
+ || stackId == HOME_STACK_ID;
+ default:
+ return false;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index cf27b97..aace5e7 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -589,7 +589,7 @@
void overridePointerIconLw(int touchSource) {
mTouchSource = touchSource;
if (isFromSource(InputDevice.SOURCE_MOUSE)) {
- InputManager.getInstance().setPointerIconShape(PointerIcon.STYLE_GRAB);
+ InputManager.getInstance().setPointerIconShape(PointerIcon.STYLE_GRABBING);
}
}
}
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index 0581a16..24783bc 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -16,38 +16,33 @@
package com.android.server.wm;
-import android.os.Looper;
import android.os.Process;
import android.view.Display;
import android.view.InputChannel;
-import android.view.InputEventReceiver;
import android.view.WindowManager;
-import android.view.WindowManagerPolicy;
-
import com.android.server.input.InputApplicationHandle;
import com.android.server.input.InputWindowHandle;
-public final class InputConsumerImpl implements WindowManagerPolicy.InputConsumer {
+class InputConsumerImpl {
final WindowManagerService mService;
final InputChannel mServerChannel, mClientChannel;
final InputApplicationHandle mApplicationHandle;
final InputWindowHandle mWindowHandle;
- final InputEventReceiver mInputEventReceiver;
- final int mWindowLayer;
- public InputConsumerImpl(WindowManagerService service, Looper looper,
- InputEventReceiver.Factory inputEventReceiverFactory) {
- String name = "input consumer";
+ InputConsumerImpl(WindowManagerService service, String name, InputChannel inputChannel) {
mService = service;
InputChannel[] channels = InputChannel.openInputChannelPair(name);
mServerChannel = channels[0];
- mClientChannel = channels[1];
+ if (inputChannel != null) {
+ channels[1].transferTo(inputChannel);
+ channels[1].dispose();
+ mClientChannel = inputChannel;
+ } else {
+ mClientChannel = channels[1];
+ }
mService.mInputManager.registerInputChannel(mServerChannel, null);
- mInputEventReceiver = inputEventReceiverFactory.createInputEventReceiver(
- mClientChannel, looper);
-
mApplicationHandle = new InputApplicationHandle(null);
mApplicationHandle.name = name;
mApplicationHandle.dispatchingTimeoutNanos =
@@ -57,8 +52,7 @@
mWindowHandle.name = name;
mWindowHandle.inputChannel = mServerChannel;
mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
- mWindowLayer = getLayerLw(mWindowHandle.layoutParamsType);
- mWindowHandle.layer = mWindowLayer;
+ mWindowHandle.layer = getLayerLw(mWindowHandle.layoutParamsType);
mWindowHandle.layoutParamsFlags = 0;
mWindowHandle.dispatchingTimeoutNanos =
WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
@@ -81,21 +75,15 @@
mWindowHandle.frameBottom = dh;
}
- @Override
- public void dismiss() {
- synchronized (mService.mWindowMap) {
- if (mService.removeInputConsumer()) {
- mInputEventReceiver.dispose();
- mService.mInputManager.unregisterInputChannel(mServerChannel);
- mClientChannel.dispose();
- mServerChannel.dispose();
- }
- }
- }
-
private int getLayerLw(int windowType) {
return mService.mPolicy.windowTypeToLayerLw(windowType)
* WindowManagerService.TYPE_LAYER_MULTIPLIER
+ WindowManagerService.TYPE_LAYER_OFFSET;
}
+
+ void disposeChannelsLw() {
+ mService.mInputManager.unregisterInputChannel(mServerChannel);
+ mClientChannel.dispose();
+ mServerChannel.dispose();
+ }
}
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index b702180..eea0e73 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -282,6 +282,8 @@
boolean addInputConsumerHandle = mService.mInputConsumer != null;
+ boolean addWallpaperInputConsumerHandle = mService.mWallpaperInputConsumer != null;
+
// Add all windows on the default display.
final int numDisplays = mService.mDisplayContents.size();
final WallpaperController wallpaperController = mService.mWallpaperControllerLocked;
@@ -302,6 +304,14 @@
addInputConsumerHandle = false;
}
+ if (addWallpaperInputConsumerHandle) {
+ if (child.mAttrs.type == WindowManager.LayoutParams.TYPE_WALLPAPER) {
+ // Add the wallpaper input consumer above the first wallpaper window.
+ addInputWindowHandleLw(mService.mWallpaperInputConsumer.mWindowHandle);
+ addWallpaperInputConsumerHandle = false;
+ }
+ }
+
final int flags = child.mAttrs.flags;
final int privateFlags = child.mAttrs.privateFlags;
final int type = child.mAttrs.type;
@@ -329,6 +339,11 @@
}
}
+ if (addWallpaperInputConsumerHandle) {
+ // No wallpaper found, add the wallpaper input consumer at the end.
+ addInputWindowHandleLw(mService.mWallpaperInputConsumer.mWindowHandle);
+ }
+
// Send windows to native code.
mService.mInputManager.setInputWindows(mInputWindowHandles);
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index c0c1ed8..daeb860 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -195,10 +195,8 @@
@Override
public void repositionChild(IWindow window, int left, int top, int right, int bottom,
- int requestedWidth, int requestedHeight,
long deferTransactionUntilFrame, Rect outFrame) {
mService.repositionChild(this, window, left, top, right, bottom,
- requestedWidth, requestedHeight,
deferTransactionUntilFrame, outFrame);
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index f097eb2..8a9ace7 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -18,8 +18,6 @@
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.app.ActivityManager.StackId.HOME_STACK_ID;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
@@ -103,6 +101,7 @@
// Whether the task is currently being drag-resized
private boolean mDragResizing;
+ private int mDragResizeMode;
private boolean mHomeTask;
@@ -140,41 +139,7 @@
final String text =
mService.mContext.getString(R.string.dock_non_resizeble_failed_to_dock_text);
mService.mH.obtainMessage(SHOW_NON_RESIZEABLE_DOCK_TOAST, 0, 0, text).sendToTarget();
- return;
}
-
- final int dockSide = mStack.getDockSide();
- if (mResizeMode != RESIZE_MODE_FORCE_RESIZEABLE || dockSide == DOCKED_INVALID) {
- return;
- }
-
- int xOffset = 0;
- int yOffset = 0;
- mStack.getBounds(mTmpRect);
-
- if (dockSide == DOCKED_LEFT || dockSide == DOCKED_RIGHT) {
- // The toast was originally placed at the bottom and centered. To place it at the
- // bottom-center of the stack, we offset it horizontally by the diff between the center
- // of the stack bounds vs. the center of the screen.
- displayContent.getLogicalDisplayRect(mTmpRect2);
- xOffset = mTmpRect.centerX() - mTmpRect2.centerX();
- } else if (dockSide == DOCKED_TOP) {
- // The toast was originally placed at the bottom and centered. To place it at the bottom
- // center of the top stack, we offset it vertically by the diff between the bottom of
- // the stack bounds vs. the bottom of the content rect.
- //
- // Note here we use the content rect instead of the display rect, as we want the toast's
- // distance to the dock divider (when it's placed at the top half) to be the same as
- // it's distance to the top of the navigation bar (when it's placed at the bottom).
-
- // We don't adjust for DOCKED_BOTTOM case since it's already at the bottom.
- displayContent.getContentRect(mTmpRect2);
- yOffset = mTmpRect2.bottom - mTmpRect.bottom;
- }
- final String text =
- mService.mContext.getString(R.string.dock_forced_resizable);
- mService.mH.obtainMessage(SHOW_NON_RESIZEABLE_DOCK_TOAST,
- xOffset, yOffset, text).sendToTarget();
}
void addAppToken(int addPos, AppWindowToken wtoken, int resizeMode, boolean homeTask) {
@@ -460,7 +425,7 @@
if (mFullscreen
|| !StackId.isTaskResizeableByDockedStack(mStack.mStackId)
|| displayContent == null
- || displayContent.getDockedStackLocked() != null) {
+ || displayContent.getDockedStackVisibleForUserLocked() != null) {
return true;
}
return false;
@@ -533,7 +498,15 @@
return;
}
- out.set(mBounds);
+ if (!mFullscreen) {
+ // When minimizing the docked stack when going home, we don't adjust the task bounds
+ // so we need to intersect the task bounds with the stack bounds here.
+ mStack.getBounds(mTmpRect);
+ mTmpRect.intersect(mBounds);
+ out.set(mTmpRect);
+ } else {
+ out.set(mBounds);
+ }
return;
}
@@ -543,9 +516,14 @@
mStack.getDisplayContent().getLogicalDisplayRect(out);
}
- void setDragResizing(boolean dragResizing) {
+ void setDragResizing(boolean dragResizing, int dragResizeMode) {
if (mDragResizing != dragResizing) {
+ if (!DragResizeMode.isModeAllowedForStack(mStack.mStackId, dragResizeMode)) {
+ throw new IllegalArgumentException("Drag resize mode not allow for stack stackId="
+ + mStack.mStackId + " dragResizeMode=" + dragResizeMode);
+ }
mDragResizing = dragResizing;
+ mDragResizeMode = dragResizeMode;
resetDragResizingChangeReported();
}
}
@@ -564,6 +542,10 @@
return mDragResizing || (mStack != null && mStack.isDragResizing());
}
+ int getDragResizeMode() {
+ return mDragResizeMode;
+ }
+
void updateDisplayInfo(final DisplayContent displayContent) {
if (displayContent == null) {
return;
@@ -613,6 +595,12 @@
if (win.mHasSurface && !resizingWindows.contains(win)) {
if (DEBUG_RESIZE) Slog.d(TAG, "resizeWindows: Resizing " + win);
resizingWindows.add(win);
+
+ // If we are not drag resizing, force recreating of a new surface so updating
+ // the content and positioning that surface will be in sync.
+ if (!win.computeDragResizing()) {
+ win.mResizedWhileNotDragResizing = true;
+ }
}
if (win.isGoneForLayoutLw()) {
win.mResizedWhileGone = true;
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 92701de..ae70aa8 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -29,6 +29,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.dipToPixel;
+import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP;
@@ -379,7 +380,7 @@
private void endDragLocked() {
mResizing = false;
- mTask.setDragResizing(false);
+ mTask.setDragResizing(false, DRAG_RESIZE_MODE_FREEFORM);
}
/** Returns true if the move operation should be ended. */
@@ -409,7 +410,7 @@
bottom = Math.max(top + mMinVisibleHeight, bottom + deltaY);
}
mWindowDragBounds.set(left, top, right, bottom);
- mTask.setDragResizing(true);
+ mTask.setDragResizing(true, DRAG_RESIZE_MODE_FREEFORM);
return false;
}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index c667767..3430ac9 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -37,8 +37,8 @@
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.content.res.Configuration.DENSITY_DPI_UNDEFINED;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.WindowManager.DOCKED_BOTTOM;
import static android.view.WindowManager.DOCKED_INVALID;
@@ -55,6 +55,11 @@
// If the stack should be resized to fullscreen.
private static final boolean FULLSCREEN = true;
+ // When we have a top-bottom split screen, we shift the bottom stack up to accommodate
+ // the IME window. The static flag below controls whether to run animation when the
+ // IME window goes away.
+ private static final boolean ANIMATE_IME_GOING_AWAY = false;
+
/** Unique identifier */
final int mStackId;
@@ -84,6 +89,9 @@
// Device rotation as of the last time {@link #mBounds} was set.
int mRotation;
+ /** Density as of last time {@link #mBounds} was set. */
+ int mDensity;
+
/** Support for non-zero {@link android.view.animation.Animation#getBackgroundColor()} */
DimLayer mAnimationBackgroundSurface;
@@ -107,6 +115,7 @@
private final Rect mLastContentBounds = new Rect();
private final Rect mTmpAdjustedBounds = new Rect();
private boolean mAdjustedForIme;
+ private boolean mImeGoingAway;
private WindowState mImeWin;
private float mMinimizeAmount;
private final int mDockedStackMinimizeThickness;
@@ -122,6 +131,10 @@
// in which case a second window animation would cause jitter.
private boolean mFreezeMovementAnimations = false;
+ // Temporary storage for the new bounds that should be used after the configuration change.
+ // Will be cleared once the client retrieves the new bounds via getBoundsForNewConfiguration().
+ private final Rect mBoundsAfterRotation = new Rect();
+
TaskStack(WindowManagerService service, int stackId) {
mService = service;
mStackId = stackId;
@@ -200,10 +213,11 @@
* @param bounds The adjusted bounds.
* @param keepInsets Whether to keep the insets from the original bounds or to calculate new
* ones depending on the adjusted bounds.
+ * @return true if the adjusted bounds has changed.
*/
- private void setAdjustedBounds(Rect bounds, boolean keepInsets) {
+ private boolean setAdjustedBounds(Rect bounds, boolean keepInsets) {
if (mAdjustedBounds.equals(bounds)) {
- return;
+ return false;
}
mAdjustedBounds.set(bounds);
@@ -211,6 +225,7 @@
alignTasksToAdjustedBounds(adjusted ? mAdjustedBounds : mBounds,
adjusted && keepInsets ? mBounds : null);
mDisplayContent.layoutNeeded = true;
+ return true;
}
private void alignTasksToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds) {
@@ -238,9 +253,11 @@
private boolean setBounds(Rect bounds) {
boolean oldFullscreen = mFullscreen;
int rotation = Surface.ROTATION_0;
+ int density = DENSITY_DPI_UNDEFINED;
if (mDisplayContent != null) {
mDisplayContent.getLogicalDisplayRect(mTmpRect);
rotation = mDisplayContent.getDisplayInfo().rotation;
+ density = mDisplayContent.getDisplayInfo().logicalDensityDpi;
mFullscreen = bounds == null;
if (mFullscreen) {
bounds = mTmpRect;
@@ -262,6 +279,7 @@
mBounds.set(bounds);
mRotation = rotation;
+ mDensity = density;
updateAdjustedBounds();
@@ -331,32 +349,34 @@
mTmpRect2.set(mBounds);
final int newRotation = mDisplayContent.getDisplayInfo().rotation;
- if (mRotation == newRotation) {
+ final int newDensity = mDisplayContent.getDisplayInfo().logicalDensityDpi;
+ if (mRotation == newRotation && mDensity == newDensity) {
setBounds(mTmpRect2);
} else {
mLastUpdateDisplayInfoRotation = newRotation;
- updateBoundsAfterRotation();
+ updateBoundsAfterConfigChange(true);
}
}
- void onConfigurationChanged() {
+ boolean onConfigurationChanged() {
mLastConfigChangedRotation = getDisplayInfo().rotation;
- updateBoundsAfterRotation();
+ return updateBoundsAfterConfigChange(false);
}
- void updateBoundsAfterRotation() {
+ boolean updateBoundsAfterConfigChange(boolean scheduleResize) {
if (mLastConfigChangedRotation != mLastUpdateDisplayInfoRotation) {
// We wait for the rotation values after configuration change and display info. update
// to be equal before updating the bounds due to rotation change otherwise things might
// get out of alignment...
- return;
+ return false;
}
final int newRotation = getDisplayInfo().rotation;
+ final int newDensity = getDisplayInfo().logicalDensityDpi;
- if (mRotation == newRotation) {
+ if (mRotation == newRotation && mDensity == newDensity) {
// Nothing to do here if the rotation didn't change
- return;
+ return false;
}
mDisplayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
@@ -365,11 +385,22 @@
snapDockedStackAfterRotation(mTmpRect2);
}
- // Post message to inform activity manager of the bounds change simulating
- // a one-way call. We do this to prevent a deadlock between window manager
- // lock and activity manager lock been held.
- mService.mH.obtainMessage(
- RESIZE_STACK, mStackId, 0 /*allowResizeInDockedMode*/, mTmpRect2).sendToTarget();
+ if (scheduleResize) {
+ // Post message to inform activity manager of the bounds change simulating
+ // a one-way call. We do this to prevent a deadlock between window manager
+ // lock and activity manager lock been held.
+ mService.mH.obtainMessage(RESIZE_STACK, mStackId,
+ 0 /*allowResizeInDockedMode*/, mTmpRect2).sendToTarget();
+ } else {
+ mBoundsAfterRotation.set(mTmpRect2);
+ }
+
+ return true;
+ }
+
+ void getBoundsForNewConfiguration(Rect outBounds) {
+ outBounds.set(mBoundsAfterRotation);
+ mBoundsAfterRotation.setEmpty();
}
/**
@@ -794,16 +825,55 @@
void setAdjustedForIme(WindowState imeWin) {
mAdjustedForIme = true;
mImeWin = imeWin;
- updateAdjustedBounds();
+ mImeGoingAway = false;
+ }
+
+ boolean isAdjustedForIme() {
+ return mAdjustedForIme || mImeGoingAway;
+ }
+ void clearImeGoingAway() {
+ mImeGoingAway = false;
+ }
+
+ boolean isAnimatingForIme() {
+ return mImeWin != null && mImeWin.isAnimatingLw();
+ }
+
+ /**
+ * Update the stack's bounds (crop or position) according to the IME window's
+ * current position. When IME window is animated, the bottom stack is animated
+ * together to track the IME window's current position, and the top stack is
+ * cropped as necessary.
+ *
+ * @return true if a traversal should be performed after the adjustment.
+ */
+ boolean updateAdjustForIme() {
+ boolean stopped = false;
+ if (mImeGoingAway && (!ANIMATE_IME_GOING_AWAY || !isAnimatingForIme())) {
+ mImeWin = null;
+ mAdjustedForIme = false;
+ stopped = true;
+ }
+ // Make sure to run a traversal when the animation stops so that the stack
+ // is moved to its final position.
+ return updateAdjustedBounds() || stopped;
}
/**
* Resets the adjustment after it got adjusted for the IME.
+ * @param adjustBoundsNow if true, reset and update the bounds immediately and forget about
+ * animations; otherwise, set flag and animates the window away together
+ * with IME window.
*/
- void resetAdjustedForIme() {
- mAdjustedForIme = false;
- mImeWin = null;
- updateAdjustedBounds();
+ void resetAdjustedForIme(boolean adjustBoundsNow) {
+ if (adjustBoundsNow) {
+ mImeWin = null;
+ mAdjustedForIme = false;
+ mImeGoingAway = false;
+ updateAdjustedBounds();
+ } else {
+ mImeGoingAway |= mAdjustedForIme;
+ }
}
/**
@@ -822,6 +892,10 @@
}
}
+ boolean isAdjustedForMinimizedDock() {
+ return mMinimizeAmount != 0f;
+ }
+
private boolean adjustForIME(final WindowState imeWin) {
final int dockedSide = getDockSide();
final boolean dockedTopOrBottom = dockedSide == DOCKED_TOP || dockedSide == DOCKED_BOTTOM;
@@ -837,6 +911,12 @@
getDisplayContent().getContentRect(displayContentRect);
contentBounds.set(displayContentRect);
int imeTop = Math.max(imeWin.getDisplayFrameLw().top, contentBounds.top);
+
+ // if IME window is animating, get its actual vertical shown position (but no smaller than
+ // the final target vertical position)
+ if (imeWin.isAnimatingLw()) {
+ imeTop = Math.max(imeTop, imeWin.getShownPositionLw().y);
+ }
imeTop += imeWin.getGivenContentInsetsLw().top;
if (contentBounds.bottom > imeTop) {
contentBounds.bottom = imeTop;
@@ -885,9 +965,11 @@
(int) (minimizeAmount * topInset + (1 - minimizeAmount) * mBounds.bottom);
} else if (dockSide == DOCKED_LEFT) {
mTmpAdjustedBounds.set(mBounds);
+ final int width = mBounds.width();
mTmpAdjustedBounds.right =
(int) (minimizeAmount * mDockedStackMinimizeThickness
+ (1 - minimizeAmount) * mBounds.right);
+ mTmpAdjustedBounds.left = mTmpAdjustedBounds.right - width;
} else if (dockSide == DOCKED_RIGHT) {
mTmpAdjustedBounds.set(mBounds);
mTmpAdjustedBounds.left =
@@ -920,7 +1002,7 @@
/**
* Updates the adjustment depending on it's current state.
*/
- void updateAdjustedBounds() {
+ boolean updateAdjustedBounds() {
boolean adjust = false;
if (mMinimizeAmount != 0f) {
adjust = adjustForMinimizedDockedStack(mMinimizeAmount);
@@ -931,7 +1013,7 @@
mTmpAdjustedBounds.setEmpty();
mLastContentBounds.setEmpty();
}
- setAdjustedBounds(mTmpAdjustedBounds, isAdjustedForMinimizedDockedStack());
+ return setAdjustedBounds(mTmpAdjustedBounds, isAdjustedForMinimizedDockedStack());
}
boolean isAdjustedForMinimizedDockedStack() {
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index f243761..eae7838 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -36,6 +36,8 @@
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
+import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
import static com.android.server.wm.WindowSurfacePlacer.SET_FORCE_HIDING_CHANGED;
import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
@@ -406,7 +408,8 @@
Animation a = mPolicy.createForceHideEnterAnimation(false,
keyguardGoingAwayToShade);
- winAnimator.setAnimation(a, mPostKeyguardExitAnimation.getStartTime());
+ winAnimator.setAnimation(a, mPostKeyguardExitAnimation.getStartTime(),
+ STACK_CLIP_BEFORE_ANIM);
winAnimator.mKeyguardGoingAwayAnimation = true;
winAnimator.mKeyguardGoingAwayWithWallpaper
= keyguardGoingAwayWithWallpaper;
@@ -445,7 +448,7 @@
}
final AppWindowToken atoken = win.mAppToken;
- if (winAnimator.mDrawState == WindowStateAnimator.READY_TO_SHOW) {
+ if (winAnimator.mDrawState == READY_TO_SHOW) {
if (atoken == null || atoken.allDrawn) {
if (winAnimator.performShowLocked()) {
setPendingLayoutChanges(displayId,
@@ -487,7 +490,7 @@
if (a != null) {
if (DEBUG_KEYGUARD) Slog.v(TAG,
"Starting keyguard exit animation on window " + winAnimator.mWin);
- winAnimator.setAnimation(a);
+ winAnimator.setAnimation(a, STACK_CLIP_BEFORE_ANIM);
winAnimator.mKeyguardGoingAwayAnimation = true;
winAnimator.mKeyguardGoingAwayWithWallpaper
= keyguardGoingAwayWithWallpaper;
diff --git a/services/core/java/com/android/server/wm/WindowLayersController.java b/services/core/java/com/android/server/wm/WindowLayersController.java
index e018a4e..6bdcd42 100644
--- a/services/core/java/com/android/server/wm/WindowLayersController.java
+++ b/services/core/java/com/android/server/wm/WindowLayersController.java
@@ -191,18 +191,20 @@
}
private void adjustSpecialWindows() {
- int layer = mHighestApplicationLayer + 1;
+ int layer = mHighestApplicationLayer + WINDOW_LAYER_MULTIPLIER;
// For pinned and docked stack window, we want to make them above other windows also when
// these windows are animating.
while (!mDockedWindows.isEmpty()) {
layer = assignAndIncreaseLayerIfNeeded(mDockedWindows.remove(), layer);
}
- // Leave some space here so the dim layer while dismissing docked/fullscreen stack has space
- // below the divider but above the app windows. It needs to be below the divider in because
- // the divider sometimes overlaps the app windows.
- layer++;
layer = assignAndIncreaseLayerIfNeeded(mDockDivider, layer);
+
+ if (mDockDivider != null && mDockDivider.isVisibleLw()
+ && mService.mInputMethodWindow != null) {
+ layer = assignAndIncreaseLayerIfNeeded(mService.mInputMethodWindow, layer);
+ }
+
// We know that we will be animating a relaunching window in the near future, which will
// receive a z-order increase. We want the replaced window to immediately receive the same
// treatment, e.g. to be above the dock divider.
@@ -216,17 +218,22 @@
}
private int assignAndIncreaseLayerIfNeeded(WindowState win, int layer) {
- if (win != null) {
+ if (win != null && layer > win.mLayer) {
assignAnimLayer(win, layer);
- layer++;
+ // Make sure we leave space inbetween normal windows for dims and such.
+ layer += WINDOW_LAYER_MULTIPLIER;
}
return layer;
}
-
+
private void assignAnimLayer(WindowState w, int layer) {
w.mLayer = layer;
w.mWinAnimator.mAnimLayer = w.mLayer + w.getAnimLayerAdjustment() +
getSpecialWindowAnimLayerAdjustment(w);
+ if (w.mAppToken != null && w.mAppToken.mAppAnimator.thumbnailForceAboveLayer > 0
+ && w.mWinAnimator.mAnimLayer > w.mAppToken.mAppAnimator.thumbnailForceAboveLayer) {
+ w.mAppToken.mAppAnimator.thumbnailForceAboveLayer = w.mWinAnimator.mAnimLayer;
+ }
}
void dump(PrintWriter pw, String s) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 20a583c..2af324d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -125,6 +125,7 @@
import com.android.internal.app.IAssistScreenshotReceiver;
import com.android.internal.os.IResultReceiver;
import com.android.internal.policy.IShortcutService;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodClient;
@@ -202,6 +203,8 @@
import static android.view.WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
import static com.android.server.wm.AppWindowAnimator.PROLONG_ANIMATION_AT_END;
import static com.android.server.wm.AppWindowAnimator.PROLONG_ANIMATION_AT_START;
+import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
+import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
@@ -236,6 +239,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_VERBOSE_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
/** {@hide} */
public class WindowManagerService extends IWindowManager.Stub
@@ -404,6 +408,11 @@
InputConsumerImpl mInputConsumer;
/**
+ * The input consumer added to the window manager before all wallpaper windows.
+ */
+ InputConsumerImpl mWallpaperInputConsumer;
+
+ /**
* Windows that are being resized. Used so we can tell the client about
* the resize after closing the transaction in which we resized the
* underlying surface.
@@ -507,6 +516,8 @@
private final SparseIntArray mTmpTaskIds = new SparseIntArray();
+ private final ArrayList<Integer> mChangedStackList = new ArrayList();
+
boolean mForceResizableTasks = false;
int getDragLayerLocked() {
@@ -798,7 +809,13 @@
= new WindowManagerInternal.AppTransitionListener() {
@Override
+ public void onAppTransitionCancelledLocked() {
+ mH.sendEmptyMessage(H.NOTIFY_APP_TRANSITION_CANCELLED);
+ }
+
+ @Override
public void onAppTransitionFinishedLocked(IBinder token) {
+ mH.sendEmptyMessage(H.NOTIFY_APP_TRANSITION_FINISHED);
AppWindowToken atoken = findAppWindowToken(token);
if (atoken == null) {
return;
@@ -1352,11 +1369,8 @@
final int fl = w.mAttrs.flags
& (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM);
final int type = w.mAttrs.type;
- // The dock divider has to sit above the application windows and so does the IME. IME also
- // needs to sit above the dock divider, so it doesn't get cut in half. We make the dock
- // divider be a target for IME, so this relationship can occur naturally.
if (fl == 0 || fl == (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM)
- || type == TYPE_APPLICATION_STARTING || type == TYPE_DOCK_DIVIDER) {
+ || type == TYPE_APPLICATION_STARTING) {
if (DEBUG_INPUT_METHOD) {
Slog.i(TAG_WM, "isVisibleOrAdding " + w + ": " + w.isVisibleOrAdding());
if (!w.isVisibleOrAdding()) {
@@ -2003,7 +2017,13 @@
final WindowStateAnimator winAnimator = win.mWinAnimator;
winAnimator.mEnterAnimationPending = true;
winAnimator.mEnteringAnimation = true;
- prepareWindowReplacementTransition(atoken);
+ // Check if we need to prepare a transition for replacing window first.
+ if (atoken != null && !prepareWindowReplacementTransition(atoken)) {
+ // If not, check if need to set up a dummy transition during display freeze
+ // so that the unfreeze wait for the apps to draw. This might be needed if
+ // the app is relaunching.
+ prepareNoneTransitionForRelaunching(atoken);
+ }
if (displayContent.isDefaultDisplay) {
if (mPolicy.getInsetHintLw(win.mAttrs, mRotation, outContentInsets, outStableInsets,
@@ -2063,10 +2083,10 @@
return res;
}
- private void prepareWindowReplacementTransition(AppWindowToken atoken) {
- if (atoken == null) {
- return;
- }
+ /**
+ * Returns true if we're done setting up any transitions.
+ */
+ private boolean prepareWindowReplacementTransition(AppWindowToken atoken) {
atoken.allDrawn = false;
WindowState replacedWindow = null;
for (int i = atoken.windows.size() - 1; i >= 0 && replacedWindow == null; i--) {
@@ -2079,7 +2099,7 @@
if (replacedWindow == null) {
// We expect to already receive a request to remove the old window. If it did not
// happen, let's just simply add a window.
- return;
+ return false;
}
// We use the visible frame, because we want the animation to morph the window from what
// was visible to the user to the final destination of the new window.
@@ -2091,6 +2111,19 @@
mAppTransition.overridePendingAppTransitionClipReveal(frame.left, frame.top,
frame.width(), frame.height());
executeAppTransition();
+ return true;
+ }
+
+ private void prepareNoneTransitionForRelaunching(AppWindowToken atoken) {
+ // Set up a none-transition and add the app to opening apps, so that the display
+ // unfreeze wait for the apps to be drawn.
+ // Note that if the display unfroze already because app unfreeze timed out,
+ // we don't set up the transition anymore and just let it go.
+ if (mDisplayFrozen && !mOpeningApps.contains(atoken) && atoken.isRelaunching()) {
+ mOpeningApps.add(atoken);
+ prepareAppTransition(AppTransition.TRANSIT_NONE, !ALWAYS_KEEP_CURRENT);
+ executeAppTransition();
+ }
}
/**
@@ -2522,7 +2555,6 @@
void repositionChild(Session session, IWindow client,
int left, int top, int right, int bottom,
- int requestedWidth, int requestedHeight,
long deferTransactionUntilFrame, Rect outFrame) {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "repositionChild");
long origId = Binder.clearCallingIdentity();
@@ -2538,7 +2570,6 @@
"repositionChild called but window is not"
+ "attached to a parent win=" + win);
}
- win.setRequestedSize(requestedWidth, requestedHeight);
win.mAttrs.x = left;
win.mAttrs.y = top;
@@ -2555,7 +2586,7 @@
try {
- win.applyGravityAndUpdateFrame();
+ win.applyGravityAndUpdateFrame(win.mContainingFrame, win.mDisplayFrame);
win.mWinAnimator.computeShownFrameLocked();
win.mWinAnimator.setSurfaceBoundariesLocked(false);
@@ -2595,8 +2626,6 @@
== PackageManager.PERMISSION_GRANTED;
long origId = Binder.clearCallingIdentity();
- final boolean preserveGeometry = (attrs != null) && (attrs.privateFlags &
- WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY) != 0;
synchronized(mWindowMap) {
WindowState win = windowForClientLocked(session, client, false);
if (win == null) {
@@ -2604,7 +2633,7 @@
}
WindowStateAnimator winAnimator = win.mWinAnimator;
- if (!preserveGeometry && viewVisibility != View.GONE) {
+ if (viewVisibility != View.GONE) {
win.setRequestedSize(requestedWidth, requestedHeight);
}
@@ -2653,9 +2682,7 @@
if ((attrChanges & WindowManager.LayoutParams.ALPHA_CHANGED) != 0) {
winAnimator.mAlpha = attrs.alpha;
}
- if (!preserveGeometry) {
- win.setWindowScale(win.mRequestedWidth, win.mRequestedHeight);
- }
+ win.setWindowScale(win.mRequestedWidth, win.mRequestedHeight);
boolean imMayMove = (flagChanges & (FLAG_ALT_FOCUSABLE_IM | FLAG_NOT_FOCUSABLE)) != 0;
final boolean isDefaultDisplay = win.isDefaultDisplay();
@@ -2908,8 +2935,9 @@
// If we're starting a drag-resize, we'll be changing the surface size as well as
// notifying the client to render to with an offset from the surface's top-left.
- if (win.isDragResizeChanged()) {
+ if (win.isDragResizeChanged() || win.mResizedWhileNotDragResizing) {
win.setDragResizing();
+ win.mResizedWhileNotDragResizing = false;
// We can only change top level windows to the full-screen surface when
// resizing (as we only have one full-screen surface). So there is no need
// to preserve and destroy windows which are attached to another, they
@@ -2920,9 +2948,9 @@
}
}
final boolean freeformResizing = win.isDragResizing()
- && win.getResizeMode() == WindowState.DRAG_RESIZE_MODE_FREEFORM;
+ && win.getResizeMode() == DRAG_RESIZE_MODE_FREEFORM;
final boolean dockedResizing = win.isDragResizing()
- && win.getResizeMode() == WindowState.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
+ && win.getResizeMode() == DRAG_RESIZE_MODE_DOCKED_DIVIDER;
result |= freeformResizing ? WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_FREEFORM : 0;
result |= dockedResizing ? WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_DOCKED : 0;
if (win.isAnimatingWithSavedSurface()) {
@@ -3031,7 +3059,7 @@
if (DEBUG_APP_TRANSITIONS) Slog.d(TAG_WM, "Loading animation for app transition."
+ " transit=" + AppTransition.appTransitionToString(transit) + " enter=" + enter
+ " frame=" + frame + " insets=" + insets + " surfaceInsets=" + surfaceInsets);
- Animation a = mAppTransition.loadAnimation(lp, transit, enter,
+ Animation a = mAppTransition.loadAnimation(lp, transit, enter, mCurConfiguration.uiMode,
mCurConfiguration.orientation, frame, displayFrame, insets, surfaceInsets,
isVoiceInteraction, freeform, atoken.mTask.mTaskId);
if (a != null) {
@@ -3039,7 +3067,7 @@
final int containingWidth = frame.width();
final int containingHeight = frame.height();
atoken.mAppAnimator.setAnimation(a, containingWidth, containingHeight,
- mAppTransition.canSkipFirstFrame());
+ mAppTransition.canSkipFirstFrame(), mAppTransition.getAppStackClipMode());
}
} else {
atoken.mAppAnimator.clearAnimation();
@@ -3390,7 +3418,8 @@
}
}
- if (isStackVisibleLocked(DOCKED_STACK_ID)
+ if ((isStackVisibleLocked(DOCKED_STACK_ID)
+ && !mStackIdToStack.get(DOCKED_STACK_ID).isAdjustedForMinimizedDock())
|| isStackVisibleLocked(FREEFORM_WORKSPACE_STACK_ID)) {
// We don't let app affect the system orientation when in freeform or docked mode since
// they don't occupy the entire display and their request can conflict with other apps.
@@ -3572,7 +3601,7 @@
}
@Override
- public void setNewConfiguration(Configuration config) {
+ public int[] setNewConfiguration(Configuration config) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"setNewConfiguration()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
@@ -3584,16 +3613,32 @@
mWaitingForConfig = false;
mLastFinishedFreezeSource = "new-config";
}
- onConfigurationChanged();
- mWindowPlacerLocked.performSurfacePlacement();
+ return onConfigurationChanged();
}
}
- private void onConfigurationChanged() {
+ @Override
+ public Rect getBoundsForNewConfiguration(int stackId) {
+ synchronized(mWindowMap) {
+ final TaskStack stack = mStackIdToStack.get(stackId);
+ final Rect outBounds = new Rect();
+ stack.getBoundsForNewConfiguration(outBounds);
+ return outBounds;
+ }
+ }
+
+ private int[] onConfigurationChanged() {
+ mPolicy.onConfigurationChanged();
+ getDefaultDisplayContentLocked().getDockedDividerController().onConfigurationChanged();
+ mChangedStackList.clear();
for (int stackNdx = mStackIdToStack.size() - 1; stackNdx >= 0; stackNdx--) {
final TaskStack stack = mStackIdToStack.valueAt(stackNdx);
- stack.onConfigurationChanged();
+ if (stack.onConfigurationChanged()) {
+ mChangedStackList.add(stack.mStackId);
+ }
}
+ return mChangedStackList.isEmpty() ?
+ null : ArrayUtils.convertToIntArray(mChangedStackList);
}
@Override
@@ -4024,8 +4069,6 @@
wAppAnimator.thumbnail.destroy();
}
wAppAnimator.thumbnail = tAppAnimator.thumbnail;
- wAppAnimator.thumbnailX = tAppAnimator.thumbnailX;
- wAppAnimator.thumbnailY = tAppAnimator.thumbnailY;
wAppAnimator.thumbnailLayer = tAppAnimator.thumbnailLayer;
wAppAnimator.thumbnailAnimation = tAppAnimator.thumbnailAnimation;
tAppAnimator.thumbnail = null;
@@ -4111,6 +4154,14 @@
for (int i = 0; i < windowsCount; i++) {
WindowState win = wtoken.allAppWindows.get(i);
if (win == wtoken.startingWindow) {
+ // Starting window that's exiting will be removed when the animation
+ // finishes. Mark all relevant flags for that finishExit will proceed
+ // all the way to actually remove it.
+ if (!visible && win.isVisibleNow() && wtoken.mAppAnimator.isAnimating()) {
+ win.mAnimatingExit = true;
+ win.mRemoveOnExit = true;
+ win.mWindowRemovalAllowed = true;
+ }
continue;
}
@@ -4260,9 +4311,13 @@
wtoken.appDied = false;
wtoken.removeAllWindows();
} else if (visible) {
- mOpeningApps.add(wtoken);
+ if (!mAppTransition.isTransitionSet() && mAppTransition.isReady()) {
+ // Add the app mOpeningApps if transition is unset but ready. This means
+ // we're doing a screen freeze, and the unfreeze will wait for all opening
+ // apps to be ready.
+ mOpeningApps.add(wtoken);
+ }
wtoken.startingMoved = false;
-
// If the token is currently hidden (should be the common case), or has been
// stopped, then we need to set up to wait for its windows to be ready.
if (wtoken.hidden || wtoken.mAppStopped) {
@@ -4306,6 +4361,7 @@
}
wtoken.inPendingTransaction = true;
if (visible) {
+ mOpeningApps.add(wtoken);
wtoken.mEnteringAnimation = true;
} else {
mClosingApps.add(wtoken);
@@ -4989,6 +5045,23 @@
}
}
+ /**
+ * Puts a specific task into docked drag resizing mode. See {@link DragResizeMode}.
+ *
+ * @param taskId The id of the task to put into drag resize mode.
+ * @param resizing Whether to put the task into drag resize mode.
+ */
+ public void setTaskDockedResizing(int taskId, boolean resizing) {
+ synchronized (mWindowMap) {
+ Task task = mTaskIdToTask.get(taskId);
+ if (task == null) {
+ throw new IllegalArgumentException("setTaskDockedResizing: taskId " + taskId
+ + " not found.");
+ }
+ task.setDragResizing(resizing, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
+ }
+ }
+
public void scrollTask(int taskId, Rect bounds) {
synchronized (mWindowMap) {
Task task = mTaskIdToTask.get(taskId);
@@ -5735,7 +5808,7 @@
if (mContext.getResources().getBoolean(
com.android.internal.R.bool.config_windowEnableCircularEmulatorDisplayOverlay)
&& SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false)
- && Build.HARDWARE.contains("goldfish")) {
+ && Build.IS_EMULATOR) {
mH.sendMessage(mH.obtainMessage(H.SHOW_EMULATOR_DISPLAY_OVERLAY));
}
}
@@ -6045,7 +6118,7 @@
int bottom = wf.bottom - cr.bottom;
frame.union(left, top, right, bottom);
ws.getVisibleBounds(stackBounds);
- if (!frame.intersect(stackBounds)) {
+ if (!Rect.intersects(frame, stackBounds)) {
// Set frame empty if there's no intersection.
frame.setEmpty();
}
@@ -7352,6 +7425,33 @@
}
}
+ private void adjustForImeIfNeeded(final DisplayContent displayContent) {
+ final WindowState imeWin = mInputMethodWindow;
+ final TaskStack focusedStack =
+ mCurrentFocus != null ? mCurrentFocus.getStack() : null;
+ final boolean dockVisible = isStackVisibleLocked(DOCKED_STACK_ID);
+ if (imeWin != null && imeWin.isVisibleLw() && imeWin.isDisplayedLw()
+ && dockVisible
+ && focusedStack != null
+ && focusedStack.getDockSide() == DOCKED_BOTTOM){
+ final ArrayList<TaskStack> stacks = displayContent.getStacks();
+ for (int i = stacks.size() - 1; i >= 0; --i) {
+ final TaskStack stack = stacks.get(i);
+ if (stack.isVisibleLocked()) {
+ stack.setAdjustedForIme(imeWin);
+ }
+ }
+ displayContent.mDividerControllerLocked.setAdjustedForIme(true, true);
+ } else {
+ final ArrayList<TaskStack> stacks = displayContent.getStacks();
+ for (int i = stacks.size() - 1; i >= 0; --i) {
+ final TaskStack stack = stacks.get(i);
+ stack.resetAdjustedForIme(!dockVisible);
+ }
+ displayContent.mDividerControllerLocked.setAdjustedForIme(false, dockVisible);
+ }
+ }
+
// -------------------------------------------------------------
// Drag and drop
// -------------------------------------------------------------
@@ -7518,6 +7618,11 @@
+ " milliseconds before attempting to detect safe mode.");
}
+ if (Settings.Global.getInt(
+ mContext.getContentResolver(), Settings.Global.SAFE_BOOT_DISALLOWED, 0) != 0) {
+ return false;
+ }
+
int menuState = mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_ANY,
KeyEvent.KEYCODE_MENU);
int sState = mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_ANY, KeyEvent.KEYCODE_S);
@@ -7670,7 +7775,9 @@
public static final int WINDOW_REPLACEMENT_TIMEOUT = 46;
public static final int NOTIFY_APP_TRANSITION_STARTING = 47;
- public static final int NOTIFY_STARTING_WINDOW_DRAWN = 48;
+ public static final int NOTIFY_APP_TRANSITION_CANCELLED = 48;
+ public static final int NOTIFY_APP_TRANSITION_FINISHED = 49;
+ public static final int NOTIFY_STARTING_WINDOW_DRAWN = 50;
/**
* Used to denote that an integer field in a message will not be used.
@@ -8209,30 +8316,8 @@
case UPDATE_DOCKED_STACK_DIVIDER: {
synchronized (mWindowMap) {
final DisplayContent displayContent = getDefaultDisplayContentLocked();
-
displayContent.getDockedDividerController().reevaluateVisibility(false);
-
- final WindowState imeWin = mInputMethodWindow;
- final TaskStack focusedStack =
- mCurrentFocus != null ? mCurrentFocus.getStack() : null;
- if (imeWin != null && imeWin.isVisibleNow()
- && isStackVisibleLocked(DOCKED_STACK_ID)
- && focusedStack != null
- && focusedStack.getDockSide() == DOCKED_BOTTOM){
- final ArrayList<TaskStack> stacks = displayContent.getStacks();
- for (int i = stacks.size() - 1; i >= 0; --i) {
- final TaskStack stack = stacks.get(i);
- if (stack.isVisibleLocked()) {
- stack.setAdjustedForIme(imeWin);
- }
- }
- } else {
- final ArrayList<TaskStack> stacks = displayContent.getStacks();
- for (int i = stacks.size() - 1; i >= 0; --i) {
- final TaskStack stack = stacks.get(i);
- stack.resetAdjustedForIme();
- }
- }
+ adjustForImeIfNeeded(displayContent);
}
}
break;
@@ -8273,6 +8358,14 @@
mAmInternal.notifyAppTransitionStarting(msg.arg1);
}
break;
+ case NOTIFY_APP_TRANSITION_CANCELLED: {
+ mAmInternal.notifyAppTransitionCancelled();
+ }
+ break;
+ case NOTIFY_APP_TRANSITION_FINISHED: {
+ mAmInternal.notifyAppTransitionFinished();
+ }
+ break;
case NOTIFY_STARTING_WINDOW_DRAWN: {
mAmInternal.notifyStartingWindowDrawn();
}
@@ -8931,7 +9024,8 @@
|| winAnimator.mSurfaceResized
|| w.mOutsetsChanged
|| configChanged
- || dragResizingChanged) {
+ || dragResizingChanged
+ || w.mResizedWhileNotDragResizing) {
if (DEBUG_RESIZE || DEBUG_ORIENTATION) {
Slog.v(TAG_WM, "Resize reasons for w=" + w + ": "
+ " contentInsetsChanged=" + w.mContentInsetsChanged
@@ -8965,13 +9059,14 @@
// the display until this window has been redrawn; to do that,
// we need to go through the process of getting informed by the
// application when it has finished drawing.
- if (w.mOrientationChanging || dragResizingChanged) {
+ if (w.mOrientationChanging || dragResizingChanged
+ || w.mResizedWhileNotDragResizing) {
if (DEBUG_SURFACE_TRACE || DEBUG_ANIM || DEBUG_ORIENTATION || DEBUG_RESIZE) {
Slog.v(TAG_WM, "Orientation or resize start waiting for draw"
+ ", mDrawState=DRAW_PENDING in " + w
+ ", surfaceController " + winAnimator.mSurfaceController);
}
- winAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING;
+ winAnimator.mDrawState = DRAW_PENDING;
if (w.mAppToken != null) {
w.mAppToken.allDrawn = false;
w.mAppToken.deferClearAllDrawn = false;
@@ -9325,6 +9420,10 @@
return;
}
+ if (DEBUG_ORIENTATION) Slog.d(TAG_WM,
+ "startFreezingDisplayLocked: inTransaction=" + inTransaction
+ + " exitAnim=" + exitAnim + " enterAnim=" + enterAnim
+ + " called by " + Debug.getCallers(8));
mScreenFrozenLock.acquire();
mDisplayFrozen = true;
@@ -9395,6 +9494,9 @@
return;
}
+ if (DEBUG_ORIENTATION) Slog.d(TAG_WM,
+ "stopFreezingDisplayLocked: Unfreezing now");
+
mDisplayFrozen = false;
mLastDisplayFreezeDuration = (int)(SystemClock.elapsedRealtime() - mDisplayFreezeTime);
StringBuilder sb = new StringBuilder(128);
@@ -9591,13 +9693,37 @@
}
}
+ private static final class HideNavInputConsumer extends InputConsumerImpl
+ implements WindowManagerPolicy.InputConsumer {
+ private final InputEventReceiver mInputEventReceiver;
+
+ HideNavInputConsumer(WindowManagerService service, Looper looper,
+ InputEventReceiver.Factory inputEventReceiverFactory) {
+ super(service, "input consumer", null);
+ mInputEventReceiver = inputEventReceiverFactory.createInputEventReceiver(
+ mClientChannel, looper);
+ }
+
+ @Override
+ public void dismiss() {
+ if (mService.removeInputConsumer()) {
+ synchronized (mService.mWindowMap) {
+ mInputEventReceiver.dispose();
+ disposeChannelsLw();
+ }
+ }
+ }
+ }
+
@Override
- public InputConsumerImpl addInputConsumer(Looper looper,
+ public WindowManagerPolicy.InputConsumer addInputConsumer(Looper looper,
InputEventReceiver.Factory inputEventReceiverFactory) {
synchronized (mWindowMap) {
- mInputConsumer = new InputConsumerImpl(this, looper, inputEventReceiverFactory);
+ HideNavInputConsumer inputConsumerImpl = new HideNavInputConsumer(
+ this, looper, inputEventReceiverFactory);
+ mInputConsumer = inputConsumerImpl;
mInputMonitor.updateInputWindowsLw(true);
- return mInputConsumer;
+ return inputConsumerImpl;
}
}
@@ -9612,6 +9738,24 @@
}
}
+ public void createWallpaperInputConsumer(InputChannel inputChannel) {
+ synchronized (mWindowMap) {
+ mWallpaperInputConsumer = new InputConsumerImpl(this, "wallpaper input", inputChannel);
+ mWallpaperInputConsumer.mWindowHandle.hasWallpaper = true;
+ mInputMonitor.updateInputWindowsLw(true);
+ }
+ }
+
+ public void removeWallpaperInputConsumer() {
+ synchronized (mWindowMap) {
+ if (mWallpaperInputConsumer != null) {
+ mWallpaperInputConsumer.disposeChannelsLw();
+ mWallpaperInputConsumer = null;
+ mInputMonitor.updateInputWindowsLw(true);
+ }
+ }
+ }
+
@Override
public boolean hasNavigationBar() {
return mPolicy.hasNavigationBar();
@@ -10517,7 +10661,10 @@
@Override
public void requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId) {
try {
- getFocusedWindow().mClient.requestAppKeyboardShortcuts(receiver, deviceId);
+ WindowState focusedWindow = getFocusedWindow();
+ if (focusedWindow != null && focusedWindow.mClient != null) {
+ getFocusedWindow().mClient.requestAppKeyboardShortcuts(receiver, deviceId);
+ }
} catch (RemoteException e) {
}
}
@@ -10689,6 +10836,19 @@
}
@Override
+ public void getMagnificationRegions(@NonNull Region outMagnified,
+ @NonNull Region outAvailable) {
+ synchronized (mWindowMap) {
+ if (mAccessibilityController != null) {
+ mAccessibilityController.getMagnificationRegionsLocked(
+ outMagnified, outAvailable);
+ } else {
+ throw new IllegalStateException("Magnification callbacks not set!");
+ }
+ }
+ }
+
+ @Override
public MagnificationSpec getCompatibleMagnificationSpecForWindow(IBinder windowToken) {
synchronized (mWindowMap) {
WindowState windowState = mWindowMap.get(windowToken);
@@ -10785,7 +10945,7 @@
final boolean isForceHiding = mPolicy.isForceHiding(win.mAttrs);
if (win.isVisibleLw()
&& (win.mAppToken != null || isForceHiding)) {
- win.mWinAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING;
+ win.mWinAnimator.mDrawState = DRAW_PENDING;
// Force add to mResizingWindows.
win.mLastContentInsets.set(-1, -1, -1, -1);
mWaitingForDrawn.add(win);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 04bd7fc..ddfc022 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -88,6 +88,8 @@
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
+import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
@@ -124,9 +126,6 @@
// to capture touch events in that area.
static final int RESIZE_HANDLE_WIDTH_IN_DP = 30;
- static final int DRAG_RESIZE_MODE_FREEFORM = 0;
- static final int DRAG_RESIZE_MODE_DOCKED_DIVIDER = 1;
-
static final boolean DEBUG_DISABLE_SAVING_SURFACES = false;
final WindowManagerService mService;
@@ -329,6 +328,8 @@
*/
final Rect mInsetFrame = new Rect();
+ private static final Rect sTmpRect = new Rect();
+
boolean mContentChanged;
// If a window showing a wallpaper: the requested offset for the
@@ -467,6 +468,13 @@
*/
boolean mResizedWhileGone = false;
+ /**
+ * Indicates whether we got resized but drag resizing flag was false. In this case, we also
+ * need to recreate the surface and defer surface bound updates in order to make sure the
+ * buffer contents and the positioning/size stay in sync.
+ */
+ boolean mResizedWhileNotDragResizing;
+
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState attachedWindow, int appOp, int seq, WindowManager.LayoutParams a,
int viewVisibility, final DisplayContent displayContent) {
@@ -630,6 +638,20 @@
return mAttrs.packageName;
}
+ /**
+ * Subtracts the insets calculated by intersecting {@param layoutFrame} with {@param insetFrame}
+ * from {@param frame}. In other words, it applies the insets that would result if
+ * {@param frame} would be shifted to {@param layoutFrame} and then applying the insets from
+ * {@param insetFrame}.
+ */
+ private void subtractInsets(Rect frame, Rect layoutFrame, Rect insetFrame) {
+ final int left = Math.max(0, insetFrame.left - layoutFrame.left);
+ final int top = Math.max(0, insetFrame.top - layoutFrame.top);
+ final int right = Math.max(0, layoutFrame.right - insetFrame.right);
+ final int bottom = Math.max(0, layoutFrame.bottom - insetFrame.bottom);
+ frame.inset(left, top, right, bottom);
+ }
+
@Override
public void computeFrameLw(Rect pf, Rect df, Rect of, Rect cf, Rect vf, Rect dcf, Rect sf,
Rect osf) {
@@ -643,7 +665,7 @@
mHaveFrame = true;
final Task task = getTask();
- final boolean fullscreenTask = !inMultiWindowMode();
+ final boolean fullscreenTask = !isInMultiWindowMode();
final boolean windowsAreFloating = task != null && task.isFloating();
// If the task has temp inset bounds set, we have to make sure all its windows uses
@@ -655,11 +677,25 @@
task.getTempInsetBounds(mInsetFrame);
}
+ // Denotes the actual frame used to calculate the insets and to perform the layout. When
+ // resizing in docked mode, we'd like to freeze the layout, so we also need to freeze the
+ // insets temporarily. By the notion of a task having a different layout frame, we can
+ // achieve that while still moving the task around.
+ final Rect layoutContainingFrame;
+ final Rect layoutDisplayFrame;
+
+ // The offset from the layout containing frame to the actual containing frame.
+ final int layoutXDiff;
+ final int layoutYDiff;
if (mInsetFrame.isEmpty() && (fullscreenTask
|| layoutInParentFrame())) {
// We use the parent frame as the containing frame for fullscreen and child windows
mContainingFrame.set(pf);
mDisplayFrame.set(df);
+ layoutDisplayFrame = df;
+ layoutContainingFrame = pf;
+ layoutXDiff = 0;
+ layoutYDiff = 0;
} else {
task.getBounds(mContainingFrame);
if (mAppToken != null && !mAppToken.mFrozenBounds.isEmpty()) {
@@ -671,10 +707,18 @@
mContainingFrame.bottom = mContainingFrame.top + frozen.height();
}
final WindowState imeWin = mService.mInputMethodWindow;
- if (imeWin != null && imeWin.isVisibleNow() && mService.mInputMethodTarget == this
- && mContainingFrame.bottom > cf.bottom) {
- // IME is up and obscuring this window. Adjust the window position so it is visible.
- mContainingFrame.top -= mContainingFrame.bottom - cf.bottom;
+ // IME is up and obscuring this window. Adjust the window position so it is visible.
+ if (imeWin != null && imeWin.isVisibleNow() && mService.mInputMethodTarget == this) {
+ if (windowsAreFloating && mContainingFrame.bottom > cf.bottom) {
+ // In freeform we want to move the top up directly.
+ // TODO: Investigate why this is cf not pf.
+ mContainingFrame.top -= mContainingFrame.bottom - cf.bottom;
+ } else if (mContainingFrame.bottom > pf.bottom) {
+ // But in docked we want to behave like fullscreen
+ // and behave as if the task were given smaller bounds
+ // for the purposes of layout.
+ mContainingFrame.bottom = pf.bottom;
+ }
}
if (windowsAreFloating) {
@@ -686,6 +730,14 @@
}
}
mDisplayFrame.set(mContainingFrame);
+ layoutXDiff = !mInsetFrame.isEmpty() ? mInsetFrame.left - mContainingFrame.left : 0;
+ layoutYDiff = !mInsetFrame.isEmpty() ? mInsetFrame.top - mContainingFrame.top : 0;
+ layoutContainingFrame = !mInsetFrame.isEmpty() ? mInsetFrame : mContainingFrame;
+ subtractInsets(mDisplayFrame, layoutContainingFrame, df);
+ subtractInsets(mContainingFrame, layoutContainingFrame, pf);
+ subtractInsets(mInsetFrame, layoutContainingFrame, pf);
+ layoutDisplayFrame = df;
+ layoutDisplayFrame.intersect(layoutContainingFrame);
}
final int pw = mContainingFrame.width();
@@ -716,7 +768,11 @@
final int fw = mFrame.width();
final int fh = mFrame.height();
- applyGravityAndUpdateFrame();
+ applyGravityAndUpdateFrame(layoutContainingFrame, layoutDisplayFrame);
+
+ // Offset the actual frame by the amount layout frame is off.
+ mFrame.offset(-layoutXDiff, -layoutYDiff);
+ mCompatFrame.offset(-layoutXDiff, -layoutYDiff);
// Calculate the outsets before the content frame gets shrinked to the window frame.
if (hasOutsets) {
@@ -728,12 +784,6 @@
mOutsets.set(0, 0, 0, 0);
}
- // Denotes the actual frame used to calculate the insets. When resizing in docked mode,
- // we'd like to freeze the layout, so we also need to freeze the insets temporarily. By the
- // notion of a task having a different inset frame, we can achieve that while still moving
- // the task around.
- final Rect frame = !mInsetFrame.isEmpty() ? mInsetFrame : mFrame;
-
// Make sure the content and visible frames are inside of the
// final window frame.
if (windowsAreFloating && !mFrame.isEmpty()) {
@@ -756,45 +806,35 @@
mVisibleFrame.set(mContentFrame);
mStableFrame.set(mContentFrame);
} else if (mAttrs.type == TYPE_DOCK_DIVIDER) {
- if (isVisibleLw() || mWinAnimator.isAnimating()) {
- // We don't adjust the dock divider frame for reasons other than performance. The
- // real reason is that if it gets adjusted before it is shown for the first time,
- // it would get size (0, 0). This causes a problem when we finally show the dock
- // divider and try to draw to it. We do set the surface size at that moment to
- // the correct size, but it's too late for the Surface Flinger to make it
- // available for view rendering and as a result the renderer receives size 1, 1.
- // This way we just keep the divider at the original size and Surface Flinger
- // will return the correct value to the renderer.
- mDisplayContent.getDockedDividerController().positionDockedStackedDivider(mFrame);
- mContentFrame.set(mFrame);
- if (!mFrame.equals(mLastFrame)) {
- mMovedByResize = true;
- }
+ mDisplayContent.getDockedDividerController().positionDockedStackedDivider(mFrame);
+ mContentFrame.set(mFrame);
+ if (!mFrame.equals(mLastFrame)) {
+ mMovedByResize = true;
}
} else {
- mContentFrame.set(Math.max(mContentFrame.left, frame.left),
- Math.max(mContentFrame.top, frame.top),
- Math.min(mContentFrame.right, frame.right),
- Math.min(mContentFrame.bottom, frame.bottom));
+ mContentFrame.set(Math.max(mContentFrame.left, layoutContainingFrame.left),
+ Math.max(mContentFrame.top, layoutContainingFrame.top),
+ Math.min(mContentFrame.right, layoutContainingFrame.right),
+ Math.min(mContentFrame.bottom, layoutContainingFrame.bottom));
- mVisibleFrame.set(Math.max(mVisibleFrame.left, frame.left),
- Math.max(mVisibleFrame.top, frame.top),
- Math.min(mVisibleFrame.right, frame.right),
- Math.min(mVisibleFrame.bottom, frame.bottom));
+ mVisibleFrame.set(Math.max(mVisibleFrame.left, layoutContainingFrame.left),
+ Math.max(mVisibleFrame.top, layoutContainingFrame.top),
+ Math.min(mVisibleFrame.right, layoutContainingFrame.right),
+ Math.min(mVisibleFrame.bottom, layoutContainingFrame.bottom));
- mStableFrame.set(Math.max(mStableFrame.left, frame.left),
- Math.max(mStableFrame.top, frame.top),
- Math.min(mStableFrame.right, frame.right),
- Math.min(mStableFrame.bottom, frame.bottom));
+ mStableFrame.set(Math.max(mStableFrame.left, layoutContainingFrame.left),
+ Math.max(mStableFrame.top, layoutContainingFrame.top),
+ Math.min(mStableFrame.right, layoutContainingFrame.right),
+ Math.min(mStableFrame.bottom, layoutContainingFrame.bottom));
}
if (fullscreenTask && !windowsAreFloating) {
// Windows that are not fullscreen can be positioned outside of the display frame,
// but that is not a reason to provide them with overscan insets.
- mOverscanInsets.set(Math.max(mOverscanFrame.left - frame.left, 0),
- Math.max(mOverscanFrame.top - frame.top, 0),
- Math.max(frame.right - mOverscanFrame.right, 0),
- Math.max(frame.bottom - mOverscanFrame.bottom, 0));
+ mOverscanInsets.set(Math.max(mOverscanFrame.left - layoutContainingFrame.left, 0),
+ Math.max(mOverscanFrame.top - layoutContainingFrame.top, 0),
+ Math.max(layoutContainingFrame.right - mOverscanFrame.right, 0),
+ Math.max(layoutContainingFrame.bottom - mOverscanFrame.bottom, 0));
}
if (mAttrs.type == TYPE_DOCK_DIVIDER) {
@@ -810,39 +850,37 @@
mContentInsets.setEmpty();
mVisibleInsets.setEmpty();
} else {
- mContentInsets.set(mContentFrame.left - frame.left,
- mContentFrame.top - frame.top,
- frame.right - mContentFrame.right,
- frame.bottom - mContentFrame.bottom);
+ getDisplayContent().getLogicalDisplayRect(mTmpRect);
+ // Override right and/or bottom insets in case if the frame doesn't fit the screen in
+ // non-fullscreen mode.
+ boolean overrideRightInset = !fullscreenTask && mFrame.right > mTmpRect.right;
+ boolean overrideBottomInset = !fullscreenTask && mFrame.bottom > mTmpRect.bottom;
+ mContentInsets.set(mContentFrame.left - layoutContainingFrame.left,
+ mContentFrame.top - layoutContainingFrame.top,
+ overrideRightInset ? mTmpRect.right - mContentFrame.right
+ : layoutContainingFrame.right - mContentFrame.right,
+ overrideBottomInset ? mTmpRect.bottom - mContentFrame.bottom
+ : layoutContainingFrame.bottom - mContentFrame.bottom);
- mVisibleInsets.set(mVisibleFrame.left - frame.left,
- mVisibleFrame.top - frame.top,
- frame.right - mVisibleFrame.right,
- frame.bottom - mVisibleFrame.bottom);
+ mVisibleInsets.set(mVisibleFrame.left - layoutContainingFrame.left,
+ mVisibleFrame.top - layoutContainingFrame.top,
+ overrideRightInset ? mTmpRect.right - mVisibleFrame.right
+ : layoutContainingFrame.right - mVisibleFrame.right,
+ overrideBottomInset ? mTmpRect.bottom - mVisibleFrame.bottom
+ : layoutContainingFrame.bottom - mVisibleFrame.bottom);
- mStableInsets.set(Math.max(mStableFrame.left - frame.left, 0),
- Math.max(mStableFrame.top - frame.top, 0),
- Math.max(frame.right - mStableFrame.right, 0),
- Math.max(frame.bottom - mStableFrame.bottom, 0));
+ mStableInsets.set(Math.max(mStableFrame.left - layoutContainingFrame.left, 0),
+ Math.max(mStableFrame.top - layoutContainingFrame.top, 0),
+ overrideRightInset ? Math.max(mTmpRect.right - mStableFrame.right, 0)
+ : Math.max(layoutContainingFrame.right - mStableFrame.right, 0),
+ overrideBottomInset ? Math.max(mTmpRect.bottom - mStableFrame.bottom, 0)
+ : Math.max(layoutContainingFrame.bottom - mStableFrame.bottom, 0));
}
- if (!mInsetFrame.isEmpty()) {
- mContentFrame.set(mFrame);
- mContentFrame.top += mContentInsets.top;
- mContentFrame.bottom += mContentInsets.bottom;
- mContentFrame.left += mContentInsets.left;
- mContentFrame.right += mContentInsets.right;
- mVisibleFrame.set(mFrame);
- mVisibleFrame.top += mVisibleInsets.top;
- mVisibleFrame.bottom += mVisibleInsets.bottom;
- mVisibleFrame.left += mVisibleInsets.left;
- mVisibleFrame.right += mVisibleInsets.right;
- mStableFrame.set(mFrame);
- mStableFrame.top += mStableInsets.top;
- mStableFrame.bottom += mStableInsets.bottom;
- mStableFrame.left += mStableInsets.left;
- mStableFrame.right += mStableInsets.right;
- }
+ mContentFrame.offset(-layoutXDiff, -layoutYDiff);
+ mVisibleFrame.offset(-layoutXDiff, -layoutYDiff);
+ mStableFrame.offset(-layoutXDiff, -layoutYDiff);
+
mCompatFrame.set(mFrame);
if (mEnforceSizeCompat) {
// If there is a size compatibility scale being applied to the
@@ -875,7 +913,7 @@
+ "): frame=" + mFrame.toShortString()
+ " ci=" + mContentInsets.toShortString()
+ " vi=" + mVisibleInsets.toShortString()
- + " vi=" + mStableInsets.toShortString()
+ + " si=" + mStableInsets.toShortString()
+ " of=" + mOutsets.toShortString());
}
@@ -1309,7 +1347,7 @@
*/
boolean hasMoved() {
return mHasSurface && (mContentChanged || mMovedByResize)
- && !mAnimatingExit && !mWinAnimator.mLastHidden && mService.okToDisplay()
+ && !mAnimatingExit && mService.okToDisplay()
&& (mFrame.top != mLastFrame.top || mFrame.left != mLastFrame.left)
&& (mAttachedWindow == null || !mAttachedWindow.hasMoved());
}
@@ -1883,6 +1921,11 @@
}
private boolean shouldSaveSurface() {
+ if (mWinAnimator.mSurfaceController == null) {
+ // Don't bother if the surface controller is gone for any reason.
+ return false;
+ }
+
if ((mAttrs.flags & FLAG_SECURE) != 0) {
// We don't save secure surfaces since their content shouldn't be shown while the app
// isn't on screen and content might leak through during the transition animation with
@@ -1956,10 +1999,18 @@
return;
}
mSurfaceSaved = false;
- setHasSurface(true);
- mWinAnimator.mDrawState = WindowStateAnimator.READY_TO_SHOW;
- if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
- Slog.v(TAG, "Restoring saved surface: " + this);
+ if (mWinAnimator.mSurfaceController != null) {
+ setHasSurface(true);
+ mWinAnimator.mDrawState = WindowStateAnimator.READY_TO_SHOW;
+
+ if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
+ Slog.v(TAG, "Restoring saved surface: " + this);
+ }
+ } else {
+ // mSurfaceController shouldn't be null if mSurfaceSaved was still true at
+ // this point. Even if we destroyed the saved surface because of rotation
+ // or resize, mSurfaceSaved flag should have been cleared. So this is a wtf.
+ Slog.wtf(TAG, "Failed to restore saved surface: surface gone! " + this);
}
}
@@ -2231,7 +2282,7 @@
}
@Override
- public boolean inMultiWindowMode() {
+ public boolean isInMultiWindowMode() {
final Task task = getTask();
return task != null && !task.isFullscreen();
}
@@ -2258,7 +2309,7 @@
return mResizeMode;
}
- private boolean computeDragResizing() {
+ boolean computeDragResizing() {
final Task task = getTask();
if (task == null) {
return false;
@@ -2288,9 +2339,14 @@
return;
}
mDragResizing = resizing;
- mResizeMode = mDragResizing && mDisplayContent.mDividerControllerLocked.isResizing()
- ? DRAG_RESIZE_MODE_DOCKED_DIVIDER
- : DRAG_RESIZE_MODE_FREEFORM;
+ final Task task = getTask();
+ if (task != null && task.isDragResizing()) {
+ mResizeMode = task.getDragResizeMode();
+ } else {
+ mResizeMode = mDragResizing && mDisplayContent.mDividerControllerLocked.isResizing()
+ ? DRAG_RESIZE_MODE_DOCKED_DIVIDER
+ : DRAG_RESIZE_MODE_FREEFORM;
+ }
}
boolean isDragResizing() {
@@ -2523,12 +2579,12 @@
}
}
- void applyGravityAndUpdateFrame() {
- final int pw = mContainingFrame.width();
- final int ph = mContainingFrame.height();
+ void applyGravityAndUpdateFrame(Rect containingFrame, Rect displayFrame) {
+ final int pw = containingFrame.width();
+ final int ph = containingFrame.height();
final Task task = getTask();
- final boolean nonFullscreenTask = inMultiWindowMode();
- final boolean fitToDisplay = task != null && !task.isFloating();
+ final boolean nonFullscreenTask = isInMultiWindowMode();
+ final boolean fitToDisplay = task != null && !task.isFloating() && !layoutInParentFrame();
float x, y;
int w,h;
@@ -2572,7 +2628,7 @@
y = mAttrs.y;
}
- if (nonFullscreenTask) {
+ if (nonFullscreenTask && !layoutInParentFrame()) {
// Make sure window fits in containing frame since it is in a non-fullscreen task as
// required by {@link Gravity#apply} call.
w = Math.min(w, pw);
@@ -2580,13 +2636,13 @@
}
// Set mFrame
- Gravity.apply(mAttrs.gravity, w, h, mContainingFrame,
+ Gravity.apply(mAttrs.gravity, w, h, containingFrame,
(int) (x + mAttrs.horizontalMargin * pw),
(int) (y + mAttrs.verticalMargin * ph), mFrame);
// Now make sure the window fits in the overall display frame.
if (fitToDisplay) {
- Gravity.applyDisplay(mAttrs.gravity, mDisplayFrame, mFrame);
+ Gravity.applyDisplay(mAttrs.gravity, displayFrame, mFrame);
}
// We need to make sure we update the CompatFrame as it is used for
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 22d3f98..34452ee 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -20,6 +20,8 @@
import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
+import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
@@ -38,8 +40,6 @@
import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
import static com.android.server.wm.WindowManagerService.localLOGV;
import static com.android.server.wm.WindowManagerService.logWithStack;
-import static com.android.server.wm.WindowState.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
-import static com.android.server.wm.WindowState.DRAG_RESIZE_MODE_FREEFORM;
import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
import static com.android.server.wm.WindowSurfacePlacer.SET_TURN_ON_SCREEN;
@@ -75,6 +75,25 @@
static final String TAG = TAG_WITH_CLASS_NAME ? "WindowStateAnimator" : TAG_WM;
static final int WINDOW_FREEZE_LAYER = TYPE_LAYER_MULTIPLIER * 200;
+ /**
+ * Mode how the window gets clipped by the stack bounds during an animation: The clipping should
+ * be applied after applying the animation transformation, i.e. the stack bounds don't move
+ * during the animation.
+ */
+ static final int STACK_CLIP_AFTER_ANIM = 0;
+
+ /**
+ * Mode how the window gets clipped by the stack bounds: The clipping should be applied before
+ * applying the animation transformation, i.e. the stack bounds move with the window.
+ */
+ static final int STACK_CLIP_BEFORE_ANIM = 1;
+
+ /**
+ * Mode how window gets clipped by the stack bounds during an animation: Don't clip the window
+ * by the stack bounds.
+ */
+ static final int STACK_CLIP_NONE = 2;
+
// Unchanging local convenience fields.
final WindowManagerService mService;
final WindowState mWin;
@@ -100,6 +119,7 @@
int mLastLayer;
long mAnimationStartTime;
long mLastAnimationTime;
+ int mStackClip = STACK_CLIP_BEFORE_ANIM;
/**
* Set when we have changed the size of the surface, to know that
@@ -128,7 +148,9 @@
boolean mHasClipRect;
Rect mClipRect = new Rect();
Rect mTmpClipRect = new Rect();
+ Rect mTmpFinalClipRect = new Rect();
Rect mLastClipRect = new Rect();
+ Rect mLastFinalClipRect = new Rect();
Rect mTmpStackBounds = new Rect();
/**
@@ -226,7 +248,7 @@
mWallpaperControllerLocked = mService.mWallpaperControllerLocked;
}
- public void setAnimation(Animation anim, long startTime) {
+ public void setAnimation(Animation anim, long startTime, int stackClip) {
if (localLOGV) Slog.v(TAG, "Setting animation in " + this + ": " + anim);
mAnimating = false;
mLocalAnimating = false;
@@ -238,10 +260,15 @@
mTransformation.setAlpha(mLastHidden ? 0 : 1);
mHasLocalTransformation = true;
mAnimationStartTime = startTime;
+ mStackClip = stackClip;
+ }
+
+ public void setAnimation(Animation anim, int stackClip) {
+ setAnimation(anim, -1, stackClip);
}
public void setAnimation(Animation anim) {
- setAnimation(anim, -1);
+ setAnimation(anim, -1, STACK_CLIP_AFTER_ANIM);
}
public void clearAnimation() {
@@ -252,6 +279,7 @@
mAnimation = null;
mKeyguardGoingAwayAnimation = false;
mKeyguardGoingAwayWithWallpaper = false;
+ mStackClip = STACK_CLIP_BEFORE_ANIM;
}
}
@@ -397,6 +425,7 @@
if (DEBUG_LAYERS) Slog.v(TAG, "Stepping win " + this + " anim layer: " + mAnimLayer);
mHasTransformation = false;
mHasLocalTransformation = false;
+ mStackClip = STACK_CLIP_BEFORE_ANIM;
mWin.checkPolicyVisibilityChange();
mTransformation.clear();
if (mDrawState == HAS_DRAWN
@@ -938,13 +967,15 @@
if (appTransformation != null) {
tmpMatrix.postConcat(appTransformation.getMatrix());
}
+
+ // The translation that applies the position of the window needs to be applied at the
+ // end in case that other translations include scaling. Otherwise the scaling will
+ // affect this translation. But it needs to be set before the screen rotation animation
+ // so the pivot point is at the center of the screen for all windows.
+ tmpMatrix.postTranslate(frame.left + mWin.mXOffset, frame.top + mWin.mYOffset);
if (screenAnimation) {
tmpMatrix.postConcat(screenRotationAnimation.getEnterTransformation().getMatrix());
}
- // The translation that applies the position of the window needs to be applied at the
- // end in case that other translations include scaling. Otherwise the scaling will
- // affect this translation.
- tmpMatrix.postTranslate(frame.left + mWin.mXOffset, frame.top + mWin.mYOffset);
//TODO (multidisplay): Magnification is supported only for the default display.
if (mService.mAccessibilityController != null && displayId == DEFAULT_DISPLAY) {
@@ -1130,11 +1161,13 @@
}
}
- Rect calculateSurfaceWindowCrop() {
+ void calculateSurfaceWindowCrop(Rect clipRect, Rect finalClipRect) {
final WindowState w = mWin;
final DisplayContent displayContent = w.getDisplayContent();
if (displayContent == null) {
- return null;
+ clipRect.setEmpty();
+ finalClipRect.setEmpty();
+ return;
}
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
if (DEBUG_WINDOW_CROP) Slog.d(TAG, "Updating crop for window: " + w + ", " + "mLastCrop=" +
@@ -1170,7 +1203,6 @@
final boolean fullscreen = w.isFrameFullscreen(displayInfo);
final boolean isFreeformResizing =
w.isDragResizing() && w.getResizeMode() == DRAG_RESIZE_MODE_FREEFORM;
- final Rect clipRect = mTmpClipRect;
// We use the clip rect as provided by the tranformation for non-fullscreen windows to
// avoid premature clipping with the system decor rect.
@@ -1201,7 +1233,8 @@
// so we need to translate to match the actual surface coordinates.
clipRect.offset(attrs.surfaceInsets.left, attrs.surfaceInsets.top);
- adjustCropToStackBounds(w, clipRect, isFreeformResizing);
+ finalClipRect.setEmpty();
+ adjustCropToStackBounds(w, clipRect, finalClipRect, isFreeformResizing);
if (DEBUG_WINDOW_CROP) Slog.d(TAG, "Clip rect after stack adjustment=" + clipRect);
w.transformFromScreenToSurfaceSpace(clipRect);
@@ -1210,35 +1243,39 @@
if (w.hasJustMovedInStack() && mLastClipRect.isEmpty() && !clipRect.isEmpty()) {
clipRect.setEmpty();
}
-
- return clipRect;
}
- void updateSurfaceWindowCrop(Rect clipRect, boolean recoveringMemory) {
+ void updateSurfaceWindowCrop(Rect clipRect, Rect finalClipRect, boolean recoveringMemory) {
if (!clipRect.equals(mLastClipRect)) {
mLastClipRect.set(clipRect);
mSurfaceController.setCropInTransaction(clipRect, recoveringMemory);
}
+ if (!finalClipRect.equals(mLastFinalClipRect)) {
+ mLastFinalClipRect.set(finalClipRect);
+ mSurfaceController.setFinalCropInTransaction(finalClipRect);
+ }
}
- private void adjustCropToStackBounds(WindowState w, Rect clipRect, boolean isFreeformResizing) {
+ private int resolveStackClip() {
+
+ // App animation overrides window animation stack clip mode.
+ if (mAppAnimator != null && mAppAnimator.animation != null) {
+ return mAppAnimator.getStackClip();
+ } else {
+ return mStackClip;
+ }
+ }
+ private void adjustCropToStackBounds(WindowState w, Rect clipRect, Rect finalClipRect,
+ boolean isFreeformResizing) {
final Task task = w.getTask();
if (task == null || !task.cropWindowsToStackBounds()) {
return;
}
- // We don't apply the stack bounds crop if:
- // 1. The window is currently animating in freeform mode, otherwise the animating window
- // will be suddenly (docked) or for whole animation (freeform) cut off.
- // 2. The window that is being replaced during animation, because it was living in a
- // different stack. If we suddenly crop it to the new stack bounds, it might get cut off.
- // We don't want it to happen, so we let it ignore the stack bounds until it gets removed.
- // The window that will replace it will abide them.
- // TODO: identify animations where we don't want to apply docked stack crop to the docked
- // task. For example, if the app is going from freeform to docked mode, we may not
- // want to apply the crop during the animation, since it will make the app appear
- // cropped prematurely.
- if (isAnimating() && (w.mWillReplaceWindow || w.inFreeformWorkspace())) {
+ final int stackClip = resolveStackClip();
+
+ // It's animating and we don't want to clip it to stack bounds during animation - abort.
+ if (isAnimating() && stackClip == STACK_CLIP_NONE) {
return;
}
@@ -1257,32 +1294,45 @@
final int frameY = isFreeformResizing ? (int) mSurfaceController.getY() :
w.mFrame.top + mWin.mYOffset - w.getAttrs().surfaceInsets.top;
+ // If we are animating, we either apply the clip before applying all the animation
+ // transformation or after all the transformation.
+ final boolean useFinalClipRect = isAnimating() && stackClip == STACK_CLIP_AFTER_ANIM;
+
// We need to do some acrobatics with surface position, because their clip region is
// relative to the inside of the surface, but the stack bounds aren't.
- clipRect.left = Math.max(0,
- Math.max(mTmpStackBounds.left, frameX + clipRect.left) - frameX);
- clipRect.top = Math.max(0,
- Math.max(mTmpStackBounds.top, frameY + clipRect.top) - frameY);
- clipRect.right = Math.max(0,
- Math.min(mTmpStackBounds.right, frameX + clipRect.right) - frameX);
- clipRect.bottom = Math.max(0,
- Math.min(mTmpStackBounds.bottom, frameY + clipRect.bottom) - frameY);
+ if (useFinalClipRect) {
+ finalClipRect.set(mTmpStackBounds);
+ } else {
+ clipRect.left = Math.max(0,
+ Math.max(mTmpStackBounds.left, frameX + clipRect.left) - frameX);
+ clipRect.top = Math.max(0,
+ Math.max(mTmpStackBounds.top, frameY + clipRect.top) - frameY);
+ clipRect.right = Math.max(0,
+ Math.min(mTmpStackBounds.right, frameX + clipRect.right) - frameX);
+ clipRect.bottom = Math.max(0,
+ Math.min(mTmpStackBounds.bottom, frameY + clipRect.bottom) - frameY);
+ }
}
void setSurfaceBoundariesLocked(final boolean recoveringMemory) {
final WindowState w = mWin;
final Task task = w.getTask();
+ // We got resized, so block all updates until we got the new surface.
+ if (w.mResizedWhileNotDragResizing) {
+ return;
+ }
+
mTmpSize.set(w.mShownPosition.x, w.mShownPosition.y, 0, 0);
calculateSurfaceBounds(w, w.getAttrs());
float extraHScale = (float) 1.0;
float extraVScale = (float) 1.0;
- final Rect crop = calculateSurfaceWindowCrop();
+ calculateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect);
if (task != null && task.mStack.getForceScaleToCrop()) {
- extraHScale = crop.width() / (float)mTmpSize.width();
- extraVScale = crop.height() / (float)mTmpSize.height();
+ extraHScale = mTmpClipRect.width() / (float)mTmpSize.width();
+ extraVScale = mTmpClipRect.height() / (float)mTmpSize.height();
// In the case of ForceScaleToCrop we scale entire tasks together,
// and so we need to scale our offsets relative to the task bounds
@@ -1296,12 +1346,13 @@
// Since we are scaled to fit in our previously desired crop, we can now
// expose the whole window in buffer space, and not risk extending
// past where the system would have cropped us
- crop.set(0, 0, mTmpSize.width(), mTmpSize.height());
- updateSurfaceWindowCrop(crop, recoveringMemory);
+ mTmpClipRect.set(0, 0, mTmpSize.width(), mTmpSize.height());
+ mTmpFinalClipRect.setEmpty();
+ updateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect, recoveringMemory);
} else {
mSurfaceController.setPositionInTransaction(mTmpSize.left, mTmpSize.top,
recoveringMemory);
- updateSurfaceWindowCrop(crop, recoveringMemory);
+ updateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect, recoveringMemory);
}
@@ -1458,7 +1509,8 @@
SurfaceControl.openTransaction();
mSurfaceController.setPositionInTransaction(mWin.mFrame.left + left,
mWin.mFrame.top + top, false);
- updateSurfaceWindowCrop(calculateSurfaceWindowCrop(), false);
+ calculateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect);
+ updateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect, false);
} catch (RuntimeException e) {
Slog.w(TAG, "Error positioning surface of " + mWin
+ " pos=(" + left + "," + top + ")", e);
@@ -1721,6 +1773,7 @@
pw.print(" mLocalAnimating="); pw.print(mLocalAnimating);
pw.print(" mAnimationIsEntrance="); pw.print(mAnimationIsEntrance);
pw.print(" mAnimation="); pw.println(mAnimation);
+ pw.print(" mStackClip="); pw.println(mStackClip);
}
if (mHasTransformation || mHasLocalTransformation) {
pw.print(prefix); pw.print("XForm: has=");
@@ -1740,6 +1793,9 @@
if (mHasClipRect) {
pw.print(" mLastClipRect="); mLastClipRect.printShortString(pw);
}
+ if (!mLastFinalClipRect.isEmpty()) {
+ pw.print(" mLastFinalClipRect="); mLastFinalClipRect.printShortString(pw);
+ }
pw.println();
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index be3ad3b..8799c61 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -189,6 +189,16 @@
}
}
+ void setFinalCropInTransaction(Rect clipRect) {
+ if (SHOW_TRANSACTIONS) logSurface(
+ "FINAL CROP " + clipRect.toShortString(), null);
+ try {
+ mSurfaceControl.setFinalCrop(clipRect);
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Error disconnecting surface in: " + this, e);
+ }
+ }
+
void setLayer(int layer) {
if (mSurfaceControl != null) {
SurfaceControl.openTransaction();
@@ -460,6 +470,7 @@
private final PointF mPosition = new PointF();
private final Point mSize = new Point();
private final Rect mWindowCrop = new Rect();
+ private final Rect mFinalCrop = new Rect();
private boolean mShown = false;
private int mLayerStack;
private boolean mIsOpaque;
@@ -545,6 +556,19 @@
}
@Override
+ public void setFinalCrop(Rect crop) {
+ if (crop != null) {
+ if (!crop.equals(mFinalCrop)) {
+ if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setFinalCrop("
+ + crop.toShortString() + "): OLD:" + this + ". Called by "
+ + Debug.getCallers(3));
+ mFinalCrop.set(crop);
+ }
+ }
+ super.setFinalCrop(crop);
+ }
+
+ @Override
public void setLayerStack(int layerStack) {
if (layerStack != mLayerStack) {
if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setLayerStack(" + layerStack + "): OLD:"
@@ -655,6 +679,7 @@
pw.print(" mSize="); pw.print(s.mSize.x); pw.print("x");
pw.println(s.mSize.y);
pw.print(" mCrop="); s.mWindowCrop.printShortString(pw); pw.println();
+ pw.print(" mFinalCrop="); s.mFinalCrop.printShortString(pw); pw.println();
pw.print(" Transform: ("); pw.print(s.mDsdx); pw.print(", ");
pw.print(s.mDtdx); pw.print(", "); pw.print(s.mDsdy);
pw.print(", "); pw.print(s.mDtdy); pw.println(")");
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 5ad771f..04aa735 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -9,6 +9,7 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
@@ -18,6 +19,7 @@
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.AppWindowAnimator.PROLONG_ANIMATION_AT_START;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
@@ -30,13 +32,21 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerService.H.*;
-import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD;
-import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.H.DO_TRAVERSAL;
+import static com.android.server.wm.WindowManagerService.H.NOTIFY_ACTIVITY_DRAWN;
+import static com.android.server.wm.WindowManagerService.H.NOTIFY_APP_TRANSITION_STARTING;
+import static com.android.server.wm.WindowManagerService.H.NOTIFY_STARTING_WINDOW_DRAWN;
+import static com.android.server.wm.WindowManagerService.H.REPORT_LOSING_FOCUS;
+import static com.android.server.wm.WindowManagerService.H.REPORT_WINDOWS_CHANGE;
+import static com.android.server.wm.WindowManagerService.H.SEND_NEW_CONFIGURATION;
+import static com.android.server.wm.WindowManagerService.H.UPDATE_DOCKED_STACK_DIVIDER;
+import static com.android.server.wm.WindowManagerService.H.WINDOW_FREEZE_TIMEOUT;
+import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD;
+import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_NONE;
@@ -683,11 +693,13 @@
// currently animating... let's do something.
final int left = w.mFrame.left;
final int top = w.mFrame.top;
- final boolean adjustedForMinimizedDockedStack = w.getTask() != null &&
- w.getTask().mStack.isAdjustedForMinimizedDockedStack();
+ final boolean adjustedForMinimizedDockOrIme = task != null
+ && (task.mStack.isAdjustedForMinimizedDockedStack()
+ || task.mStack.isAdjustedForIme());
if ((w.mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0
- && !w.isDragResizing() && !adjustedForMinimizedDockedStack
- && (task == null || !w.getTask().mStack.getFreezeMovementAnimations())) {
+ && !w.isDragResizing() && !adjustedForMinimizedDockOrIme
+ && (task == null || !w.getTask().mStack.getFreezeMovementAnimations())
+ && !w.mWinAnimator.mLastHidden) {
winAnimator.setMoveAnimation(left, top);
}
@@ -701,11 +713,11 @@
w.mClient.moved(left, top);
} catch (RemoteException e) {
}
+ w.mMovedByResize = false;
}
//Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing");
w.mContentChanged = false;
- w.mMovedByResize = false;
// Moved from updateWindowsAndWallpaperLocked().
if (w.mHasSurface) {
@@ -848,6 +860,10 @@
mService.mInputConsumer.layout(dw, dh);
}
+ if (mService.mWallpaperInputConsumer != null) {
+ mService.mWallpaperInputConsumer.layout(dw, dh);
+ }
+
final int N = windows.size();
int i;
@@ -1218,6 +1234,10 @@
if (mService.mAppTransition.isNextAppTransitionThumbnailUp()) {
createThumbnailAppAnimator(transit, wtoken, topOpeningLayer, topClosingLayer);
}
+ if (mService.mAppTransition.getAppTransition()
+ == AppTransition.TRANSIT_DOCK_TASK_FROM_RECENTS) {
+ appAnimator.startProlongAnimation(PROLONG_ANIMATION_AT_START);
+ }
}
return topOpeningApp;
}
@@ -1556,12 +1576,14 @@
WindowState win = appToken.findMainWindow();
Rect appRect = win != null ? win.getContentFrameLw() :
new Rect(0, 0, displayInfo.appWidth, displayInfo.appHeight);
+ Rect insets = win != null ? win.mContentInsets : null;
// For the new aspect-scaled transition, we want it to always show
// above the animating opening/closing window, and we want to
// synchronize its thumbnail surface with the surface for the
// open/close animation (only on the way down)
anim = mService.mAppTransition.createThumbnailAspectScaleAnimationLocked(appRect,
- thumbnailHeader, taskId);
+ insets, thumbnailHeader, taskId, mService.mCurConfiguration.uiMode,
+ mService.mCurConfiguration.orientation);
openingAppAnimator.thumbnailForceAboveLayer = Math.max(openingLayer, closingLayer);
openingAppAnimator.deferThumbnailDestruction =
!mService.mAppTransition.isNextThumbnailTransitionScaleUp();
@@ -1576,8 +1598,6 @@
openingAppAnimator.thumbnailLayer = openingLayer;
openingAppAnimator.thumbnailAnimation = anim;
mService.mAppTransition.getNextAppTransitionStartRect(taskId, mTmpStartRect);
- openingAppAnimator.thumbnailX = mTmpStartRect.left;
- openingAppAnimator.thumbnailY = mTmpStartRect.top;
} catch (Surface.OutOfResourcesException e) {
Slog.e(TAG, "Can't allocate thumbnail/Canvas surface w="
+ dirty.width() + " h=" + dirty.height(), e);
diff --git a/services/core/java/com/android/server/wm/animation/CurvedTranslateAnimation.java b/services/core/java/com/android/server/wm/animation/CurvedTranslateAnimation.java
new file mode 100644
index 0000000..33ac2ff
--- /dev/null
+++ b/services/core/java/com/android/server/wm/animation/CurvedTranslateAnimation.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm.animation;
+
+import android.animation.KeyframeSet;
+import android.animation.PathKeyframes;
+import android.graphics.Path;
+import android.graphics.PointF;
+import android.view.animation.Animation;
+import android.view.animation.Transformation;
+
+/**
+ * Translate animation which follows a curved path.
+ */
+public class CurvedTranslateAnimation extends Animation {
+
+ private final PathKeyframes mKeyframes;
+
+ public CurvedTranslateAnimation(Path path) {
+ mKeyframes = KeyframeSet.ofPath(path);
+ }
+
+ @Override
+ protected void applyTransformation(float interpolatedTime, Transformation t) {
+ PointF location = (PointF) mKeyframes.getValue(interpolatedTime);
+ t.getMatrix().setTranslate(location.x, location.y);
+ }
+}
diff --git a/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp b/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp
index ec5e8c9..14d50ce 100644
--- a/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp
+++ b/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp
@@ -36,7 +36,8 @@
enum {
TEMPERATURE_CURRENT = 0,
TEMPERATURE_THROTTLING = 1,
- TEMPERATURE_SHUTDOWN = 2
+ TEMPERATURE_SHUTDOWN = 2,
+ TEMPERATURE_THROTTLING_BELOW_VR_MIN = 3
};
static struct {
@@ -127,6 +128,13 @@
values[length++] = list[i].shutdown_threshold;
}
break;
+ case TEMPERATURE_THROTTLING_BELOW_VR_MIN:
+ if (list[i].vr_throttling_threshold == UNKNOWN_TEMPERATURE) {
+ values[length++] = gUndefinedTemperature;
+ } else {
+ values[length++] = list[i].vr_throttling_threshold;
+ }
+ break;
}
}
}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index a5237ca..cd485c5 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -1312,6 +1312,12 @@
}
}
+static void nativeToggleCapsLock(JNIEnv* env, jclass /* clazz */,
+ jlong ptr, jint deviceId) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+ im->getInputManager()->getReader()->toggleCapsLockState(deviceId);
+}
+
static void nativeSetInputWindows(JNIEnv* env, jclass /* clazz */,
jlong ptr, jobjectArray windowHandleObjArray) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
@@ -1508,6 +1514,8 @@
(void*) nativeSetInputFilterEnabled },
{ "nativeInjectInputEvent", "(JLandroid/view/InputEvent;IIIIII)I",
(void*) nativeInjectInputEvent },
+ { "nativeToggleCapsLock", "(JI)V",
+ (void*) nativeToggleCapsLock },
{ "nativeSetInputWindows", "(J[Lcom/android/server/input/InputWindowHandle;)V",
(void*) nativeSetInputWindows },
{ "nativeSetFocusedApplication", "(JLcom/android/server/input/InputApplicationHandle;)V",
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 7da0a9a..42b5705 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -136,6 +136,7 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.JournaledFile;
+import com.android.internal.util.ParcelableString;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.internal.widget.LockPatternUtils;
@@ -184,12 +185,16 @@
private static final String DEVICE_POLICIES_XML = "device_policies.xml";
+ private static final String TAG_ACCEPTED_CA_CERTIFICATES = "accepted-ca-certificate";
+
private static final String TAG_LOCK_TASK_COMPONENTS = "lock-task-component";
private static final String TAG_STATUS_BAR = "statusbar";
private static final String ATTR_DISABLED = "disabled";
+ private static final String ATTR_NAME = "name";
+
private static final String DO_NOT_ASK_CREDENTIALS_ON_BOOT_XML =
"do-not-ask-credentials-on-boot";
@@ -420,6 +425,8 @@
final ArrayList<ActiveAdmin> mAdminList = new ArrayList<>();
final ArrayList<ComponentName> mRemovingAdmins = new ArrayList<>();
+ final ArraySet<String> mAcceptedCaCertificates = new ArraySet<>();
+
// This is the list of component allowed to start lock task mode.
List<String> mLockTaskPackages = new ArrayList<>();
@@ -483,7 +490,8 @@
}
if (Intent.ACTION_BOOT_COMPLETED.equals(action)
|| KeyChain.ACTION_STORAGE_CHANGED.equals(action)) {
- new MonitoringCertNotificationTask().execute(intent);
+ int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_ALL);
+ new MonitoringCertNotificationTask().execute(userId);
}
if (Intent.ACTION_USER_ADDED.equals(action)) {
disableSecurityLoggingIfNotCompliant();
@@ -654,8 +662,8 @@
String shortSupportMessage = null;
String longSupportMessage = null;
- // Background color of confirm credentials screen. Default: gray.
- static final int DEF_ORGANIZATION_COLOR = Color.GRAY;
+ // Background color of confirm credentials screen. Default: teal.
+ static final int DEF_ORGANIZATION_COLOR = Color.parseColor("#00796B");
int organizationColor = DEF_ORGANIZATION_COLOR;
// Default title of confirm credentials screen
@@ -1123,9 +1131,7 @@
}
String tagDAM = parser.getName();
if (TAG_TRUST_AGENT_COMPONENT_OPTIONS.equals(tagDAM)) {
- PersistableBundle bundle = new PersistableBundle();
- bundle.restoreFromXml(parser);
- result.options = bundle;
+ result.options = PersistableBundle.restoreFromXml(parser);
} else {
Slog.w(LOG_TAG, "Unknown tag under " + tag + ": " + tagDAM);
}
@@ -1480,6 +1486,12 @@
return "/data/system/";
}
+ void registerContentObserver(Uri uri, boolean notifyForDescendents,
+ ContentObserver observer, int userHandle) {
+ mContext.getContentResolver().registerContentObserver(uri, notifyForDescendents,
+ observer, userHandle);
+ }
+
int settingsSecureGetIntForUser(String name, int def, int userHandle) {
return Settings.Secure.getIntForUser(mContext.getContentResolver(),
name, def, userHandle);
@@ -2215,6 +2227,12 @@
out.endTag(null, "active-password");
}
+ for (int i = 0; i < policy.mAcceptedCaCertificates.size(); i++) {
+ out.startTag(null, TAG_ACCEPTED_CA_CERTIFICATES);
+ out.attribute(null, ATTR_NAME, policy.mAcceptedCaCertificates.valueAt(i));
+ out.endTag(null, TAG_ACCEPTED_CA_CERTIFICATES);
+ }
+
for (int i=0; i<policy.mLockTaskPackages.size(); i++) {
String component = policy.mLockTaskPackages.get(i);
out.startTag(null, TAG_LOCK_TASK_COMPONENTS);
@@ -2381,6 +2399,8 @@
parser.getAttributeValue(null, "symbols"));
policy.mActivePasswordNonLetter = Integer.parseInt(
parser.getAttributeValue(null, "nonletter"));
+ } else if (TAG_ACCEPTED_CA_CERTIFICATES.equals(tag)) {
+ policy.mAcceptedCaCertificates.add(parser.getAttributeValue(null, ATTR_NAME));
} else if (TAG_LOCK_TASK_COMPONENTS.equals(tag)) {
policy.mLockTaskPackages.add(parser.getAttributeValue(null, "name"));
} else if (TAG_STATUS_BAR.equals(tag)) {
@@ -2536,7 +2556,7 @@
onStartUser(UserHandle.USER_SYSTEM);
// Register an observer for watching for user setup complete.
- new SetupContentObserver(mHandler).register(mContext.getContentResolver());
+ new SetupContentObserver(mHandler).register();
// Initialize the user setup state, to handle the upgrade case.
updateUserSetupComplete();
@@ -2632,17 +2652,17 @@
}
}
- private class MonitoringCertNotificationTask extends AsyncTask<Intent, Void, Void> {
+ private class MonitoringCertNotificationTask extends AsyncTask<Integer, Void, Void> {
@Override
- protected Void doInBackground(Intent... params) {
- int userHandle = params[0].getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_ALL);
+ protected Void doInBackground(Integer... params) {
+ int userHandle = params[0];
if (userHandle == UserHandle.USER_ALL) {
for (UserInfo userInfo : mUserManager.getUsers()) {
manageNotification(userInfo.getUserHandle());
}
} else {
- manageNotification(new UserHandle(userHandle));
+ manageNotification(UserHandle.of(userHandle));
}
return null;
}
@@ -2652,25 +2672,27 @@
return;
}
- // Call out to KeyChain to check for user-added CAs
- boolean hasCert = false;
+ // Call out to KeyChain to check for CAs which are waiting for approval.
+ final List<String> pendingCertificates;
try {
- KeyChainConnection kcs = KeyChain.bindAsUser(mContext, userHandle);
- try {
- if (!kcs.getService().getUserCaAliases().getList().isEmpty()) {
- hasCert = true;
- }
- } catch (RemoteException e) {
- Log.e(LOG_TAG, "Could not connect to KeyChain service", e);
- } finally {
- kcs.close();
- }
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- } catch (RuntimeException | AssertionError e) {
- Log.e(LOG_TAG, "Could not connect to KeyChain service", e);
+ pendingCertificates = getInstalledCaCertificates(userHandle);
+ } catch (RemoteException | RuntimeException e) {
+ Log.e(LOG_TAG, "Could not retrieve certificates from KeyChain service", e);
+ return;
}
- if (!hasCert) {
+
+ synchronized (DevicePolicyManagerService.this) {
+ final DevicePolicyData policy = getUserData(userHandle.getIdentifier());
+
+ // Remove deleted certificates. Flush xml if necessary.
+ if (policy.mAcceptedCaCertificates.retainAll(pendingCertificates)) {
+ saveSettingsLocked(userHandle.getIdentifier());
+ }
+ // Trim to approved certificates.
+ pendingCertificates.removeAll(policy.mAcceptedCaCertificates);
+ }
+
+ if (pendingCertificates.isEmpty()) {
mInjector.getNotificationManager().cancelAsUser(
null, MONITORING_CERT_NOTIFICATION_ID, userHandle);
return;
@@ -2701,7 +2723,8 @@
final Context userContext;
try {
- userContext = mContext.createPackageContextAsUser("android", 0, userHandle);
+ final String packageName = mContext.getPackageName();
+ userContext = mContext.createPackageContextAsUser(packageName, 0, userHandle);
} catch (PackageManager.NameNotFoundException e) {
Log.e(LOG_TAG, "Create context as " + userHandle + " failed", e);
return;
@@ -2720,6 +2743,29 @@
mInjector.getNotificationManager().notifyAsUser(
null, MONITORING_CERT_NOTIFICATION_ID, noti, userHandle);
}
+
+ private List<String> getInstalledCaCertificates(UserHandle userHandle)
+ throws RemoteException, RuntimeException {
+ KeyChainConnection conn = null;
+ try {
+ conn = KeyChain.bindAsUser(mContext, userHandle);
+ List<ParcelableString> aliases = conn.getService().getUserCaAliases().getList();
+ List<String> result = new ArrayList<>(aliases.size());
+ for (int i = 0; i < aliases.size(); i++) {
+ result.add(aliases.get(i).string);
+ }
+ return result;
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ return null;
+ } catch (AssertionError e) {
+ throw new RuntimeException(e);
+ } finally {
+ if (conn != null) {
+ conn.close();
+ }
+ }
+ }
}
/**
@@ -2757,6 +2803,10 @@
&& getActiveAdminUncheckedLocked(adminReceiver, userHandle) != null) {
throw new IllegalArgumentException("Admin is already added");
}
+ if (policy.mRemovingAdmins.contains(adminReceiver)) {
+ throw new IllegalArgumentException(
+ "Trying to set an admin which is being removed");
+ }
ActiveAdmin newAdmin = new ActiveAdmin(info, /* parent */ false);
policy.mAdminMap.put(adminReceiver, newAdmin);
int replaceIndex = -1;
@@ -2958,7 +3008,7 @@
ArrayList<ActiveAdmin> admins = new ArrayList<ActiveAdmin>();
for (UserInfo userInfo : mUserManager.getProfiles(userHandle)) {
DevicePolicyData policy = getUserData(userInfo.id);
- if (!isManagedProfile(userInfo.id)) {
+ if (!userInfo.isManagedProfile()) {
admins.addAll(policy.mAdminList);
} else {
// For managed profiles, we always include the policies set on the parent
@@ -3692,32 +3742,26 @@
final int callingUid = mInjector.binderGetCallingUid();
final int userHandle = mInjector.userHandleGetCallingUserId();
- if (getCredentialOwner(userHandle, /* parent */ false) != userHandle) {
- throw new SecurityException("You can not change password for this profile because"
- + " it shares the password with the owner profile");
- }
-
String password = passwordOrNull != null ? passwordOrNull : "";
+ // Password resetting to empty/null is not allowed for managed profiles.
+ if (TextUtils.isEmpty(password)) {
+ enforceNotManagedProfile(userHandle, "clear the active password");
+ }
+
int quality;
synchronized (this) {
- // If caller has PO (or DO), it can clear the password, so see if that's the case
- // first.
+ // If caller has PO (or DO) it can change the password, so see if that's the case first.
ActiveAdmin admin = getActiveAdminWithPolicyForUidLocked(
null, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, callingUid);
if (admin == null) {
// Otherwise, make sure the caller has any active admin with the right policy.
admin = getActiveAdminForCallerLocked(null,
DeviceAdminInfo.USES_POLICY_RESET_PASSWORD);
- }
- final ComponentName adminComponent = admin.info.getComponent();
-
- // As of N, only profile owners and device owners can reset the password.
- if (!(isProfileOwner(adminComponent, userHandle)
- || isDeviceOwner(adminComponent, userHandle))) {
final boolean preN = getTargetSdk(admin.info.getPackageName(), userHandle)
<= android.os.Build.VERSION_CODES.M;
+
// As of N, password resetting to empty/null is not allowed anymore.
// TODO Should we allow DO/PO to set an empty password?
if (TextUtils.isEmpty(password)) {
@@ -3846,6 +3890,9 @@
// back in to the service.
final long ident = mInjector.binderClearCallingIdentity();
try {
+ if (isManagedProfile(userHandle)) {
+ mLockPatternUtils.setSeparateProfileChallengeEnabled(userHandle, true);
+ }
if (!TextUtils.isEmpty(password)) {
mLockPatternUtils.saveLockPassword(password, null, quality, userHandle);
} else {
@@ -4070,6 +4117,29 @@
}
@Override
+ public boolean approveCaCert(String alias, int userId, boolean approval) {
+ enforceManageUsers();
+ synchronized (this) {
+ Set<String> certs = getUserData(userId).mAcceptedCaCertificates;
+ boolean changed = (approval ? certs.add(alias) : certs.remove(alias));
+ if (!changed) {
+ return false;
+ }
+ saveSettingsLocked(userId);
+ }
+ new MonitoringCertNotificationTask().execute(userId);
+ return true;
+ }
+
+ @Override
+ public boolean isCaCertApproved(String alias, int userId) {
+ enforceManageUsers();
+ synchronized (this) {
+ return getUserData(userId).mAcceptedCaCertificates.contains(alias);
+ }
+ }
+
+ @Override
public boolean installCaCert(ComponentName admin, byte[] certBuffer) throws RemoteException {
enforceCanManageCaCerts(admin);
@@ -4138,8 +4208,8 @@
}
@Override
- public boolean installKeyPair(ComponentName who, byte[] privKey, byte[] cert, String alias,
- boolean requestAccess) {
+ public boolean installKeyPair(ComponentName who, byte[] privKey, byte[] cert, byte[] chain,
+ String alias, boolean requestAccess) {
enforceCanManageInstalledKeys(who);
final int callingUid = mInjector.binderGetCallingUid();
@@ -4149,7 +4219,7 @@
KeyChain.bindAsUser(mContext, UserHandle.getUserHandleForUid(callingUid));
try {
IKeyChainService keyChain = keyChainConnection.getService();
- if (!keyChain.installKeyPair(privKey, cert, alias)) {
+ if (!keyChain.installKeyPair(privKey, cert, chain, alias)) {
return false;
}
if (requestAccess) {
@@ -4285,6 +4355,13 @@
}
}
+ /**
+ * @return {@code true} if the package is installed and set as always-on, {@code false} if it is
+ * not installed and therefore not available.
+ *
+ * @throws SecurityException if the caller is not a profile or device owner.
+ * @throws UnsupportedException if the package does not support being set as always-on.
+ */
@Override
public boolean setAlwaysOnVpnPackage(ComponentName admin, String vpnPackage)
throws SecurityException {
@@ -4294,13 +4371,19 @@
final int userId = mInjector.userHandleGetCallingUserId();
final long token = mInjector.binderClearCallingIdentity();
- try{
+ try {
+ if (vpnPackage != null && !isPackageInstalledForUser(vpnPackage, userId)) {
+ return false;
+ }
ConnectivityManager connectivityManager = (ConnectivityManager)
mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
- return connectivityManager.setAlwaysOnVpnPackageForUser(userId, vpnPackage);
+ if (!connectivityManager.setAlwaysOnVpnPackageForUser(userId, vpnPackage)) {
+ throw new UnsupportedOperationException();
+ }
} finally {
mInjector.binderRestoreCallingIdentity(token);
}
+ return true;
}
@Override
@@ -4868,10 +4951,24 @@
// we start using it for different purposes.
ensureCallerPackage(callerPackage);
+ final ApplicationInfo ai;
+ try {
+ ai = mIPackageManager.getApplicationInfo(callerPackage, 0, userHandle);
+ } catch (RemoteException e) {
+ throw new SecurityException(e);
+ }
+
+ boolean legacyApp = false;
+ if (ai.targetSdkVersion <= Build.VERSION_CODES.M) {
+ legacyApp = true;
+ } else if ("com.google.android.apps.enterprise.dmagent".equals(ai.packageName)
+ && ai.versionCode == 697) {
+ // TODO: STOPSHIP remove this (revert ag/895987) once a new prebuilt is dropped
+ legacyApp = true;
+ }
+
final int rawStatus = getEncryptionStatus();
- if ((rawStatus == DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER)
- && (callerPackage != null)
- && (getTargetSdk(callerPackage, userHandle) <= VERSION_CODES.M)) {
+ if ((rawStatus == DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER) && legacyApp) {
return DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE;
}
return rawStatus;
@@ -5682,26 +5779,25 @@
}
@Override
- public boolean setDeviceOwnerLockScreenInfo(ComponentName who, String info) {
+ public void setDeviceOwnerLockScreenInfo(ComponentName who, CharSequence info) {
Preconditions.checkNotNull(who, "ComponentName is null");
if (!mHasFeature) {
- return false;
+ return;
}
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
long token = mInjector.binderClearCallingIdentity();
try {
- mLockPatternUtils.setDeviceOwnerInfo(info);
+ mLockPatternUtils.setDeviceOwnerInfo(info != null ? info.toString() : null);
} finally {
mInjector.binderRestoreCallingIdentity(token);
}
- return true;
}
}
@Override
- public String getDeviceOwnerLockScreenInfo() {
+ public CharSequence getDeviceOwnerLockScreenInfo() {
return mLockPatternUtils.getDeviceOwnerInfo();
}
@@ -5783,8 +5879,7 @@
transitionCheckNeeded = false;
} else {
// For all other cases, caller must have MANAGE_PROFILE_AND_DEVICE_OWNERS.
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, null);
+ enforceCanManageProfileAndDeviceOwners();
}
final DevicePolicyData policyData = getUserData(userHandle);
@@ -5977,8 +6072,7 @@
}
return;
}
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, null);
+ enforceCanManageProfileAndDeviceOwners();
if (hasUserSetupCompleted(userHandle) && !isCallerWithSystemUid()) {
throw new IllegalStateException("Cannot set the profile owner on a user which is "
+ "already set-up");
@@ -5993,8 +6087,7 @@
int callingUid = mInjector.binderGetCallingUid();
boolean isAdb = callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID;
if (!isAdb) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, null);
+ enforceCanManageProfileAndDeviceOwners();
}
final int code = checkSetDeviceOwnerPreCondition(userId, isAdb);
@@ -6313,17 +6406,16 @@
@Override
public void setTrustAgentConfiguration(ComponentName admin, ComponentName agent,
- PersistableBundle args) {
+ PersistableBundle args, boolean parent) {
if (!mHasFeature) {
return;
}
Preconditions.checkNotNull(admin, "admin is null");
Preconditions.checkNotNull(agent, "agent is null");
final int userHandle = UserHandle.getCallingUserId();
- enforceNotManagedProfile(userHandle, "set trust agent configuration");
synchronized (this) {
ActiveAdmin ap = getActiveAdminForCallerLocked(admin,
- DeviceAdminInfo.USES_POLICY_DISABLE_KEYGUARD_FEATURES);
+ DeviceAdminInfo.USES_POLICY_DISABLE_KEYGUARD_FEATURES, parent);
ap.trustAgentInfos.put(agent.flattenToString(), new TrustAgentInfo(args));
saveSettingsLocked(userHandle);
}
@@ -6331,7 +6423,7 @@
@Override
public List<PersistableBundle> getTrustAgentConfiguration(ComponentName admin,
- ComponentName agent, int userHandle) {
+ ComponentName agent, int userHandle, boolean parent) {
if (!mHasFeature) {
return null;
}
@@ -6341,46 +6433,44 @@
synchronized (this) {
final String componentName = agent.flattenToString();
if (admin != null) {
- final ActiveAdmin ap = getActiveAdminUncheckedLocked(admin, userHandle);
+ final ActiveAdmin ap = getActiveAdminUncheckedLocked(admin, userHandle, parent);
if (ap == null) return null;
TrustAgentInfo trustAgentInfo = ap.trustAgentInfos.get(componentName);
if (trustAgentInfo == null || trustAgentInfo.options == null) return null;
- List<PersistableBundle> result = new ArrayList<PersistableBundle>();
+ List<PersistableBundle> result = new ArrayList<>();
result.add(trustAgentInfo.options);
return result;
}
// Return strictest policy for this user and profiles that are visible from this user.
- final List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
List<PersistableBundle> result = null;
-
// Search through all admins that use KEYGUARD_DISABLE_TRUST_AGENTS and keep track
// of the options. If any admin doesn't have options, discard options for the rest
// and return null.
+ List<ActiveAdmin> admins =
+ getActiveAdminsForLockscreenPoliciesLocked(userHandle, parent);
boolean allAdminsHaveOptions = true;
- for (UserInfo userInfo : profiles) {
- DevicePolicyData policy = getUserDataUnchecked(userInfo.id);
- final int N = policy.mAdminList.size();
- for (int i=0; i < N; i++) {
- final ActiveAdmin active = policy.mAdminList.get(i);
- final boolean disablesTrust = (active.disabledKeyguardFeatures
- & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0;
- final TrustAgentInfo info = active.trustAgentInfos.get(componentName);
- if (info != null && info.options != null && !info.options.isEmpty()) {
- if (disablesTrust) {
- if (result == null) {
- result = new ArrayList<PersistableBundle>();
- }
- result.add(info.options);
- } else {
- Log.w(LOG_TAG, "Ignoring admin " + active.info
- + " because it has trust options but doesn't declare "
- + "KEYGUARD_DISABLE_TRUST_AGENTS");
+ final int N = admins.size();
+ for (int i = 0; i < N; i++) {
+ final ActiveAdmin active = admins.get(i);
+
+ final boolean disablesTrust = (active.disabledKeyguardFeatures
+ & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0;
+ final TrustAgentInfo info = active.trustAgentInfos.get(componentName);
+ if (info != null && info.options != null && !info.options.isEmpty()) {
+ if (disablesTrust) {
+ if (result == null) {
+ result = new ArrayList<>();
}
- } else if (disablesTrust) {
- allAdminsHaveOptions = false;
- break;
+ result.add(info.options);
+ } else {
+ Log.w(LOG_TAG, "Ignoring admin " + active.info
+ + " because it has trust options but doesn't declare "
+ + "KEYGUARD_DISABLE_TRUST_AGENTS");
}
+ } else if (disablesTrust) {
+ allAdminsHaveOptions = false;
+ break;
}
}
return allAdminsHaveOptions ? result : null;
@@ -6650,6 +6740,9 @@
}
synchronized (this) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
+ if (admin == null) {
+ return false;
+ }
if (admin.permittedAccessiblityServices == null) {
return true;
}
@@ -6820,6 +6913,9 @@
}
synchronized (this) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
+ if (admin == null) {
+ return false;
+ }
if (admin.permittedInputMethods == null) {
return true;
}
@@ -7002,7 +7098,7 @@
}
@Override
- public boolean getPackageSuspended(ComponentName who, String packageName) {
+ public boolean isPackageSuspended(ComponentName who, String packageName) {
Preconditions.checkNotNull(who, "ComponentName is null");
int callingUserId = UserHandle.getCallingUserId();
synchronized (this) {
@@ -7090,19 +7186,30 @@
}
@Override
- public Bundle getUserRestrictions(ComponentName who, int userHandle) {
+ public Bundle getUserRestrictions(ComponentName who) {
+ if (!mHasFeature) {
+ return null;
+ }
+ Preconditions.checkNotNull(who, "ComponentName is null");
+ synchronized (this) {
+ final ActiveAdmin activeAdmin = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ return activeAdmin.userRestrictions;
+ }
+ }
+
+ @Override
+ public Bundle getUserRestrictionsForUser(ComponentName who, int userHandle) {
+ if (!mHasFeature) {
+ return null;
+ }
Preconditions.checkNotNull(who, "ComponentName is null");
enforceFullCrossUsersPermission(userHandle);
+ enforceCanManageProfileAndDeviceOwners();
synchronized (this) {
ActiveAdmin activeAdmin = getActiveAdminUncheckedLocked(who, userHandle);
if (activeAdmin == null) {
- throw new SecurityException("No active admin: " + activeAdmin);
- }
- if (activeAdmin.getUid() != mInjector.binderGetCallingUid()) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS,
- "Calling uid " + mInjector.binderGetCallingUid() + " neither owns the admin"
- + " " + who + " nor has MANAGE_PROFILE_AND_DEVICE_OWNERS permission");
+ return null;
}
return activeAdmin.userRestrictions;
}
@@ -7820,9 +7927,9 @@
super(handler);
}
- void register(ContentResolver resolver) {
- resolver.registerContentObserver(mUserSetupComplete, false, this, UserHandle.USER_ALL);
- resolver.registerContentObserver(mDeviceProvisioned, false, this, UserHandle.USER_ALL);
+ void register() {
+ mInjector.registerContentObserver(mUserSetupComplete, false, this, UserHandle.USER_ALL);
+ mInjector.registerContentObserver(mDeviceProvisioned, false, this, UserHandle.USER_ALL);
}
@Override
@@ -8676,6 +8783,11 @@
null);
}
+ private void enforceCanManageProfileAndDeviceOwners() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, null);
+ }
+
@Override
public boolean isUninstallInQueue(final String packageName) {
enforceCanManageDeviceAdmin();
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 5975405..e7ae2b0 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -153,6 +153,12 @@
"com.android.server.MountService$Lifecycle";
private static final String SEARCH_MANAGER_SERVICE_CLASS =
"com.android.server.search.SearchManagerService$Lifecycle";
+ private static final String THERMAL_OBSERVER_CLASS =
+ "com.google.android.clockwork.ThermalObserver";
+ private static final String WEAR_BLUETOOTH_SERVICE_CLASS =
+ "com.google.android.clockwork.bluetooth.WearBluetoothService";
+ private static final String CONTENT_SERVICE_CLASS =
+ "com.android.server.content.ContentService$Lifecycle";
private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
@@ -165,7 +171,7 @@
* visual content.
*/
private static final int DEFAULT_SYSTEM_THEME =
- com.android.internal.R.style.Theme_Material_DayNight_DarkActionBar;
+ com.android.internal.R.style.Theme_Material_Light_DarkActionBar;
private final int mFactoryTestMode;
private Timer mProfilerSnapshotTimer;
@@ -570,8 +576,7 @@
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
traceBeginAndSlog("StartContentService");
- contentService = ContentService.main(context,
- mFactoryTestMode == FactoryTest.FACTORY_TEST_LOW_LEVEL);
+ mSystemServiceManager.startService(CONTENT_SERVICE_CLASS);
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
traceBeginAndSlog("InstallSystemProviders");
@@ -695,6 +700,14 @@
// as appropriate.
mSystemServiceManager.startService(UiModeManagerService.class);
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "UpdatePackagesIfNeeded");
+ try {
+ mPackageManagerService.updatePackagesIfNeeded();
+ } catch (Throwable e) {
+ reportWtf("update packages", e);
+ }
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "PerformFstrimIfNeeded");
try {
mPackageManagerService.performFstrimIfNeeded();
@@ -703,14 +716,6 @@
}
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
- Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "ExtractPackagesIfNeeded");
- try {
- mPackageManagerService.extractPackagesIfNeeded();
- } catch (Throwable e) {
- reportWtf("extract packages", e);
- }
- Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
-
try {
ActivityManagerNative.getDefault().showBootMessage(
context.getResources().getText(
@@ -957,9 +962,8 @@
if (!disableNonCoreServices) {
mSystemServiceManager.startService(DockObserver.class);
- if (context.getPackageManager().hasSystemFeature
- (PackageManager.FEATURE_WATCH)) {
- mSystemServiceManager.startService(ThermalObserver.class);
+ if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+ mSystemServiceManager.startService(THERMAL_OBSERVER_CLASS);
}
}
@@ -1023,7 +1027,8 @@
mSystemServiceManager.startService(BACKUP_MANAGER_SERVICE_CLASS);
}
- if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)) {
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)
+ || context.getResources().getBoolean(R.bool.config_enableAppWidgetService)) {
mSystemServiceManager.startService(APPWIDGET_SERVICE_CLASS);
}
@@ -1129,7 +1134,9 @@
mSystemServiceManager.startService(TvInputManagerService.class);
}
- mSystemServiceManager.startService(MediaResourceMonitorService.class);
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)) {
+ mSystemServiceManager.startService(MediaResourceMonitorService.class);
+ }
if (!disableNonCoreServices) {
traceBeginAndSlog("StartMediaRouterService");
@@ -1167,6 +1174,10 @@
mSystemServiceManager.startService(MediaProjectionManagerService.class);
}
+ if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+ mSystemServiceManager.startService(WEAR_BLUETOOTH_SERVICE_CLASS);
+ }
+
// Before things start rolling, be sure we have decided whether
// we are in safe mode.
final boolean safeMode = wm.detectSafeMode();
@@ -1314,7 +1325,7 @@
reportWtf("starting System UI", e);
}
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
- Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "MakeMountServiceReady");
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "MakeNetworkScoreReady");
try {
if (networkScoreF != null) networkScoreF.systemReady();
} catch (Throwable e) {
@@ -1413,6 +1424,12 @@
} catch (Throwable e) {
reportWtf("Notifying MmsService running", e);
}
+
+ try {
+ if (networkScoreF != null) networkScoreF.systemRunning();
+ } catch (Throwable e) {
+ reportWtf("Notifying NetworkScoreService running", e);
+ }
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
});
diff --git a/services/net/java/android/net/apf/ApfCapabilities.java b/services/net/java/android/net/apf/ApfCapabilities.java
new file mode 100644
index 0000000..0ec50c4
--- /dev/null
+++ b/services/net/java/android/net/apf/ApfCapabilities.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.apf;
+
+/**
+ * APF program support capabilities.
+ *
+ * @hide
+ */
+public class ApfCapabilities {
+ /**
+ * Version of APF instruction set supported for packet filtering. 0 indicates no support for
+ * packet filtering using APF programs.
+ */
+ public final int apfVersionSupported;
+
+ /**
+ * Maximum size of APF program allowed.
+ */
+ public final int maximumApfProgramSize;
+
+ /**
+ * Format of packets passed to APF filter. Should be one of ARPHRD_*
+ */
+ public final int apfPacketFormat;
+
+ ApfCapabilities(int apfVersionSupported, int maximumApfProgramSize, int apfPacketFormat) {
+ this.apfVersionSupported = apfVersionSupported;
+ this.maximumApfProgramSize = maximumApfProgramSize;
+ this.apfPacketFormat = apfPacketFormat;
+ }
+
+ public String toString() {
+ return String.format("%s{version: %d, maxSize: %d format: %d}", getClass().getSimpleName(),
+ apfVersionSupported, maximumApfProgramSize, apfPacketFormat);
+ }
+}
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
new file mode 100644
index 0000000..f430a30
--- /dev/null
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -0,0 +1,909 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.apf;
+
+import static android.system.OsConstants.*;
+
+import android.net.LinkProperties;
+import android.net.NetworkUtils;
+import android.net.apf.ApfGenerator;
+import android.net.apf.ApfGenerator.IllegalInstructionException;
+import android.net.apf.ApfGenerator.Register;
+import android.net.ip.IpManager;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.PacketSocketAddress;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.HexDump;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.lang.Thread;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.nio.BufferUnderflowException;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import libcore.io.IoBridge;
+
+/**
+ * For networks that support packet filtering via APF programs, {@code ApfFilter}
+ * listens for IPv6 ICMPv6 router advertisements (RAs) and generates APF programs to
+ * filter out redundant duplicate ones.
+ *
+ * Threading model:
+ * A collection of RAs we've received is kept in mRas. Generating APF programs uses mRas to
+ * know what RAs to filter for, thus generating APF programs is dependent on mRas.
+ * mRas can be accessed by multiple threads:
+ * - ReceiveThread, which listens for RAs and adds them to mRas, and generates APF programs.
+ * - callers of:
+ * - setMulticastFilter(), which can cause an APF program to be generated.
+ * - dump(), which dumps mRas among other things.
+ * - shutdown(), which clears mRas.
+ * So access to mRas is synchronized.
+ *
+ * @hide
+ */
+public class ApfFilter {
+ // Thread to listen for RAs.
+ private class ReceiveThread extends Thread {
+ private final byte[] mPacket = new byte[1514];
+ private final FileDescriptor mSocket;
+ private volatile boolean mStopped;
+
+ public ReceiveThread(FileDescriptor socket) {
+ mSocket = socket;
+ }
+
+ public void halt() {
+ mStopped = true;
+ try {
+ // Interrupts the read() call the thread is blocked in.
+ IoBridge.closeAndSignalBlockedThreads(mSocket);
+ } catch (IOException ignored) {}
+ }
+
+ @Override
+ public void run() {
+ log("begin monitoring");
+ while (!mStopped) {
+ try {
+ int length = Os.read(mSocket, mPacket, 0, mPacket.length);
+ processRa(mPacket, length);
+ } catch (IOException|ErrnoException e) {
+ if (!mStopped) {
+ Log.e(TAG, "Read error", e);
+ }
+ }
+ }
+ }
+ }
+
+ private static final String TAG = "ApfFilter";
+ private static final boolean DBG = true;
+ private static final boolean VDBG = false;
+
+ private static final int ETH_HEADER_LEN = 14;
+ private static final int ETH_ETHERTYPE_OFFSET = 12;
+ private static final byte[] ETH_BROADCAST_MAC_ADDRESS = new byte[]{
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
+ // TODO: Make these offsets relative to end of link-layer header; don't include ETH_HEADER_LEN.
+ private static final int IPV4_FRAGMENT_OFFSET_OFFSET = ETH_HEADER_LEN + 6;
+ // Endianness is not an issue for this constant because the APF interpreter always operates in
+ // network byte order.
+ private static final int IPV4_FRAGMENT_OFFSET_MASK = 0x1fff;
+ private static final int IPV4_PROTOCOL_OFFSET = ETH_HEADER_LEN + 9;
+ private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16;
+
+ private static final int IPV6_NEXT_HEADER_OFFSET = ETH_HEADER_LEN + 6;
+ private static final int IPV6_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 8;
+ private static final int IPV6_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 24;
+ private static final int IPV6_HEADER_LEN = 40;
+ // The IPv6 all nodes address ff02::1
+ private static final byte[] IPV6_ALL_NODES_ADDRESS =
+ new byte[]{ (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
+
+ private static final int ICMP6_TYPE_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN;
+ private static final int ICMP6_NEIGHBOR_ANNOUNCEMENT = 136;
+
+ // NOTE: this must be added to the IPv4 header length in IPV4_HEADER_SIZE_MEMORY_SLOT
+ private static final int UDP_DESTINATION_PORT_OFFSET = ETH_HEADER_LEN + 2;
+ private static final int UDP_HEADER_LEN = 8;
+
+ private static final int DHCP_CLIENT_PORT = 68;
+ // NOTE: this must be added to the IPv4 header length in IPV4_HEADER_SIZE_MEMORY_SLOT
+ private static final int DHCP_CLIENT_MAC_OFFSET = ETH_HEADER_LEN + UDP_HEADER_LEN + 28;
+
+ private static int ARP_HEADER_OFFSET = ETH_HEADER_LEN;
+ private static final byte[] ARP_IPV4_REQUEST_HEADER = new byte[]{
+ 0, 1, // Hardware type: Ethernet (1)
+ 8, 0, // Protocol type: IP (0x0800)
+ 6, // Hardware size: 6
+ 4, // Protocol size: 4
+ 0, 1 // Opcode: request (1)
+ };
+ private static int ARP_TARGET_IP_ADDRESS_OFFSET = ETH_HEADER_LEN + 24;
+
+ private final ApfCapabilities mApfCapabilities;
+ private final IpManager.Callback mIpManagerCallback;
+ private final NetworkInterface mNetworkInterface;
+ private byte[] mHardwareAddress;
+ private ReceiveThread mReceiveThread;
+ @GuardedBy("this")
+ private long mUniqueCounter;
+ @GuardedBy("this")
+ private boolean mMulticastFilter;
+ // Our IPv4 address, if we have just one, otherwise null.
+ @GuardedBy("this")
+ private byte[] mIPv4Address;
+
+ private ApfFilter(ApfCapabilities apfCapabilities, NetworkInterface networkInterface,
+ IpManager.Callback ipManagerCallback, boolean multicastFilter) {
+ mApfCapabilities = apfCapabilities;
+ mIpManagerCallback = ipManagerCallback;
+ mNetworkInterface = networkInterface;
+ mMulticastFilter = multicastFilter;
+
+ maybeStartFilter();
+ }
+
+ private void log(String s) {
+ Log.d(TAG, "(" + mNetworkInterface.getName() + "): " + s);
+ }
+
+ @GuardedBy("this")
+ private long getUniqueNumberLocked() {
+ return mUniqueCounter++;
+ }
+
+ /**
+ * Attempt to start listening for RAs and, if RAs are received, generating and installing
+ * filters to ignore useless RAs.
+ */
+ private void maybeStartFilter() {
+ FileDescriptor socket;
+ try {
+ mHardwareAddress = mNetworkInterface.getHardwareAddress();
+ synchronized(this) {
+ // Install basic filters
+ installNewProgramLocked();
+ }
+ socket = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IPV6);
+ PacketSocketAddress addr = new PacketSocketAddress((short) ETH_P_IPV6,
+ mNetworkInterface.getIndex());
+ Os.bind(socket, addr);
+ NetworkUtils.attachRaFilter(socket, mApfCapabilities.apfPacketFormat);
+ } catch(SocketException|ErrnoException e) {
+ Log.e(TAG, "Error starting filter", e);
+ return;
+ }
+ mReceiveThread = new ReceiveThread(socket);
+ mReceiveThread.start();
+ }
+
+ // Returns seconds since Unix Epoch.
+ private static long curTime() {
+ return System.currentTimeMillis() / 1000L;
+ }
+
+ // A class to hold information about an RA.
+ private class Ra {
+ // From RFC4861:
+ private static final int ICMP6_RA_HEADER_LEN = 16;
+ private static final int ICMP6_RA_CHECKSUM_OFFSET =
+ ETH_HEADER_LEN + IPV6_HEADER_LEN + 2;
+ private static final int ICMP6_RA_CHECKSUM_LEN = 2;
+ private static final int ICMP6_RA_OPTION_OFFSET =
+ ETH_HEADER_LEN + IPV6_HEADER_LEN + ICMP6_RA_HEADER_LEN;
+ private static final int ICMP6_RA_ROUTER_LIFETIME_OFFSET =
+ ETH_HEADER_LEN + IPV6_HEADER_LEN + 6;
+ private static final int ICMP6_RA_ROUTER_LIFETIME_LEN = 2;
+ // Prefix information option.
+ private static final int ICMP6_PREFIX_OPTION_TYPE = 3;
+ private static final int ICMP6_PREFIX_OPTION_LEN = 32;
+ private static final int ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET = 4;
+ private static final int ICMP6_PREFIX_OPTION_VALID_LIFETIME_LEN = 4;
+ private static final int ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET = 8;
+ private static final int ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_LEN = 4;
+
+ // From RFC6106: Recursive DNS Server option
+ private static final int ICMP6_RDNSS_OPTION_TYPE = 25;
+ // From RFC6106: DNS Search List option
+ private static final int ICMP6_DNSSL_OPTION_TYPE = 31;
+
+ // From RFC4191: Route Information option
+ private static final int ICMP6_ROUTE_INFO_OPTION_TYPE = 24;
+ // Above three options all have the same format:
+ private static final int ICMP6_4_BYTE_LIFETIME_OFFSET = 4;
+ private static final int ICMP6_4_BYTE_LIFETIME_LEN = 4;
+
+ // Note: mPacket's position() cannot be assumed to be reset.
+ private final ByteBuffer mPacket;
+ // List of binary ranges that include the whole packet except the lifetimes.
+ // Pairs consist of offset and length.
+ private final ArrayList<Pair<Integer, Integer>> mNonLifetimes =
+ new ArrayList<Pair<Integer, Integer>>();
+ // Minimum lifetime in packet
+ long mMinLifetime;
+ // When the packet was last captured, in seconds since Unix Epoch
+ long mLastSeen;
+
+ // For debugging only. Offsets into the packet where PIOs are.
+ private final ArrayList<Integer> mPrefixOptionOffsets;
+ // For debugging only. How many times this RA was seen.
+ int seenCount = 0;
+
+ // For debugging only. Returns the hex representation of the last matching packet.
+ String getLastMatchingPacket() {
+ return HexDump.toHexString(mPacket.array(), 0, mPacket.capacity(), false /* lowercase */);
+ }
+
+ private String IPv6AddresstoString(int pos) {
+ try {
+ byte[] array = mPacket.array();
+ // Can't just call copyOfRange() and see if it throws, because if it reads past the
+ // end it pads with zeros instead of throwing.
+ if (pos < 0 || pos + 16 > array.length || pos + 16 < pos) {
+ return "???";
+ }
+ byte[] addressBytes = Arrays.copyOfRange(array, pos, pos + 16);
+ InetAddress address = (Inet6Address) InetAddress.getByAddress(addressBytes);
+ return address.getHostAddress();
+ } catch (UnsupportedOperationException e) {
+ // array() failed. Cannot happen, mPacket is array-backed and read-write.
+ return "???";
+ } catch (ClassCastException | UnknownHostException e) {
+ // Cannot happen.
+ return "???";
+ }
+ }
+
+ // Can't be static because it's in a non-static inner class.
+ // TODO: Make this final once RA is its own class.
+ private int uint8(byte b) {
+ return b & 0xff;
+ }
+
+ private int uint16(short s) {
+ return s & 0xffff;
+ }
+
+ private long uint32(int s) {
+ return s & 0xffffffff;
+ }
+
+ public String toString() {
+ try {
+ StringBuffer sb = new StringBuffer();
+ sb.append(String.format("RA %s -> %s %d ",
+ IPv6AddresstoString(IPV6_SRC_ADDR_OFFSET),
+ IPv6AddresstoString(IPV6_DEST_ADDR_OFFSET),
+ uint16(mPacket.getShort(ICMP6_RA_ROUTER_LIFETIME_OFFSET))));
+ for (int i: mPrefixOptionOffsets) {
+ String prefix = IPv6AddresstoString(i + 16);
+ int length = uint8(mPacket.get(i + 2));
+ long valid = mPacket.getInt(i + 4);
+ long preferred = mPacket.getInt(i + 8);
+ sb.append(String.format("%s/%d %d/%d ", prefix, length, valid, preferred));
+ }
+ return sb.toString();
+ } catch (BufferUnderflowException | IndexOutOfBoundsException e) {
+ return "<Malformed RA>";
+ }
+ }
+
+ /**
+ * Add a binary range of the packet that does not include a lifetime to mNonLifetimes.
+ * Assumes mPacket.position() is as far as we've parsed the packet.
+ * @param lastNonLifetimeStart offset within packet of where the last binary range of
+ * data not including a lifetime.
+ * @param lifetimeOffset offset from mPacket.position() to the next lifetime data.
+ * @param lifetimeLength length of the next lifetime data.
+ * @return offset within packet of where the next binary range of data not including
+ * a lifetime. This can be passed into the next invocation of this function
+ * via {@code lastNonLifetimeStart}.
+ */
+ private int addNonLifetime(int lastNonLifetimeStart, int lifetimeOffset,
+ int lifetimeLength) {
+ lifetimeOffset += mPacket.position();
+ mNonLifetimes.add(new Pair<Integer, Integer>(lastNonLifetimeStart,
+ lifetimeOffset - lastNonLifetimeStart));
+ return lifetimeOffset + lifetimeLength;
+ }
+
+ // Note that this parses RA and may throw IllegalArgumentException (from
+ // Buffer.position(int) ) or IndexOutOfBoundsException (from ByteBuffer.get(int) ) if
+ // parsing encounters something non-compliant with specifications.
+ Ra(byte[] packet, int length) {
+ mPacket = ByteBuffer.allocate(length).put(ByteBuffer.wrap(packet, 0, length));
+ mPacket.clear();
+ mLastSeen = curTime();
+
+ // Ignore the checksum.
+ int lastNonLifetimeStart = addNonLifetime(0,
+ ICMP6_RA_CHECKSUM_OFFSET,
+ ICMP6_RA_CHECKSUM_LEN);
+
+ // Parse router lifetime
+ lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
+ ICMP6_RA_ROUTER_LIFETIME_OFFSET,
+ ICMP6_RA_ROUTER_LIFETIME_LEN);
+
+ // Parse ICMPv6 options
+ mPrefixOptionOffsets = new ArrayList<>();
+ mPacket.position(ICMP6_RA_OPTION_OFFSET);
+ while (mPacket.hasRemaining()) {
+ int optionType = ((int)mPacket.get(mPacket.position())) & 0xff;
+ int optionLength = (((int)mPacket.get(mPacket.position() + 1)) & 0xff) * 8;
+ switch (optionType) {
+ case ICMP6_PREFIX_OPTION_TYPE:
+ // Parse valid lifetime
+ lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
+ ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET,
+ ICMP6_PREFIX_OPTION_VALID_LIFETIME_LEN);
+ // Parse preferred lifetime
+ lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
+ ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET,
+ ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_LEN);
+ mPrefixOptionOffsets.add(mPacket.position());
+ break;
+ // These three options have the same lifetime offset and size, so process
+ // together:
+ case ICMP6_ROUTE_INFO_OPTION_TYPE:
+ case ICMP6_RDNSS_OPTION_TYPE:
+ case ICMP6_DNSSL_OPTION_TYPE:
+ // Parse lifetime
+ lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
+ ICMP6_4_BYTE_LIFETIME_OFFSET,
+ ICMP6_4_BYTE_LIFETIME_LEN);
+ break;
+ default:
+ // RFC4861 section 4.2 dictates we ignore unknown options for fowards
+ // compatibility.
+ break;
+ }
+ mPacket.position(mPacket.position() + optionLength);
+ }
+ // Mark non-lifetime bytes since last lifetime.
+ addNonLifetime(lastNonLifetimeStart, 0, 0);
+ mMinLifetime = minLifetime(packet, length);
+ }
+
+ // Ignoring lifetimes (which may change) does {@code packet} match this RA?
+ boolean matches(byte[] packet, int length) {
+ if (length != mPacket.capacity()) return false;
+ byte[] referencePacket = mPacket.array();
+ for (Pair<Integer, Integer> nonLifetime : mNonLifetimes) {
+ for (int i = nonLifetime.first; i < (nonLifetime.first + nonLifetime.second); i++) {
+ if (packet[i] != referencePacket[i]) return false;
+ }
+ }
+ return true;
+ }
+
+ // What is the minimum of all lifetimes within {@code packet} in seconds?
+ // Precondition: matches(packet, length) already returned true.
+ long minLifetime(byte[] packet, int length) {
+ long minLifetime = Long.MAX_VALUE;
+ // Wrap packet in ByteBuffer so we can read big-endian values easily
+ ByteBuffer byteBuffer = ByteBuffer.wrap(packet);
+ for (int i = 0; (i + 1) < mNonLifetimes.size(); i++) {
+ int offset = mNonLifetimes.get(i).first + mNonLifetimes.get(i).second;
+
+ // The checksum is in mNonLifetimes, but it's not a lifetime.
+ if (offset == ICMP6_RA_CHECKSUM_OFFSET) {
+ continue;
+ }
+
+ int lifetimeLength = mNonLifetimes.get(i+1).first - offset;
+ long val;
+ switch (lifetimeLength) {
+ case 2: val = byteBuffer.getShort(offset); break;
+ case 4: val = byteBuffer.getInt(offset); break;
+ default: throw new IllegalStateException("bogus lifetime size " + length);
+ }
+ // Mask to size, converting signed to unsigned
+ val &= (1L << (lifetimeLength * 8)) - 1;
+ minLifetime = Math.min(minLifetime, val);
+ }
+ return minLifetime;
+ }
+
+ // How many seconds does this RA's have to live, taking into account the fact
+ // that we might have seen it a while ago.
+ long currentLifetime() {
+ return mMinLifetime - (curTime() - mLastSeen);
+ }
+
+ boolean isExpired() {
+ // TODO: We may want to handle 0 lifetime RAs differently, if they are common. We'll
+ // have to calculte the filter lifetime specially as a fraction of 0 is still 0.
+ return currentLifetime() <= 0;
+ }
+
+ // Append a filter for this RA to {@code gen}. Jump to DROP_LABEL if it should be dropped.
+ // Jump to the next filter if packet doesn't match this RA.
+ @GuardedBy("ApfFilter.this")
+ long generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
+ String nextFilterLabel = "Ra" + getUniqueNumberLocked();
+ // Skip if packet is not the right size
+ gen.addLoadFromMemory(Register.R0, gen.PACKET_SIZE_MEMORY_SLOT);
+ gen.addJumpIfR0NotEquals(mPacket.capacity(), nextFilterLabel);
+ int filterLifetime = (int)(currentLifetime() / FRACTION_OF_LIFETIME_TO_FILTER);
+ // Skip filter if expired
+ gen.addLoadFromMemory(Register.R0, gen.FILTER_AGE_MEMORY_SLOT);
+ gen.addJumpIfR0GreaterThan(filterLifetime, nextFilterLabel);
+ for (int i = 0; i < mNonLifetimes.size(); i++) {
+ // Generate code to match the packet bytes
+ Pair<Integer, Integer> nonLifetime = mNonLifetimes.get(i);
+ // Don't generate JNEBS instruction for 0 bytes as it always fails the
+ // ASSERT_FORWARD_IN_PROGRAM(pc + cmp_imm - 1) check where cmp_imm is
+ // the number of bytes to compare. nonLifetime is zero between the
+ // valid and preferred lifetimes in the prefix option.
+ if (nonLifetime.second != 0) {
+ gen.addLoadImmediate(Register.R0, nonLifetime.first);
+ gen.addJumpIfBytesNotEqual(Register.R0,
+ Arrays.copyOfRange(mPacket.array(), nonLifetime.first,
+ nonLifetime.first + nonLifetime.second),
+ nextFilterLabel);
+ }
+ // Generate code to test the lifetimes haven't gone down too far
+ if ((i + 1) < mNonLifetimes.size()) {
+ Pair<Integer, Integer> nextNonLifetime = mNonLifetimes.get(i + 1);
+ int offset = nonLifetime.first + nonLifetime.second;
+ // Skip the checksum.
+ if (offset == ICMP6_RA_CHECKSUM_OFFSET) {
+ continue;
+ }
+ int length = nextNonLifetime.first - offset;
+ switch (length) {
+ case 4: gen.addLoad32(Register.R0, offset); break;
+ case 2: gen.addLoad16(Register.R0, offset); break;
+ default: throw new IllegalStateException("bogus lifetime size " + length);
+ }
+ gen.addJumpIfR0LessThan(filterLifetime, nextFilterLabel);
+ }
+ }
+ gen.addJump(gen.DROP_LABEL);
+ gen.defineLabel(nextFilterLabel);
+ return filterLifetime;
+ }
+ }
+
+ // Maximum number of RAs to filter for.
+ private static final int MAX_RAS = 10;
+
+ @GuardedBy("this")
+ private ArrayList<Ra> mRas = new ArrayList<Ra>();
+
+ // There is always some marginal benefit to updating the installed APF program when an RA is
+ // seen because we can extend the program's lifetime slightly, but there is some cost to
+ // updating the program, so don't bother unless the program is going to expire soon. This
+ // constant defines "soon" in seconds.
+ private static final long MAX_PROGRAM_LIFETIME_WORTH_REFRESHING = 30;
+ // We don't want to filter an RA for it's whole lifetime as it'll be expired by the time we ever
+ // see a refresh. Using half the lifetime might be a good idea except for the fact that
+ // packets may be dropped, so let's use 6.
+ private static final int FRACTION_OF_LIFETIME_TO_FILTER = 6;
+
+ // When did we last install a filter program? In seconds since Unix Epoch.
+ @GuardedBy("this")
+ private long mLastTimeInstalledProgram;
+ // How long should the last installed filter program live for? In seconds.
+ @GuardedBy("this")
+ private long mLastInstalledProgramMinLifetime;
+
+ // For debugging only. The last program installed.
+ @GuardedBy("this")
+ private byte[] mLastInstalledProgram;
+
+ /**
+ * Generate filter code to process ARP packets. Execution of this code ends in either the
+ * DROP_LABEL or PASS_LABEL and does not fall off the end.
+ * Preconditions:
+ * - Packet being filtered is ARP
+ */
+ @GuardedBy("this")
+ private void generateArpFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
+ // Here's a basic summary of what the ARP filter program does:
+ //
+ // if it's not an ARP IPv4 request:
+ // pass
+ // if it's not a request for our IPv4 address:
+ // drop
+ // pass
+
+ // if it's not an ARP IPv4 request, pass
+ gen.addLoadImmediate(Register.R0, ARP_HEADER_OFFSET);
+ gen.addJumpIfBytesNotEqual(Register.R0, ARP_IPV4_REQUEST_HEADER, gen.PASS_LABEL);
+ // if it's not a request for our IPv4 address, drop
+ gen.addLoadImmediate(Register.R0, ARP_TARGET_IP_ADDRESS_OFFSET);
+ gen.addJumpIfBytesNotEqual(Register.R0, mIPv4Address, gen.DROP_LABEL);
+
+ // Otherwise, pass
+ gen.addJump(gen.PASS_LABEL);
+ }
+
+ /**
+ * Generate filter code to process IPv4 packets. Execution of this code ends in either the
+ * DROP_LABEL or PASS_LABEL and does not fall off the end.
+ * Preconditions:
+ * - Packet being filtered is IPv4
+ * - R1 is initialized to 0
+ */
+ @GuardedBy("this")
+ private void generateIPv4FilterLocked(ApfGenerator gen) throws IllegalInstructionException {
+ // Here's a basic summary of what the IPv4 filter program does:
+ //
+ // if it's multicast and we're dropping multicast:
+ // drop
+ // if it's not broadcast:
+ // pass
+ // if it's not DHCP destined to our MAC:
+ // drop
+ // pass
+
+ if (mMulticastFilter) {
+ // Check for multicast destination address range
+ gen.addLoad8(Register.R0, IPV4_DEST_ADDR_OFFSET);
+ gen.addAnd(0xf0);
+ gen.addJumpIfR0Equals(0xe0, gen.DROP_LABEL);
+ }
+
+ // Drop all broadcasts besides DHCP addressed to us
+ // If not a broadcast packet, pass
+ // NOTE: Relies on R1 being initialized to 0 which is the offset of the ethernet
+ // destination MAC address
+ gen.addJumpIfBytesNotEqual(Register.R1, ETH_BROADCAST_MAC_ADDRESS, gen.PASS_LABEL);
+ // If not UDP, drop
+ gen.addLoad8(Register.R0, IPV4_PROTOCOL_OFFSET);
+ gen.addJumpIfR0NotEquals(IPPROTO_UDP, gen.DROP_LABEL);
+ // If fragment, drop. This matches the BPF filter installed by the DHCP client.
+ gen.addLoad16(Register.R0, IPV4_FRAGMENT_OFFSET_OFFSET);
+ gen.addJumpIfR0AnyBitsSet(IPV4_FRAGMENT_OFFSET_MASK, gen.DROP_LABEL);
+ // If not to DHCP client port, drop
+ gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
+ gen.addLoad16Indexed(Register.R0, UDP_DESTINATION_PORT_OFFSET);
+ gen.addJumpIfR0NotEquals(DHCP_CLIENT_PORT, gen.DROP_LABEL);
+ // If not DHCP to our MAC address, drop
+ gen.addLoadImmediate(Register.R0, DHCP_CLIENT_MAC_OFFSET);
+ // NOTE: Relies on R1 containing IPv4 header offset.
+ gen.addAddR1();
+ gen.addJumpIfBytesNotEqual(Register.R0, mHardwareAddress, gen.DROP_LABEL);
+
+ // Otherwise, pass
+ gen.addJump(gen.PASS_LABEL);
+ }
+
+
+ /**
+ * Generate filter code to process IPv6 packets. Execution of this code ends in either the
+ * DROP_LABEL or PASS_LABEL, or falls off the end for ICMPv6 packets.
+ * Preconditions:
+ * - Packet being filtered is IPv6
+ */
+ @GuardedBy("this")
+ private void generateIPv6FilterLocked(ApfGenerator gen) throws IllegalInstructionException {
+ // Here's a basic summary of what the IPv6 filter program does:
+ //
+ // if it's not ICMPv6:
+ // pass
+ // if it's ICMPv6 NA to ff02::1:
+ // drop
+
+ // If not ICMPv6, pass
+ gen.addLoad8(Register.R0, IPV6_NEXT_HEADER_OFFSET);
+ // TODO: Drop multicast if the multicast filter is enabled.
+ gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, gen.PASS_LABEL);
+ // Add unsolicited multicast neighbor announcements filter
+ String skipUnsolicitedMulticastNALabel = "skipUnsolicitedMulticastNA";
+ // If not neighbor announcements, skip unsolicited multicast NA filter
+ gen.addLoad8(Register.R0, ICMP6_TYPE_OFFSET);
+ gen.addJumpIfR0NotEquals(ICMP6_NEIGHBOR_ANNOUNCEMENT, skipUnsolicitedMulticastNALabel);
+ // If to ff02::1, drop
+ // TODO: Drop only if they don't contain the address of on-link neighbours.
+ gen.addLoadImmediate(Register.R0, IPV6_DEST_ADDR_OFFSET);
+ gen.addJumpIfBytesNotEqual(Register.R0, IPV6_ALL_NODES_ADDRESS,
+ skipUnsolicitedMulticastNALabel);
+ gen.addJump(gen.DROP_LABEL);
+ gen.defineLabel(skipUnsolicitedMulticastNALabel);
+ }
+
+ /**
+ * Begin generating an APF program to:
+ * <ul>
+ * <li>Drop ARP requests not for us, if mIPv4Address is set,
+ * <li>Drop IPv4 broadcast packets, except DHCP destined to our MAC,
+ * <li>Drop IPv4 multicast packets, if mMulticastFilter,
+ * <li>Pass all other IPv4 packets,
+ * <li>Pass all non-ICMPv6 IPv6 packets,
+ * <li>Pass all non-IPv4 and non-IPv6 packets,
+ * <li>Drop IPv6 ICMPv6 NAs to ff02::1.
+ * <li>Let execution continue off the end of the program for IPv6 ICMPv6 packets. This allows
+ * insertion of RA filters here, or if there aren't any, just passes the packets.
+ * </ul>
+ */
+ @GuardedBy("this")
+ private ApfGenerator beginProgramLocked() throws IllegalInstructionException {
+ ApfGenerator gen = new ApfGenerator();
+ // This is guaranteed to return true because of the check in maybeCreate.
+ gen.setApfVersion(mApfCapabilities.apfVersionSupported);
+
+ // Here's a basic summary of what the initial program does:
+ //
+ // if it's ARP:
+ // inesrt ARP filter to drop or pass these appropriately
+ // if it's IPv4:
+ // insert IPv4 filter to drop or pass these appropriately
+ // if it's not IPv6:
+ // pass
+ // insert IPv6 filter to drop, pass, or fall off the end for ICMPv6 packets
+
+ gen.addLoad16(Register.R0, ETH_ETHERTYPE_OFFSET);
+
+ if (mIPv4Address != null) {
+ // Add ARP filters:
+ String skipArpFiltersLabel = "skipArpFilters";
+ // If not ARP, skip ARP filters
+ // NOTE: Relies on R0 containing ethertype.
+ gen.addJumpIfR0NotEquals(ETH_P_ARP, skipArpFiltersLabel);
+ generateArpFilterLocked(gen);
+ gen.defineLabel(skipArpFiltersLabel);
+ }
+
+ // Add IPv4 filters:
+ String skipIPv4FiltersLabel = "skipIPv4Filters";
+ // NOTE: Relies on R0 containing ethertype. This is safe because if we got here, we did not
+ // execute the ARP filter, since that filter does not fall through, but either drops or
+ // passes.
+ gen.addJumpIfR0NotEquals(ETH_P_IP, skipIPv4FiltersLabel);
+ // NOTE: Relies on R1 being initialized to 0.
+ generateIPv4FilterLocked(gen);
+ gen.defineLabel(skipIPv4FiltersLabel);
+
+ // Add IPv6 filters:
+ // If not IPv6, pass
+ // NOTE: Relies on R0 containing ethertype. This is safe because if we got here, we did not
+ // execute the ARP or IPv4 filters, since those filters do not fall through, but either
+ // drop or pass.
+ gen.addJumpIfR0NotEquals(ETH_P_IPV6, gen.PASS_LABEL);
+ generateIPv6FilterLocked(gen);
+ return gen;
+ }
+
+ @GuardedBy("this")
+ private void installNewProgramLocked() {
+ purgeExpiredRasLocked();
+ final byte[] program;
+ long programMinLifetime = Long.MAX_VALUE;
+ try {
+ // Step 1: Determine how many RA filters we can fit in the program.
+ ApfGenerator gen = beginProgramLocked();
+ ArrayList<Ra> rasToFilter = new ArrayList<Ra>();
+ for (Ra ra : mRas) {
+ ra.generateFilterLocked(gen);
+ // Stop if we get too big.
+ if (gen.programLengthOverEstimate() > mApfCapabilities.maximumApfProgramSize) break;
+ rasToFilter.add(ra);
+ }
+ // Step 2: Actually generate the program
+ gen = beginProgramLocked();
+ for (Ra ra : rasToFilter) {
+ programMinLifetime = Math.min(programMinLifetime, ra.generateFilterLocked(gen));
+ }
+ // Execution will reach the end of the program if no filters match, which will pass the
+ // packet to the AP.
+ program = gen.generate();
+ } catch (IllegalInstructionException e) {
+ Log.e(TAG, "Program failed to generate: ", e);
+ return;
+ }
+ mLastTimeInstalledProgram = curTime();
+ mLastInstalledProgramMinLifetime = programMinLifetime;
+ mLastInstalledProgram = program;
+ if (VDBG) {
+ hexDump("Installing filter: ", program, program.length);
+ }
+ mIpManagerCallback.installPacketFilter(program);
+ }
+
+ // Install a new filter program if the last installed one will die soon.
+ @GuardedBy("this")
+ private void maybeInstallNewProgramLocked() {
+ if (mRas.size() == 0) return;
+ // If the current program doesn't expire for a while, don't bother updating.
+ long expiry = mLastTimeInstalledProgram + mLastInstalledProgramMinLifetime;
+ if (expiry < curTime() + MAX_PROGRAM_LIFETIME_WORTH_REFRESHING) {
+ installNewProgramLocked();
+ }
+ }
+
+ private void hexDump(String msg, byte[] packet, int length) {
+ log(msg + HexDump.toHexString(packet, 0, length, false /* lowercase */));
+ }
+
+ @GuardedBy("this")
+ private void purgeExpiredRasLocked() {
+ for (int i = 0; i < mRas.size();) {
+ if (mRas.get(i).isExpired()) {
+ log("Expiring " + mRas.get(i));
+ mRas.remove(i);
+ } else {
+ i++;
+ }
+ }
+ }
+
+ private synchronized void processRa(byte[] packet, int length) {
+ if (VDBG) hexDump("Read packet = ", packet, length);
+
+ // Have we seen this RA before?
+ for (int i = 0; i < mRas.size(); i++) {
+ Ra ra = mRas.get(i);
+ if (ra.matches(packet, length)) {
+ if (VDBG) log("matched RA " + ra);
+ // Update lifetimes.
+ ra.mLastSeen = curTime();
+ ra.mMinLifetime = ra.minLifetime(packet, length);
+ ra.seenCount++;
+
+ // Keep mRas in LRU order so as to prioritize generating filters for recently seen
+ // RAs. LRU prioritizes this because RA filters are generated in order from mRas
+ // until the filter program exceeds the maximum filter program size allowed by the
+ // chipset, so RAs appearing earlier in mRas are more likely to make it into the
+ // filter program.
+ // TODO: consider sorting the RAs in order of increasing expiry time as well.
+ // Swap to front of array.
+ mRas.add(0, mRas.remove(i));
+
+ maybeInstallNewProgramLocked();
+ return;
+ }
+ }
+ purgeExpiredRasLocked();
+ // TODO: figure out how to proceed when we've received more then MAX_RAS RAs.
+ if (mRas.size() >= MAX_RAS) return;
+ final Ra ra;
+ try {
+ ra = new Ra(packet, length);
+ } catch (Exception e) {
+ Log.e(TAG, "Error parsing RA: " + e);
+ return;
+ }
+ // Ignore 0 lifetime RAs.
+ if (ra.isExpired()) return;
+ log("Adding " + ra);
+ mRas.add(ra);
+ installNewProgramLocked();
+ }
+
+ /**
+ * Create an {@link ApfFilter} if {@code apfCapabilities} indicates support for packet
+ * filtering using APF programs.
+ */
+ public static ApfFilter maybeCreate(ApfCapabilities apfCapabilities,
+ NetworkInterface networkInterface, IpManager.Callback ipManagerCallback,
+ boolean multicastFilter) {
+ if (apfCapabilities == null || networkInterface == null) return null;
+ if (apfCapabilities.apfVersionSupported == 0) return null;
+ if (apfCapabilities.maximumApfProgramSize < 512) {
+ Log.e(TAG, "Unacceptably small APF limit: " + apfCapabilities.maximumApfProgramSize);
+ return null;
+ }
+ // For now only support generating programs for Ethernet frames. If this restriction is
+ // lifted:
+ // 1. the program generator will need its offsets adjusted.
+ // 2. the packet filter attached to our packet socket will need its offset adjusted.
+ if (apfCapabilities.apfPacketFormat != ARPHRD_ETHER) return null;
+ if (!new ApfGenerator().setApfVersion(apfCapabilities.apfVersionSupported)) {
+ Log.e(TAG, "Unsupported APF version: " + apfCapabilities.apfVersionSupported);
+ return null;
+ }
+ return new ApfFilter(apfCapabilities, networkInterface, ipManagerCallback, multicastFilter);
+ }
+
+ public synchronized void shutdown() {
+ if (mReceiveThread != null) {
+ log("shutting down");
+ mReceiveThread.halt(); // Also closes socket.
+ mReceiveThread = null;
+ }
+ mRas.clear();
+ }
+
+ public synchronized void setMulticastFilter(boolean enabled) {
+ if (mMulticastFilter != enabled) {
+ mMulticastFilter = enabled;
+ installNewProgramLocked();
+ }
+ }
+
+ // Find the single IPv4 address if there is one, otherwise return null.
+ private static byte[] findIPv4Address(LinkProperties lp) {
+ byte[] ipv4Address = null;
+ for (InetAddress inetAddr : lp.getAddresses()) {
+ byte[] addr = inetAddr.getAddress();
+ if (addr.length != 4) continue;
+ // More than one IPv4 address, abort
+ if (ipv4Address != null && !Arrays.equals(ipv4Address, addr)) return null;
+ ipv4Address = addr;
+ }
+ return ipv4Address;
+ }
+
+ public synchronized void setLinkProperties(LinkProperties lp) {
+ // NOTE: Do not keep a copy of LinkProperties as it would further duplicate state.
+ byte[] ipv4Address = findIPv4Address(lp);
+ // If ipv4Address is the same as mIPv4Address, then there's no change, just return.
+ if (Arrays.equals(ipv4Address, mIPv4Address)) return;
+ // Otherwise update mIPv4Address and install new program.
+ mIPv4Address = ipv4Address;
+ installNewProgramLocked();
+ }
+
+ public synchronized void dump(IndentingPrintWriter pw) {
+ pw.println("APF caps: " + mApfCapabilities);
+ pw.println("Receive thread: " + (mReceiveThread != null ? "RUNNING" : "STOPPED"));
+ pw.println("Multicast filtering: " + mMulticastFilter);
+ try {
+ pw.println("IPv4 address: " + InetAddress.getByAddress(mIPv4Address));
+ } catch (UnknownHostException|NullPointerException e) {}
+ if (mLastTimeInstalledProgram == 0) {
+ pw.println("No program installed.");
+ return;
+ }
+
+ pw.println(String.format(
+ "Last program length %d, installed %ds ago, lifetime %d",
+ mLastInstalledProgram.length, curTime() - mLastTimeInstalledProgram,
+ mLastInstalledProgramMinLifetime));
+
+ pw.println("RA filters:");
+ pw.increaseIndent();
+ for (Ra ra: mRas) {
+ pw.println(ra);
+ pw.increaseIndent();
+ pw.println(String.format(
+ "Seen: %d, last %ds ago", ra.seenCount, curTime() - ra.mLastSeen));
+ if (DBG) {
+ pw.println("Last match:");
+ pw.increaseIndent();
+ pw.println(ra.getLastMatchingPacket());
+ pw.decreaseIndent();
+ }
+ pw.decreaseIndent();
+ }
+
+ if (DBG) {
+ pw.println("Last program:");
+ pw.increaseIndent();
+ pw.println(HexDump.toHexString(mLastInstalledProgram, false /* lowercase */));
+ pw.decreaseIndent();
+ }
+
+ pw.decreaseIndent();
+ }
+}
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index e2562cd..406dd56 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -30,6 +30,8 @@
import android.net.InterfaceConfiguration;
import android.net.LinkAddress;
import android.net.NetworkUtils;
+import android.net.metrics.IpConnectivityEvent;
+import android.net.metrics.DhcpClientEvent;
import android.os.IBinder;
import android.os.INetworkManagementService;
import android.os.Message;
@@ -136,7 +138,7 @@
MessageUtils.findMessageNames(sMessageClasses);
// DHCP parameters that we request.
- private static final byte[] REQUESTED_PARAMS = new byte[] {
+ /* package */ static final byte[] REQUESTED_PARAMS = new byte[] {
DHCP_SUBNET_MASK,
DHCP_ROUTER,
DHCP_DNS_SERVER,
@@ -146,6 +148,7 @@
DHCP_LEASE_TIME,
DHCP_RENEWAL_TIME,
DHCP_REBINDING_TIME,
+ DHCP_VENDOR_INFO,
};
// DHCP flag that means "yes, we support unicast."
@@ -355,11 +358,15 @@
if (!stopped) {
Log.e(TAG, "Read error", e);
}
+ DhcpClientEvent.logEvent(IpConnectivityEvent.IPCE_DHCP_RECV_ERROR,
+ mIfaceName, e.getMessage());
} catch (DhcpPacket.ParseException e) {
Log.e(TAG, "Can't parse packet: " + e.getMessage());
if (PACKET_DBG) {
Log.d(TAG, HexDump.dumpHexString(mPacket, 0, length));
}
+ DhcpClientEvent.logEvent(IpConnectivityEvent.IPCE_DHCP_PARSE_ERROR, mIfaceName,
+ e.getMessage());
}
}
if (DBG) Log.d(TAG, "Receive thread stopped");
@@ -456,7 +463,9 @@
abstract class LoggingState extends State {
public void enter() {
- if (STATE_DBG) Log.d(TAG, "Entering state " + getName());
+ String msg = "Entering state " + getName();
+ if (STATE_DBG) Log.d(TAG, msg);
+ DhcpClientEvent.logEvent(IpConnectivityEvent.IPCE_DHCP_STATE_CHANGE, mIfaceName, msg);
}
private String messageName(int what) {
diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java
index e27f69e..a881408 100644
--- a/services/net/java/android/net/dhcp/DhcpPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpPacket.java
@@ -57,6 +57,17 @@
public static final int HWADDR_LEN = 16;
public static final int MAX_OPTION_LEN = 255;
+
+ /**
+ * The minimum and maximum MTU that we are prepared to use. We set the minimum to the minimum
+ * IPv6 MTU because the IPv6 stack enters unusual codepaths when the link MTU drops below 1280,
+ * and does not recover if the MTU is brought above 1280 again. We set the maximum to 1500
+ * because in general it is risky to assume that the hardware is able to send/receive packets
+ * larger than 1500 bytes even if the network supports it.
+ */
+ private static final int MIN_MTU = 1280;
+ private static final int MAX_MTU = 1500;
+
/**
* IP layer definitions.
*/
@@ -917,7 +928,7 @@
break;
case DHCP_MTU:
expectedLen = 2;
- mtu = Short.valueOf(packet.getShort());
+ mtu = packet.getShort();
break;
case DHCP_DOMAIN_NAME:
expectedLen = optionLen;
@@ -1106,6 +1117,8 @@
results.serverAddress = mServerIdentifier;
results.vendorInfo = mVendorInfo;
results.leaseDuration = (mLeaseTime != null) ? mLeaseTime : INFINITE_LEASE;
+ results.mtu = (mMtu != null && MIN_MTU <= mMtu && mMtu <= MAX_MTU) ? mMtu : 0;
+
return results;
}
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index c7c5015..54aeb3d 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -19,6 +19,8 @@
import com.android.internal.util.MessageUtils;
import android.content.Context;
+import android.net.apf.ApfCapabilities;
+import android.net.apf.ApfFilter;
import android.net.DhcpResults;
import android.net.InterfaceConfiguration;
import android.net.LinkAddress;
@@ -38,10 +40,13 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.server.net.NetlinkTracker;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
@@ -95,10 +100,6 @@
public void onProvisioningSuccess(LinkProperties newLp) {}
public void onProvisioningFailure(LinkProperties newLp) {}
- // This is called whenever 464xlat is being enabled or disabled (i.e.
- // started or stopped).
- public void on464XlatChange(boolean enabled) {}
-
// Invoked on LinkProperties changes.
public void onLinkPropertiesChange(LinkProperties newLp) {}
@@ -108,6 +109,17 @@
// Called when the IpManager state machine terminates.
public void onQuit() {}
+
+ // Install an APF program to filter incoming packets.
+ public void installPacketFilter(byte[] filter) {}
+
+ // If multicast filtering cannot be accomplished with APF, this function will be called to
+ // actuate multicast filtering using another means.
+ public void setFallbackMulticastFilter(boolean enabled) {}
+
+ // Enabled/disable Neighbor Discover offload functionality. This is
+ // called, for example, whenever 464xlat is being started or stopped.
+ public void setNeighborDiscoveryOffload(boolean enable) {}
}
public static class WaitForProvisioningCallback extends Callback {
@@ -179,6 +191,11 @@
return this;
}
+ public Builder withApfCapabilities(ApfCapabilities apfCapabilities) {
+ mConfig.mApfCapabilities = apfCapabilities;
+ return this;
+ }
+
public ProvisioningConfiguration build() {
return new ProvisioningConfiguration(mConfig);
}
@@ -187,6 +204,7 @@
/* package */ boolean mUsingIpReachabilityMonitor = true;
/* package */ boolean mRequestedPreDhcpAction;
/* package */ StaticIpConfiguration mStaticIpConfig;
+ /* package */ ApfCapabilities mApfCapabilities;
public ProvisioningConfiguration() {}
@@ -194,9 +212,12 @@
mUsingIpReachabilityMonitor = other.mUsingIpReachabilityMonitor;
mRequestedPreDhcpAction = other.mRequestedPreDhcpAction;
mStaticIpConfig = other.mStaticIpConfig;
+ mApfCapabilities = other.mApfCapabilities;
}
}
+ public static final String DUMP_ARG = "ipmanager";
+
private static final int CMD_STOP = 1;
private static final int CMD_START = 2;
private static final int CMD_CONFIRM = 3;
@@ -205,6 +226,7 @@
private static final int EVENT_NETLINK_LINKPROPERTIES_CHANGED = 5;
private static final int CMD_UPDATE_TCP_BUFFER_SIZES = 6;
private static final int CMD_UPDATE_HTTP_PROXY = 7;
+ private static final int CMD_SET_MULTICAST_FILTER = 8;
private static final int MAX_LOG_RECORDS = 1000;
@@ -229,7 +251,7 @@
private final INetworkManagementService mNwService;
private final NetlinkTracker mNetlinkTracker;
- private int mInterfaceIndex;
+ private NetworkInterface mNetworkInterface;
/**
* Non-final member variables accessed only from within our StateMachine.
@@ -240,6 +262,8 @@
private DhcpResults mDhcpResults;
private String mTcpBufferSizes;
private ProxyInfo mHttpProxy;
+ private ApfFilter mApfFilter;
+ private boolean mMulticastFiltering;
/**
* Member variables accessed both from within the StateMachine thread
@@ -278,15 +302,20 @@
}) {
@Override
public void interfaceAdded(String iface) {
+ super.interfaceAdded(iface);
if (mClatInterfaceName.equals(iface)) {
- mCallback.on464XlatChange(true);
+ mCallback.setNeighborDiscoveryOffload(false);
}
}
@Override
public void interfaceRemoved(String iface) {
+ super.interfaceRemoved(iface);
if (mClatInterfaceName.equals(iface)) {
- mCallback.on464XlatChange(false);
+ // TODO: consider sending a message to the IpManager main
+ // StateMachine thread, in case "NDO enabled" state becomes
+ // tied to more things that 464xlat operation.
+ mCallback.setNeighborDiscoveryOffload(true);
}
}
};
@@ -325,7 +354,7 @@
}
public void startProvisioning(ProvisioningConfiguration req) {
- getInterfaceIndex();
+ getNetworkInterface();
sendMessage(CMD_START, new ProvisioningConfiguration(req));
}
@@ -372,12 +401,34 @@
sendMessage(CMD_UPDATE_HTTP_PROXY, proxyInfo);
}
+ /**
+ * Enable or disable the multicast filter. Attempts to use APF to accomplish the filtering,
+ * if not, Callback.setFallbackMulticastFilter() is called.
+ */
+ public void setMulticastFilter(boolean enabled) {
+ sendMessage(CMD_SET_MULTICAST_FILTER, enabled);
+ }
+
public LinkProperties getLinkProperties() {
synchronized (mLock) {
return new LinkProperties(mLinkProperties);
}
}
+ public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
+ pw.println("APF dump:");
+ pw.increaseIndent();
+ // Thread-unsafe access to mApfFilter but just used for debugging.
+ ApfFilter apfFilter = mApfFilter;
+ if (apfFilter != null) {
+ apfFilter.dump(pw);
+ } else {
+ pw.println("No apf support");
+ }
+ pw.decreaseIndent();
+ }
+
/**
* Internals.
@@ -392,7 +443,7 @@
protected String getLogRecString(Message msg) {
final String logLine = String.format(
"iface{%s/%d} arg1{%d} arg2{%d} obj{%s}",
- mInterfaceName, mInterfaceIndex,
+ mInterfaceName, mNetworkInterface == null ? -1 : mNetworkInterface.getIndex(),
msg.arg1, msg.arg2, Objects.toString(msg.obj));
if (VDBG) {
Log.d(mTag, getWhatToString(msg.what) + " " + logLine);
@@ -400,12 +451,12 @@
return logLine;
}
- private void getInterfaceIndex() {
+ private void getNetworkInterface() {
try {
- mInterfaceIndex = NetworkInterface.getByName(mInterfaceName).getIndex();
+ mNetworkInterface = NetworkInterface.getByName(mInterfaceName);
} catch (SocketException | NullPointerException e) {
// TODO: throw new IllegalStateException.
- Log.e(mTag, "ALERT: Failed to get interface index: ", e);
+ Log.e(mTag, "ALERT: Failed to get interface object: ", e);
}
}
@@ -487,6 +538,7 @@
}
private void dispatchCallback(ProvisioningChange delta, LinkProperties newLp) {
+ if (mApfFilter != null) mApfFilter.setLinkProperties(newLp);
switch (delta) {
case GAINED_PROVISIONING:
if (VDBG) { Log.d(mTag, "onProvisioningSuccess()"); }
@@ -573,6 +625,10 @@
}
}
newLp.setDomains(mDhcpResults.domains);
+
+ if (mDhcpResults.mtu != 0) {
+ newLp.setMtu(mDhcpResults.mtu);
+ }
}
// [4] Add in TCP buffer sizes and HTTP Proxy config, if available.
@@ -693,6 +749,10 @@
handleLinkPropertiesUpdate(NO_CALLBACKS);
break;
+ case CMD_SET_MULTICAST_FILTER:
+ mMulticastFiltering = (boolean) msg.obj;
+ break;
+
case DhcpClient.CMD_ON_QUIT:
// Everything is already stopped.
Log.e(mTag, "Unexpected CMD_ON_QUIT (already stopped).");
@@ -732,6 +792,11 @@
class StartedState extends State {
@Override
public void enter() {
+ mApfFilter = ApfFilter.maybeCreate(mConfiguration.mApfCapabilities, mNetworkInterface,
+ mCallback, mMulticastFiltering);
+ // TODO: investigate the effects of any multicast filtering racing/interfering with the
+ // rest of this IP configuration startup.
+ if (mApfFilter == null) mCallback.setFallbackMulticastFilter(mMulticastFiltering);
// Set privacy extensions.
try {
mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
@@ -788,6 +853,11 @@
mDhcpClient.doQuit();
}
+ if (mApfFilter != null) {
+ mApfFilter.shutdown();
+ mApfFilter = null;
+ }
+
resetLinkProperties();
}
@@ -839,6 +909,16 @@
handleLinkPropertiesUpdate(SEND_CALLBACKS);
break;
+ case CMD_SET_MULTICAST_FILTER: {
+ mMulticastFiltering = (boolean) msg.obj;
+ if (mApfFilter != null) {
+ mApfFilter.setMulticastFilter(mMulticastFiltering);
+ } else {
+ mCallback.setFallbackMulticastFilter(mMulticastFiltering);
+ }
+ break;
+ }
+
case DhcpClient.CMD_PRE_DHCP_ACTION:
if (VDBG) { Log.d(mTag, "onPreDhcpAction()"); }
if (mConfiguration.mRequestedPreDhcpAction) {
diff --git a/services/net/java/android/net/ip/IpReachabilityMonitor.java b/services/net/java/android/net/ip/IpReachabilityMonitor.java
index 5b4fd50..af3175a 100644
--- a/services/net/java/android/net/ip/IpReachabilityMonitor.java
+++ b/services/net/java/android/net/ip/IpReachabilityMonitor.java
@@ -24,6 +24,8 @@
import android.net.LinkProperties.ProvisioningChange;
import android.net.ProxyInfo;
import android.net.RouteInfo;
+import android.net.metrics.IpReachabilityMonitorMessageEvent;
+import android.net.metrics.IpReachabilityMonitorProbeEvent;
import android.net.netlink.NetlinkConstants;
import android.net.netlink.NetlinkErrorMessage;
import android.net.netlink.NetlinkMessage;
@@ -162,7 +164,7 @@
private boolean mRunning;
/**
- * Make the kernel to perform neighbor reachability detection (IPv4 ARP or IPv6 ND)
+ * Make the kernel perform neighbor reachability detection (IPv4 ARP or IPv6 ND)
* for the given IP address on the specified interface index.
*
* @return true, if the request was successfully passed to the kernel; false otherwise.
@@ -203,7 +205,8 @@
} catch (ErrnoException | InterruptedIOException | SocketException e) {
Log.d(TAG, "Error " + msgSnippet, e);
}
-
+ IpReachabilityMonitorProbeEvent.logEvent("ifindex-" + ifIndex, ip.getHostAddress(),
+ returnValue);
return returnValue;
}
@@ -400,8 +403,7 @@
return (numUnicastProbes * retransTimeMs) + gracePeriodMs;
}
-
- // TODO: simply the number of objects by making this extend Thread.
+ // TODO: simplify the number of objects by making this extend Thread.
private final class NetlinkSocketObserver implements Runnable {
private NetlinkSocket mSocket;
@@ -519,6 +521,8 @@
final short msgType = neighMsg.getHeader().nlmsg_type;
final short nudState = ndMsg.ndm_state;
+ IpReachabilityMonitorMessageEvent.logEvent(maybeGetInterfaceName(mInterfaceIndex),
+ destination.getHostAddress(), msgType, nudState);
final String eventMsg = "NeighborEvent{"
+ "elapsedMs=" + whenMs + ", "
+ destination.getHostAddress() + ", "
@@ -549,4 +553,11 @@
}
}
}
+
+ private String maybeGetInterfaceName(int index) {
+ if (index == mInterfaceIndex) {
+ return mInterfaceName;
+ }
+ return "ifindex-" + index;
+ }
}
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index 8cfc4a3..4d02928 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -41,12 +41,14 @@
import android.print.IPrintDocumentAdapter;
import android.print.IPrintJobStateChangeListener;
import android.print.IPrintManager;
+import android.printservice.recommendation.IRecommendationsChangeListener;
import android.print.IPrintServicesChangeListener;
import android.print.IPrinterDiscoveryObserver;
import android.print.PrintAttributes;
import android.print.PrintJobId;
import android.print.PrintJobInfo;
import android.print.PrintManager;
+import android.printservice.recommendation.RecommendationInfo;
import android.print.PrinterId;
import android.printservice.PrintServiceInfo;
import android.provider.Settings;
@@ -129,7 +131,7 @@
}
resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
resolvedPackageName = resolveCallingPackageNameEnforcingSecurity(packageName);
- userState = getOrCreateUserStateLocked(resolvedUserId);
+ userState = getOrCreateUserStateLocked(resolvedUserId, false);
}
final long identity = Binder.clearCallingIdentity();
try {
@@ -151,7 +153,7 @@
return null;
}
resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
- userState = getOrCreateUserStateLocked(resolvedUserId);
+ userState = getOrCreateUserStateLocked(resolvedUserId, false);
}
final long identity = Binder.clearCallingIdentity();
try {
@@ -176,7 +178,7 @@
return null;
}
resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
- userState = getOrCreateUserStateLocked(resolvedUserId);
+ userState = getOrCreateUserStateLocked(resolvedUserId, false);
}
final long identity = Binder.clearCallingIdentity();
try {
@@ -197,7 +199,7 @@
if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
return null;
}
- userState = getOrCreateUserStateLocked(resolvedUserId);
+ userState = getOrCreateUserStateLocked(resolvedUserId, false);
}
final long identity = Binder.clearCallingIdentity();
try {
@@ -222,7 +224,7 @@
return;
}
resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
- userState = getOrCreateUserStateLocked(resolvedUserId);
+ userState = getOrCreateUserStateLocked(resolvedUserId, false);
}
final long identity = Binder.clearCallingIdentity();
try {
@@ -247,7 +249,7 @@
return;
}
resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
- userState = getOrCreateUserStateLocked(resolvedUserId);
+ userState = getOrCreateUserStateLocked(resolvedUserId, false);
}
final long identity = Binder.clearCallingIdentity();
try {
@@ -265,11 +267,11 @@
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
final UserState userState;
synchronized (mLock) {
- // Only the current group members can get enabled services.
+ // Only the current group members can get print services.
if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
return null;
}
- userState = getOrCreateUserStateLocked(resolvedUserId);
+ userState = getOrCreateUserStateLocked(resolvedUserId, false);
}
final long identity = Binder.clearCallingIdentity();
try {
@@ -303,7 +305,7 @@
if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
return;
}
- userState = getOrCreateUserStateLocked(resolvedUserId);
+ userState = getOrCreateUserStateLocked(resolvedUserId, false);
}
final long identity = Binder.clearCallingIdentity();
try {
@@ -314,6 +316,25 @@
}
@Override
+ public List<RecommendationInfo> getPrintServiceRecommendations(int userId) {
+ final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+ final UserState userState;
+ synchronized (mLock) {
+ // Only the current group members can get print service recommendations.
+ if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
+ return null;
+ }
+ userState = getOrCreateUserStateLocked(resolvedUserId, false);
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return userState.getPrintServiceRecommendations();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void createPrinterDiscoverySession(IPrinterDiscoveryObserver observer,
int userId) {
observer = Preconditions.checkNotNull(observer);
@@ -325,7 +346,7 @@
if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
return;
}
- userState = getOrCreateUserStateLocked(resolvedUserId);
+ userState = getOrCreateUserStateLocked(resolvedUserId, false);
}
final long identity = Binder.clearCallingIdentity();
try {
@@ -347,7 +368,7 @@
if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
return;
}
- userState = getOrCreateUserStateLocked(resolvedUserId);
+ userState = getOrCreateUserStateLocked(resolvedUserId, false);
}
final long identity = Binder.clearCallingIdentity();
try {
@@ -373,7 +394,7 @@
if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
return;
}
- userState = getOrCreateUserStateLocked(resolvedUserId);
+ userState = getOrCreateUserStateLocked(resolvedUserId, false);
}
final long identity = Binder.clearCallingIdentity();
try {
@@ -394,7 +415,7 @@
if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
return;
}
- userState = getOrCreateUserStateLocked(resolvedUserId);
+ userState = getOrCreateUserStateLocked(resolvedUserId, false);
}
final long identity = Binder.clearCallingIdentity();
try {
@@ -415,7 +436,7 @@
if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
return;
}
- userState = getOrCreateUserStateLocked(resolvedUserId);
+ userState = getOrCreateUserStateLocked(resolvedUserId, false);
}
final long identity = Binder.clearCallingIdentity();
try {
@@ -436,7 +457,7 @@
if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
return;
}
- userState = getOrCreateUserStateLocked(resolvedUserId);
+ userState = getOrCreateUserStateLocked(resolvedUserId, false);
}
final long identity = Binder.clearCallingIdentity();
try {
@@ -457,7 +478,7 @@
if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
return;
}
- userState = getOrCreateUserStateLocked(resolvedUserId);
+ userState = getOrCreateUserStateLocked(resolvedUserId, false);
}
final long identity = Binder.clearCallingIdentity();
try {
@@ -481,7 +502,7 @@
return;
}
resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
- userState = getOrCreateUserStateLocked(resolvedUserId);
+ userState = getOrCreateUserStateLocked(resolvedUserId, false);
}
final long identity = Binder.clearCallingIdentity();
try {
@@ -503,7 +524,7 @@
if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
return;
}
- userState = getOrCreateUserStateLocked(resolvedUserId);
+ userState = getOrCreateUserStateLocked(resolvedUserId, false);
}
final long identity = Binder.clearCallingIdentity();
try {
@@ -525,7 +546,7 @@
if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
return;
}
- userState = getOrCreateUserStateLocked(resolvedUserId);
+ userState = getOrCreateUserStateLocked(resolvedUserId, false);
}
final long identity = Binder.clearCallingIdentity();
try {
@@ -543,11 +564,11 @@
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
final UserState userState;
synchronized (mLock) {
- // Only the current group members can remove a print job listener.
+ // Only the current group members can remove a print services change listener.
if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
return;
}
- userState = getOrCreateUserStateLocked(resolvedUserId);
+ userState = getOrCreateUserStateLocked(resolvedUserId, false);
}
final long identity = Binder.clearCallingIdentity();
try {
@@ -558,6 +579,52 @@
}
@Override
+ public void addPrintServiceRecommendationsChangeListener(
+ IRecommendationsChangeListener listener, int userId)
+ throws RemoteException {
+ listener = Preconditions.checkNotNull(listener);
+
+ final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+ final UserState userState;
+ synchronized (mLock) {
+ // Only the current group members can add a print service recommendations listener.
+ if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
+ return;
+ }
+ userState = getOrCreateUserStateLocked(resolvedUserId, false);
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ userState.addPrintServiceRecommendationsChangeListener(listener);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void removePrintServiceRecommendationsChangeListener(
+ IRecommendationsChangeListener listener, int userId) {
+ listener = Preconditions.checkNotNull(listener);
+
+ final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+ final UserState userState;
+ synchronized (mLock) {
+ // Only the current group members can remove a print service recommendations
+ // listener.
+ if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
+ return;
+ }
+ userState = getOrCreateUserStateLocked(resolvedUserId, false);
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ userState.removePrintServiceRecommendationsChangeListener(listener);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
fd = Preconditions.checkNotNull(fd);
pw = Preconditions.checkNotNull(pw);
@@ -661,7 +728,7 @@
@Override
public void onPackageModified(String packageName) {
if (!mUserManager.isUserUnlocked(getChangingUserId())) return;
- UserState userState = getOrCreateUserStateLocked(getChangingUserId());
+ UserState userState = getOrCreateUserStateLocked(getChangingUserId(), false);
synchronized (mLock) {
if (hadPrintService(userState, packageName)
@@ -676,7 +743,7 @@
@Override
public void onPackageRemoved(String packageName, int uid) {
if (!mUserManager.isUserUnlocked(getChangingUserId())) return;
- UserState userState = getOrCreateUserStateLocked(getChangingUserId());
+ UserState userState = getOrCreateUserStateLocked(getChangingUserId(), false);
synchronized (mLock) {
if (hadPrintService(userState, packageName)) {
@@ -695,7 +762,8 @@
// A background user/profile's print jobs are running but there is
// no UI shown. Hence, if the packages of such a user change we need
// to handle it as the change may affect ongoing print jobs.
- UserState userState = getOrCreateUserStateLocked(getChangingUserId());
+ UserState userState = getOrCreateUserStateLocked(getChangingUserId(),
+ false);
boolean stoppedSomePackages = false;
List<PrintServiceInfo> enabledServices = userState
@@ -730,7 +798,8 @@
if (!mUserManager.isUserUnlocked(getChangingUserId())) return;
synchronized (mLock) {
if (hasPrintService(packageName)) {
- UserState userState = getOrCreateUserStateLocked(getChangingUserId());
+ UserState userState = getOrCreateUserStateLocked(getChangingUserId(),
+ false);
userState.updateIfNeededLocked();
}
}
@@ -742,7 +811,7 @@
UserHandle.ALL, true);
}
- private UserState getOrCreateUserStateLocked(int userId) {
+ private UserState getOrCreateUserStateLocked(int userId, boolean lowPriority) {
if (!mUserManager.isUserUnlocked(userId)) {
throw new IllegalStateException(
"User " + userId + " must be unlocked for printing to be available");
@@ -750,9 +819,14 @@
UserState userState = mUserStates.get(userId);
if (userState == null) {
- userState = new UserState(mContext, userId, mLock);
+ userState = new UserState(mContext, userId, mLock, lowPriority);
mUserStates.put(userId, userState);
}
+
+ if (!lowPriority) {
+ userState.increasePriority();
+ }
+
return userState;
}
@@ -764,7 +838,7 @@
public void run() {
UserState userState;
synchronized (mLock) {
- userState = getOrCreateUserStateLocked(userId);
+ userState = getOrCreateUserStateLocked(userId, true);
userState.updateIfNeededLocked();
}
// This is the first time we switch to this user after boot, so
diff --git a/services/print/java/com/android/server/print/RemotePrintService.java b/services/print/java/com/android/server/print/RemotePrintService.java
index 9b99c67..9c3a852 100644
--- a/services/print/java/com/android/server/print/RemotePrintService.java
+++ b/services/print/java/com/android/server/print/RemotePrintService.java
@@ -19,6 +19,7 @@
import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.StringRes;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -813,6 +814,20 @@
}
@Override
+ public void setStatusRes(@NonNull PrintJobId printJobId, @StringRes int status,
+ @NonNull CharSequence appPackageName) {
+ RemotePrintService service = mWeakService.get();
+ if (service != null) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ service.mSpooler.setStatus(printJobId, status, appPackageName);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
+ @Override
@SuppressWarnings({"rawtypes", "unchecked"})
public void onPrintersAdded(ParceledListSlice printers) {
RemotePrintService service = mWeakService.get();
diff --git a/services/print/java/com/android/server/print/RemotePrintServiceRecommendationService.java b/services/print/java/com/android/server/print/RemotePrintServiceRecommendationService.java
new file mode 100644
index 0000000..fa1f232
--- /dev/null
+++ b/services/print/java/com/android/server/print/RemotePrintServiceRecommendationService.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.print;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ResolveInfo;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.printservice.recommendation.IRecommendationService;
+import android.printservice.recommendation.IRecommendationServiceCallbacks;
+import android.printservice.recommendation.RecommendationInfo;
+import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
+
+import java.util.List;
+
+import static android.content.pm.PackageManager.GET_META_DATA;
+import static android.content.pm.PackageManager.GET_SERVICES;
+import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
+
+/**
+ * Connection to a remote print service recommendation service.
+ */
+class RemotePrintServiceRecommendationService {
+ private static final String LOG_TAG = "RemotePrintServiceRecS";
+
+ /** Lock for this object */
+ private final Object mLock = new Object();
+
+ /** Context used for the connection */
+ private @NonNull final Context mContext;
+
+ /** The connection to the service (if {@link #mIsBound bound}) */
+ @GuardedBy("mLock")
+ private @NonNull final Connection mConnection;
+
+ /** If the service is currently bound. */
+ @GuardedBy("mLock")
+ private boolean mIsBound;
+
+ /** The service once bound */
+ @GuardedBy("mLock")
+ private IRecommendationService mService;
+
+ /**
+ * Callbacks to be called when there are updates to the print service recommendations.
+ */
+ public interface RemotePrintServiceRecommendationServiceCallbacks {
+ /**
+ * Called when there is an update list of print service recommendations.
+ *
+ * @param recommendations The new recommendations.
+ */
+ void onPrintServiceRecommendationsUpdated(
+ @Nullable List<RecommendationInfo> recommendations);
+ }
+
+ /**
+ * @return The intent that is used to connect to the print service recommendation service.
+ */
+ private Intent getServiceIntent(@NonNull UserHandle userHandle) throws Exception {
+ List<ResolveInfo> installedServices = mContext.getPackageManager()
+ .queryIntentServicesAsUser(new Intent(
+ android.printservice.recommendation.RecommendationService.SERVICE_INTERFACE),
+ GET_SERVICES | GET_META_DATA | MATCH_DEBUG_TRIAGED_MISSING,
+ userHandle.getIdentifier());
+
+ if (installedServices.size() != 1) {
+ throw new Exception(installedServices.size() + " instead of exactly one service found");
+ }
+
+ ResolveInfo installedService = installedServices.get(0);
+
+ ComponentName serviceName = new ComponentName(
+ installedService.serviceInfo.packageName,
+ installedService.serviceInfo.name);
+
+ ApplicationInfo appInfo = mContext.getPackageManager()
+ .getApplicationInfo(installedService.serviceInfo.packageName, 0);
+
+ if (appInfo == null) {
+ throw new Exception("Cannot read appInfo for service");
+ }
+
+ if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ throw new Exception("Service is not part of the system");
+ }
+
+ if (!android.Manifest.permission.BIND_PRINT_RECOMMENDATION_SERVICE.equals(
+ installedService.serviceInfo.permission)) {
+ throw new Exception("Service " + serviceName.flattenToShortString()
+ + " does not require permission "
+ + android.Manifest.permission.BIND_PRINT_RECOMMENDATION_SERVICE);
+ }
+
+ Intent serviceIntent = new Intent();
+ serviceIntent.setComponent(serviceName);
+
+ return serviceIntent;
+ }
+
+ /**
+ * Open a new connection to a {@link IRecommendationService remote print service
+ * recommendation service}.
+ *
+ * @param context The context establishing the connection
+ * @param userHandle The user the connection is for
+ * @param callbacks The callbacks to call by the service
+ */
+ RemotePrintServiceRecommendationService(@NonNull Context context,
+ @NonNull UserHandle userHandle,
+ @NonNull RemotePrintServiceRecommendationServiceCallbacks callbacks) {
+ mContext = context;
+ mConnection = new Connection(callbacks);
+
+ try {
+ Intent serviceIntent = getServiceIntent(userHandle);
+
+ synchronized (mLock) {
+ mIsBound = mContext.bindServiceAsUser(serviceIntent, mConnection,
+ Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, userHandle);
+
+ if (!mIsBound) {
+ throw new Exception("Failed to bind to service " + serviceIntent);
+ }
+ }
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Could not connect to print service recommendation service", e);
+ }
+ }
+
+ /**
+ * Terminate the connection to the {@link IRecommendationService remote print
+ * service recommendation service}.
+ */
+ void close() {
+ synchronized (mLock) {
+ if (mService != null) {
+ try {
+ mService.registerCallbacks(null);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Could not unregister callbacks", e);
+ }
+
+ mService = null;
+ }
+
+ if (mIsBound) {
+ mContext.unbindService(mConnection);
+ mIsBound = false;
+ }
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ if (mIsBound || mService != null) {
+ Log.w(LOG_TAG, "Service still connected on finalize()");
+ close();
+ }
+
+ super.finalize();
+ }
+
+ /**
+ * Connection to the service.
+ */
+ private class Connection implements ServiceConnection {
+ private final RemotePrintServiceRecommendationServiceCallbacks mCallbacks;
+
+ public Connection(@NonNull RemotePrintServiceRecommendationServiceCallbacks callbacks) {
+ mCallbacks = callbacks;
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ synchronized (mLock) {
+ mService = (IRecommendationService)IRecommendationService.Stub.asInterface(service);
+
+ try {
+ mService.registerCallbacks(new IRecommendationServiceCallbacks.Stub() {
+ @Override
+ public void onRecommendationsUpdated(
+ List<RecommendationInfo> recommendations) {
+ synchronized (mLock) {
+ if (mIsBound && mService != null) {
+ if (recommendations != null) {
+ Preconditions.checkCollectionElementsNotNull(
+ recommendations, "recommendation");
+ }
+
+ mCallbacks.onPrintServiceRecommendationsUpdated(
+ recommendations);
+ }
+ }
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Could not register callbacks", e);
+ }
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Log.w(LOG_TAG, "Unexpected termination of connection");
+
+ synchronized (mLock) {
+ mService = null;
+ }
+ }
+ }
+}
diff --git a/services/print/java/com/android/server/print/RemotePrintSpooler.java b/services/print/java/com/android/server/print/RemotePrintSpooler.java
index e1d8c6c..07cc9c0 100644
--- a/services/print/java/com/android/server/print/RemotePrintSpooler.java
+++ b/services/print/java/com/android/server/print/RemotePrintSpooler.java
@@ -19,6 +19,7 @@
import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.StringRes;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -97,6 +98,8 @@
private final PrintSpoolerCallbacks mCallbacks;
+ private boolean mIsLowPriority;
+
private IPrintSpooler mRemoteInstance;
private boolean mDestroyed;
@@ -109,17 +112,42 @@
public void onPrintJobStateChanged(PrintJobInfo printJob);
}
- public RemotePrintSpooler(Context context, int userId,
+ public RemotePrintSpooler(Context context, int userId, boolean lowPriority,
PrintSpoolerCallbacks callbacks) {
mContext = context;
mUserHandle = new UserHandle(userId);
mCallbacks = callbacks;
+ mIsLowPriority = lowPriority;
mClient = new PrintSpoolerClient(this);
mIntent = new Intent();
mIntent.setComponent(new ComponentName(PrintManager.PRINT_SPOOLER_PACKAGE_NAME,
PrintManager.PRINT_SPOOLER_PACKAGE_NAME + ".model.PrintSpoolerService"));
}
+ public void increasePriority() {
+ if (mIsLowPriority) {
+ mIsLowPriority = false;
+
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+
+ while (!mCanUnbind) {
+ try {
+ mLock.wait();
+ } catch (InterruptedException e) {
+ Slog.e(LOG_TAG, "Interrupted while waiting for operation to complete");
+ }
+ }
+
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Unbinding as previous binding was low priority");
+ }
+
+ unbindLocked();
+ }
+ }
+ }
+
public final List<PrintJobInfo> getPrintJobInfos(ComponentName componentName, int state,
int appId) {
throwIfCalledOnMainThread();
@@ -301,6 +329,35 @@
}
/**
+ * Set status of a print job.
+ *
+ * @param printJobId The print job to update
+ * @param status The new status as a string resource
+ * @param appPackageName The app package name the string res belongs to
+ */
+ public final void setStatus(@NonNull PrintJobId printJobId, @StringRes int status,
+ @NonNull CharSequence appPackageName) {
+ throwIfCalledOnMainThread();
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ mCanUnbind = false;
+ }
+ try {
+ getRemoteInstanceLazy().setStatusRes(printJobId, status, appPackageName);
+ } catch (RemoteException|TimeoutException re) {
+ Slog.e(LOG_TAG, "Error setting status.", re);
+ } finally {
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] setStatus()");
+ }
+ synchronized (mLock) {
+ mCanUnbind = true;
+ mLock.notifyAll();
+ }
+ }
+ }
+
+ /**
* Handle that a custom icon for a printer was loaded.
*
* @param printerId the id of the printer the icon belongs to
@@ -549,11 +606,18 @@
return;
}
if (DEBUG) {
- Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] bindLocked()");
+ Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] bindLocked() " +
+ (mIsLowPriority ? "low priority" : ""));
}
- mContext.bindServiceAsUser(mIntent, mServiceConnection,
- Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, mUserHandle);
+ int flags;
+ if (mIsLowPriority) {
+ flags = Context.BIND_AUTO_CREATE;
+ } else {
+ flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE;
+ }
+
+ mContext.bindServiceAsUser(mIntent, mServiceConnection, flags, mUserHandle);
final long startMillis = SystemClock.uptimeMillis();
while (true) {
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index 8ab1878..026942e 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -37,6 +37,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
+import android.os.IInterface;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteCallbackList;
@@ -44,12 +45,14 @@
import android.os.UserHandle;
import android.print.IPrintDocumentAdapter;
import android.print.IPrintJobStateChangeListener;
+import android.printservice.recommendation.IRecommendationsChangeListener;
import android.print.IPrintServicesChangeListener;
import android.print.IPrinterDiscoveryObserver;
import android.print.PrintAttributes;
import android.print.PrintJobId;
import android.print.PrintJobInfo;
import android.print.PrintManager;
+import android.printservice.recommendation.RecommendationInfo;
import android.print.PrinterId;
import android.print.PrinterInfo;
import android.printservice.PrintServiceInfo;
@@ -68,6 +71,7 @@
import com.android.internal.os.SomeArgs;
import com.android.server.print.RemotePrintService.PrintServiceCallbacks;
import com.android.server.print.RemotePrintSpooler.PrintSpoolerCallbacks;
+import com.android.server.print.RemotePrintServiceRecommendationService.RemotePrintServiceRecommendationServiceCallbacks;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -82,7 +86,8 @@
/**
* Represents the print state for a user.
*/
-final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks {
+final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks,
+ RemotePrintServiceRecommendationServiceCallbacks {
private static final String LOG_TAG = "UserState";
@@ -122,15 +127,27 @@
private List<PrintJobStateChangeListenerRecord> mPrintJobStateChangeListenerRecords;
- private List<PrintServicesChangeListenerRecord> mPrintServicesChangeListenerRecords;
+ private List<ListenerRecord<IPrintServicesChangeListener>> mPrintServicesChangeListenerRecords;
+
+ private List<ListenerRecord<IRecommendationsChangeListener>>
+ mPrintServiceRecommendationsChangeListenerRecords;
private boolean mDestroyed;
- public UserState(Context context, int userId, Object lock) {
+ /** Currently known list of print service recommendations */
+ private List<RecommendationInfo> mPrintServiceRecommendations;
+
+ /**
+ * Connection to the service updating the {@link #mPrintServiceRecommendations print service
+ * recommendations}.
+ */
+ private RemotePrintServiceRecommendationService mPrintServiceRecommendationsService;
+
+ public UserState(Context context, int userId, Object lock, boolean lowPriority) {
mContext = context;
mUserId = userId;
mLock = lock;
- mSpooler = new RemotePrintSpooler(context, userId, this);
+ mSpooler = new RemotePrintSpooler(context, userId, lowPriority, this);
mHandler = new UserStateHandler(context.getMainLooper());
synchronized (mLock) {
@@ -145,6 +162,10 @@
}
}
+ public void increasePriority() {
+ mSpooler.increasePriority();
+ }
+
@Override
public void onPrintJobQueued(PrintJobInfo printJob) {
final RemotePrintService service;
@@ -405,6 +426,13 @@
}
}
+ /**
+ * @return The currently known print service recommendations
+ */
+ public @Nullable List<RecommendationInfo> getPrintServiceRecommendations() {
+ return mPrintServiceRecommendations;
+ }
+
public void createPrinterDiscoverySession(@NonNull IPrinterDiscoveryObserver observer) {
synchronized (mLock) {
throwIfDestroyedLocked();
@@ -562,7 +590,7 @@
mPrintServicesChangeListenerRecords = new ArrayList<>();
}
mPrintServicesChangeListenerRecords.add(
- new PrintServicesChangeListenerRecord(listener) {
+ new ListenerRecord<IPrintServicesChangeListener>(listener) {
@Override
public void onBinderDied() {
mPrintServicesChangeListenerRecords.remove(this);
@@ -579,7 +607,7 @@
}
final int recordCount = mPrintServicesChangeListenerRecords.size();
for (int i = 0; i < recordCount; i++) {
- PrintServicesChangeListenerRecord record =
+ ListenerRecord<IPrintServicesChangeListener> record =
mPrintServicesChangeListenerRecords.get(i);
if (record.listener.asBinder().equals(listener.asBinder())) {
mPrintServicesChangeListenerRecords.remove(i);
@@ -592,6 +620,54 @@
}
}
+ public void addPrintServiceRecommendationsChangeListener(
+ @NonNull IRecommendationsChangeListener listener) throws RemoteException {
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ if (mPrintServiceRecommendationsChangeListenerRecords == null) {
+ mPrintServiceRecommendationsChangeListenerRecords = new ArrayList<>();
+
+ mPrintServiceRecommendationsService =
+ new RemotePrintServiceRecommendationService(mContext,
+ UserHandle.getUserHandleForUid(mUserId), this);
+ }
+ mPrintServiceRecommendationsChangeListenerRecords.add(
+ new ListenerRecord<IRecommendationsChangeListener>(listener) {
+ @Override
+ public void onBinderDied() {
+ mPrintServiceRecommendationsChangeListenerRecords.remove(this);
+ }
+ });
+ }
+ }
+
+ public void removePrintServiceRecommendationsChangeListener(
+ @NonNull IRecommendationsChangeListener listener) {
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ if (mPrintServiceRecommendationsChangeListenerRecords == null) {
+ return;
+ }
+ final int recordCount = mPrintServiceRecommendationsChangeListenerRecords.size();
+ for (int i = 0; i < recordCount; i++) {
+ ListenerRecord<IRecommendationsChangeListener> record =
+ mPrintServiceRecommendationsChangeListenerRecords.get(i);
+ if (record.listener.asBinder().equals(listener.asBinder())) {
+ mPrintServiceRecommendationsChangeListenerRecords.remove(i);
+ break;
+ }
+ }
+ if (mPrintServiceRecommendationsChangeListenerRecords.isEmpty()) {
+ mPrintServiceRecommendationsChangeListenerRecords = null;
+
+ mPrintServiceRecommendations = null;
+
+ mPrintServiceRecommendationsService.close();
+ mPrintServiceRecommendationsService = null;
+ }
+ }
+ }
+
@Override
public void onPrintJobStateChanged(PrintJobInfo printJob) {
mPrintJobForAppCache.onPrintJobStateChanged(printJob);
@@ -604,6 +680,12 @@
}
@Override
+ public void onPrintServiceRecommendationsUpdated(List<RecommendationInfo> recommendations) {
+ mHandler.obtainMessage(UserStateHandler.MSG_DISPATCH_PRINT_SERVICES_RECOMMENDATIONS_UPDATED,
+ 0, 0, recommendations).sendToTarget();
+ }
+
+ @Override
public void onPrintersAdded(List<PrinterInfo> printers) {
synchronized (mLock) {
throwIfDestroyedLocked();
@@ -1054,7 +1136,7 @@
}
private void handleDispatchPrintServicesChanged() {
- final List<PrintServicesChangeListenerRecord> records;
+ final List<ListenerRecord<IPrintServicesChangeListener>> records;
synchronized (mLock) {
if (mPrintServicesChangeListenerRecords == null) {
return;
@@ -1063,7 +1145,7 @@
}
final int recordCount = records.size();
for (int i = 0; i < recordCount; i++) {
- PrintServicesChangeListenerRecord record = records.get(i);
+ ListenerRecord<IPrintServicesChangeListener> record = records.get(i);
try {
record.listener.onPrintServicesChanged();;
@@ -1073,9 +1155,33 @@
}
}
+ private void handleDispatchPrintServiceRecommendationsUpdated(
+ @Nullable List<RecommendationInfo> recommendations) {
+ final List<ListenerRecord<IRecommendationsChangeListener>> records;
+ synchronized (mLock) {
+ if (mPrintServiceRecommendationsChangeListenerRecords == null) {
+ return;
+ }
+ records = new ArrayList<>(mPrintServiceRecommendationsChangeListenerRecords);
+
+ mPrintServiceRecommendations = recommendations;
+ }
+ final int recordCount = records.size();
+ for (int i = 0; i < recordCount; i++) {
+ ListenerRecord<IRecommendationsChangeListener> record = records.get(i);
+
+ try {
+ record.listener.onRecommendationsChanged();
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error notifying for print service recommendations change", re);
+ }
+ }
+ }
+
private final class UserStateHandler extends Handler {
public static final int MSG_DISPATCH_PRINT_JOB_STATE_CHANGED = 1;
public static final int MSG_DISPATCH_PRINT_SERVICES_CHANGED = 2;
+ public static final int MSG_DISPATCH_PRINT_SERVICES_RECOMMENDATIONS_UPDATED = 3;
public UserStateHandler(Looper looper) {
super(looper, null, false);
@@ -1092,6 +1198,10 @@
case MSG_DISPATCH_PRINT_SERVICES_CHANGED:
handleDispatchPrintServicesChanged();
break;
+ case MSG_DISPATCH_PRINT_SERVICES_RECOMMENDATIONS_UPDATED:
+ handleDispatchPrintServiceRecommendationsUpdated(
+ (List<RecommendationInfo>) message.obj);
+ break;
default:
// not reached
}
@@ -1118,10 +1228,10 @@
public abstract void onBinderDied();
}
- private abstract class PrintServicesChangeListenerRecord implements DeathRecipient {
- @NonNull final IPrintServicesChangeListener listener;
+ private abstract class ListenerRecord<T extends IInterface> implements DeathRecipient {
+ @NonNull final T listener;
- public PrintServicesChangeListenerRecord(@NonNull IPrintServicesChangeListener listener) throws RemoteException {
+ public ListenerRecord(@NonNull T listener) throws RemoteException {
this.listener = listener;
listener.asBinder().linkToDeath(this, 0);
}
diff --git a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
index c322ab8..f8eaf7d 100644
--- a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
+++ b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
@@ -261,7 +261,7 @@
private void assertDhcpResults(String ipAddress, String gateway, String dnsServersString,
String domains, String serverAddress, String vendorInfo, int leaseDuration,
- boolean hasMeteredHint, DhcpResults dhcpResults) throws Exception {
+ boolean hasMeteredHint, int mtu, DhcpResults dhcpResults) throws Exception {
assertEquals(new LinkAddress(ipAddress), dhcpResults.ipAddress);
assertEquals(v4Address(gateway), dhcpResults.gateway);
@@ -277,6 +277,7 @@
assertEquals(vendorInfo, dhcpResults.vendorInfo);
assertEquals(leaseDuration, dhcpResults.leaseDuration);
assertEquals(hasMeteredHint, dhcpResults.hasMeteredHint());
+ assertEquals(mtu, dhcpResults.mtu);
}
@SmallTest
@@ -310,7 +311,7 @@
assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null.
DhcpResults dhcpResults = offerPacket.toDhcpResults();
assertDhcpResults("192.168.159.247/20", "192.168.159.254", "8.8.8.8,8.8.4.4",
- null, "192.168.144.3", null, 7200, false, dhcpResults);
+ null, "192.168.144.3", null, 7200, false, 0, dhcpResults);
}
@SmallTest
@@ -342,10 +343,70 @@
assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null.
DhcpResults dhcpResults = offerPacket.toDhcpResults();
assertDhcpResults("192.168.43.247/24", "192.168.43.1", "192.168.43.1",
- null, "192.168.43.1", "ANDROID_METERED", 3600, true, dhcpResults);
+ null, "192.168.43.1", "ANDROID_METERED", 3600, true, 0, dhcpResults);
assertTrue(dhcpResults.hasMeteredHint());
}
+ private byte[] mtuBytes(int mtu) {
+ // 0x1a02: option 26, length 2. 0xff: no more options.
+ if (mtu > Short.MAX_VALUE - Short.MIN_VALUE) {
+ throw new IllegalArgumentException(
+ String.format("Invalid MTU %d, must be 16-bit unsigned", mtu));
+ }
+ String hexString = String.format("1a02%04xff", mtu);
+ return HexEncoding.decode(hexString.toCharArray(), false);
+ }
+
+ private void checkMtu(ByteBuffer packet, int expectedMtu, byte[] mtuBytes) throws Exception {
+ if (mtuBytes != null) {
+ packet.position(packet.capacity() - mtuBytes.length);
+ packet.put(mtuBytes);
+ packet.clear();
+ }
+ DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
+ assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null.
+ DhcpResults dhcpResults = offerPacket.toDhcpResults();
+ assertDhcpResults("192.168.159.247/20", "192.168.159.254", "8.8.8.8,8.8.4.4",
+ null, "192.168.144.3", null, 7200, false, expectedMtu, dhcpResults);
+ }
+
+ @SmallTest
+ public void testMtu() throws Exception {
+ final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
+ // IP header.
+ "451001480000000080118849c0a89003c0a89ff7" +
+ // UDP header.
+ "004300440134dcfa" +
+ // BOOTP header.
+ "02010600c997a63b0000000000000000c0a89ff70000000000000000" +
+ // MAC address.
+ "30766ff2a90c00000000000000000000" +
+ // Server name.
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ // File.
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ // Options
+ "638253633501023604c0a89003330400001c200104fffff0000304c0a89ffe06080808080808080404" +
+ "3a0400000e103b040000189cff00000000"
+ ).toCharArray(), false));
+
+ checkMtu(packet, 0, null);
+ checkMtu(packet, 0, mtuBytes(1501));
+ checkMtu(packet, 1500, mtuBytes(1500));
+ checkMtu(packet, 1499, mtuBytes(1499));
+ checkMtu(packet, 1280, mtuBytes(1280));
+ checkMtu(packet, 0, mtuBytes(1279));
+ checkMtu(packet, 0, mtuBytes(576));
+ checkMtu(packet, 0, mtuBytes(68));
+ checkMtu(packet, 0, mtuBytes(Short.MIN_VALUE));
+ checkMtu(packet, 0, mtuBytes(Short.MAX_VALUE + 3));
+ checkMtu(packet, 0, mtuBytes(-1));
+ }
+
@SmallTest
public void testBadHwaddrLength() throws Exception {
final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
@@ -453,7 +514,7 @@
assertTrue(offerPacket instanceof DhcpOfferPacket);
DhcpResults dhcpResults = offerPacket.toDhcpResults();
assertDhcpResults("172.17.152.118/16", "172.17.1.1", "172.17.1.1",
- null, "1.1.1.1", null, 43200, false, dhcpResults);
+ null, "1.1.1.1", null, 43200, false, 0, dhcpResults);
}
@SmallTest
@@ -484,7 +545,7 @@
assertTrue(offerPacket instanceof DhcpOfferPacket);
DhcpResults dhcpResults = offerPacket.toDhcpResults();
assertDhcpResults("10.63.93.4/20", "10.63.80.1", "192.0.2.1,192.0.2.2",
- "domain123.co.uk", "192.0.2.254", null, 49094, false, dhcpResults);
+ "domain123.co.uk", "192.0.2.254", null, 49094, false, 0, dhcpResults);
}
@SmallTest
@@ -518,7 +579,7 @@
assertEquals("BCF5AC000000", HexDump.toHexString(offerPacket.getClientMac()));
DhcpResults dhcpResults = offerPacket.toDhcpResults();
assertDhcpResults("10.32.158.205/20", "10.32.144.1", "148.88.65.52,148.88.65.53",
- "lancs.ac.uk", "10.32.255.128", null, 7200, false, dhcpResults);
+ "lancs.ac.uk", "10.32.255.128", null, 7200, false, 0, dhcpResults);
}
@SmallTest
@@ -554,7 +615,7 @@
DhcpResults dhcpResults = offerPacket.toDhcpResults();
assertDhcpResults("10.15.122.242/16", "10.15.200.23",
"209.129.128.3,209.129.148.3,209.129.128.6",
- "wvm.edu", "10.1.105.252", null, 86400, false, dhcpResults);
+ "wvm.edu", "10.1.105.252", null, 86400, false, 0, dhcpResults);
}
@SmallTest
@@ -621,7 +682,7 @@
assertEquals("FC3D93000000", HexDump.toHexString(offerPacket.getClientMac()));
DhcpResults dhcpResults = offerPacket.toDhcpResults();
assertDhcpResults("192.168.189.49/24", "192.168.189.1", "8.8.8.8,8.8.4.4",
- null, "192.171.189.2", null, 28800, false, dhcpResults);
+ null, "192.171.189.2", null, 28800, false, 0, dhcpResults);
}
@SmallTest
@@ -631,18 +692,10 @@
byte[] hwaddr = {
(byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, (byte) 0xb1, (byte) 0x7a
};
- byte[] params = new byte[] {
- DHCP_SUBNET_MASK,
- DHCP_ROUTER,
- DHCP_DNS_SERVER,
- DHCP_DOMAIN_NAME,
- DHCP_MTU,
- DHCP_LEASE_TIME,
- };
ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
DhcpPacket.ENCAP_L2, transactionId, secs, hwaddr,
- false /* do unicast */, params);
+ false /* do unicast */, DhcpClient.REQUESTED_PARAMS);
byte[] headers = new byte[] {
// Ethernet header.
@@ -650,14 +703,14 @@
(byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, (byte) 0xb1, (byte) 0x7a,
(byte) 0x08, (byte) 0x00,
// IP header.
- (byte) 0x45, (byte) 0x10, (byte) 0x01, (byte) 0x52,
+ (byte) 0x45, (byte) 0x10, (byte) 0x01, (byte) 0x56,
(byte) 0x00, (byte) 0x00, (byte) 0x40, (byte) 0x00,
- (byte) 0x40, (byte) 0x11, (byte) 0x39, (byte) 0x8c,
+ (byte) 0x40, (byte) 0x11, (byte) 0x39, (byte) 0x88,
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
// UDP header.
(byte) 0x00, (byte) 0x44, (byte) 0x00, (byte) 0x43,
- (byte) 0x01, (byte) 0x3e, (byte) 0xd8, (byte) 0xa4,
+ (byte) 0x01, (byte) 0x42, (byte) 0x6a, (byte) 0x4a,
// BOOTP.
(byte) 0x01, (byte) 0x01, (byte) 0x06, (byte) 0x00,
(byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef,
@@ -688,13 +741,17 @@
'a', 'n', 'd', 'r', 'o', 'i', 'd', '-',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'a', 'b', 'c', 'd', 'e',
// Requested parameter list.
- (byte) 0x37, (byte) 0x06,
+ (byte) 0x37, (byte) 0x0a,
DHCP_SUBNET_MASK,
DHCP_ROUTER,
DHCP_DNS_SERVER,
DHCP_DOMAIN_NAME,
DHCP_MTU,
+ DHCP_BROADCAST_ADDRESS,
DHCP_LEASE_TIME,
+ DHCP_RENEWAL_TIME,
+ DHCP_REBINDING_TIME,
+ DHCP_VENDOR_INFO,
// End options.
(byte) 0xff,
// Our packets are always of even length. TODO: find out why and possibly fix it.
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 72a458b..622e46e 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -313,7 +313,7 @@
public void testScreenChangesRules() throws Exception {
Future<Void> future;
- expectSetUidNetworkRules(UID_A, false);
+ expectSetUidMeteredNetworkBlacklist(UID_A, false);
expectSetUidForeground(UID_A, true);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
@@ -322,7 +322,7 @@
verifyAndReset();
// push strict policy for foreground uid, verify ALLOW rule
- expectSetUidNetworkRules(UID_A, false);
+ expectSetUidMeteredNetworkBlacklist(UID_A, false);
expectSetUidForeground(UID_A, true);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
@@ -332,7 +332,7 @@
// now turn screen off and verify REJECT rule
expect(mPowerManager.isInteractive()).andReturn(false).atLeastOnce();
- expectSetUidNetworkRules(UID_A, true);
+ expectSetUidMeteredNetworkBlacklist(UID_A, true);
expectSetUidForeground(UID_A, false);
future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
replay();
@@ -342,7 +342,7 @@
// and turn screen back on, verify ALLOW rule restored
expect(mPowerManager.isInteractive()).andReturn(true).atLeastOnce();
- expectSetUidNetworkRules(UID_A, false);
+ expectSetUidMeteredNetworkBlacklist(UID_A, false);
expectSetUidForeground(UID_A, true);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
@@ -354,7 +354,7 @@
public void testPolicyNone() throws Exception {
Future<Void> future;
- expectSetUidNetworkRules(UID_A, false);
+ expectSetUidMeteredNetworkBlacklist(UID_A, false);
expectSetUidForeground(UID_A, true);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
@@ -363,7 +363,7 @@
verifyAndReset();
// POLICY_NONE should RULE_ALLOW in foreground
- expectSetUidNetworkRules(UID_A, false);
+ expectSetUidMeteredNetworkBlacklist(UID_A, false);
expectSetUidForeground(UID_A, true);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
@@ -372,7 +372,7 @@
verifyAndReset();
// POLICY_NONE should RULE_ALLOW in background
- expectSetUidNetworkRules(UID_A, false);
+ expectSetUidMeteredNetworkBlacklist(UID_A, false);
expectSetUidForeground(UID_A, false);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
@@ -385,7 +385,7 @@
Future<Void> future;
// POLICY_REJECT should RULE_ALLOW in background
- expectSetUidNetworkRules(UID_A, true);
+ expectSetUidMeteredNetworkBlacklist(UID_A, true);
expectSetUidForeground(UID_A, false);
future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
replay();
@@ -394,7 +394,7 @@
verifyAndReset();
// POLICY_REJECT should RULE_ALLOW in foreground
- expectSetUidNetworkRules(UID_A, false);
+ expectSetUidMeteredNetworkBlacklist(UID_A, false);
expectSetUidForeground(UID_A, true);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
@@ -403,7 +403,7 @@
verifyAndReset();
// POLICY_REJECT should RULE_REJECT in background
- expectSetUidNetworkRules(UID_A, true);
+ expectSetUidMeteredNetworkBlacklist(UID_A, true);
expectSetUidForeground(UID_A, false);
future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
replay();
@@ -416,7 +416,7 @@
Future<Void> future;
// POLICY_NONE should have RULE_ALLOW in background
- expectSetUidNetworkRules(UID_A, false);
+ expectSetUidMeteredNetworkBlacklist(UID_A, false);
expectSetUidForeground(UID_A, false);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
@@ -426,7 +426,7 @@
verifyAndReset();
// adding POLICY_REJECT should cause RULE_REJECT
- expectSetUidNetworkRules(UID_A, true);
+ expectSetUidMeteredNetworkBlacklist(UID_A, true);
expectSetUidForeground(UID_A, false);
future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
replay();
@@ -435,7 +435,7 @@
verifyAndReset();
// removing POLICY_REJECT should return us to RULE_ALLOW
- expectSetUidNetworkRules(UID_A, false);
+ expectSetUidMeteredNetworkBlacklist(UID_A, false);
expectSetUidForeground(UID_A, false);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
@@ -632,7 +632,7 @@
Future<Void> future;
// POLICY_REJECT should RULE_REJECT in background
- expectSetUidNetworkRules(UID_A, true);
+ expectSetUidMeteredNetworkBlacklist(UID_A, true);
expectSetUidForeground(UID_A, false);
future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
replay();
@@ -641,7 +641,7 @@
verifyAndReset();
// uninstall should clear RULE_REJECT
- expectSetUidNetworkRules(UID_A, false);
+ expectSetUidMeteredNetworkBlacklist(UID_A, false);
expectSetUidForeground(UID_A, false);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
@@ -890,9 +890,9 @@
expectLastCall().atLeastOnce();
}
- private void expectSetUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces)
+ private void expectSetUidMeteredNetworkBlacklist(int uid, boolean rejectOnQuotaInterfaces)
throws Exception {
- mNetworkManager.setUidNetworkRules(uid, rejectOnQuotaInterfaces);
+ mNetworkManager.setUidMeteredNetworkBlacklist(uid, rejectOnQuotaInterfaces);
expectLastCall().atLeastOnce();
}
diff --git a/services/tests/servicestests/src/com/android/server/content/ObserverNodeTest.java b/services/tests/servicestests/src/com/android/server/content/ObserverNodeTest.java
index 5b70c17..07280bc 100644
--- a/services/tests/servicestests/src/com/android/server/content/ObserverNodeTest.java
+++ b/services/tests/servicestests/src/com/android/server/content/ObserverNodeTest.java
@@ -62,7 +62,7 @@
ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
for (int i = nums.length - 1; i >=0; --i) {
- root.collectObserversLocked(uris[i], 0, null, false, myUserHandle, calls);
+ root.collectObserversLocked(uris[i], 0, null, false, 0, myUserHandle, calls);
assertEquals(nums[i], calls.size());
calls.clear();
}
@@ -92,7 +92,7 @@
ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
for (int i = uris.length - 1; i >=0; --i) {
- root.collectObserversLocked(uris[i], 0, null, false, myUserHandle, calls);
+ root.collectObserversLocked(uris[i], 0, null, false, 0, myUserHandle, calls);
assertEquals(nums[i], calls.size());
calls.clear();
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index 35777ce..744443f 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -15,14 +15,14 @@
*/
package com.android.server.devicepolicy;
-import com.android.internal.widget.LockPatternUtils;
-
import android.app.IActivityManager;
import android.app.NotificationManager;
import android.app.backup.IBackupManager;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManagerInternal;
+import android.database.ContentObserver;
import android.media.IAudioService;
+import android.net.Uri;
import android.os.Looper;
import android.os.PowerManagerInternal;
import android.os.UserHandle;
@@ -30,12 +30,15 @@
import android.os.UserManagerInternal;
import android.os.storage.StorageManager;
import android.telephony.TelephonyManager;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Pair;
import android.view.IWindowManager;
-import java.io.File;
+import com.android.internal.widget.LockPatternUtils;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.when;
+import java.io.File;
+import java.util.Map;
/**
* Overrides {@link #DevicePolicyManagerService} for dependency injection.
@@ -77,6 +80,7 @@
}
public final DpmMockContext context;
+ private final MockInjector mMockInjector;
public DevicePolicyManagerServiceTestable(DpmMockContext context, File dataDir) {
this(new MockInjector(context, dataDir));
@@ -84,15 +88,36 @@
private DevicePolicyManagerServiceTestable(MockInjector injector) {
super(injector);
+ mMockInjector = injector;
this.context = injector.context;
}
+
+ public void notifyChangeToContentObserver(Uri uri, int userHandle) {
+ ContentObserver co = mMockInjector.mContentObservers
+ .get(new Pair<Uri, Integer>(uri, userHandle));
+ if (co != null) {
+ co.onChange(false, uri, userHandle); // notify synchronously
+ }
+
+ // Notify USER_ALL observer too.
+ co = mMockInjector.mContentObservers
+ .get(new Pair<Uri, Integer>(uri, UserHandle.USER_ALL));
+ if (co != null) {
+ co.onChange(false, uri, userHandle); // notify synchronously
+ }
+ }
+
+
private static class MockInjector extends Injector {
public final DpmMockContext context;
public final File dataDir;
+ // Key is a pair of uri and userId
+ private final Map<Pair<Uri, Integer>, ContentObserver> mContentObservers = new ArrayMap<>();
+
private MockInjector(DpmMockContext context, File dataDir) {
super(context);
this.context = context;
@@ -265,6 +290,12 @@
}
@Override
+ void registerContentObserver(Uri uri, boolean notifyForDescendents,
+ ContentObserver observer, int userHandle) {
+ mContentObservers.put(new Pair<Uri, Integer>(uri, userHandle), observer);
+ }
+
+ @Override
int settingsSecureGetIntForUser(String name, int def, int userHandle) {
return context.settings.settingsSecureGetIntForUser(name, def, userHandle);
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index e6963d5..3a2e946 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -543,10 +543,31 @@
}
/**
- * Test for: {@link DevicePolicyManager#setDeviceOwner} DO on system user installs
- * successfully.
+ * Test for: {@link DevicePolicyManager#setDeviceOwner} DO on system user installs successfully.
*/
public void testSetDeviceOwner() throws Exception {
+ setDeviceOwner();
+
+ // Try to set a profile owner on the same user, which should fail.
+ setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_SYSTEM_USER_UID);
+ dpm.setActiveAdmin(admin2, /* refreshing= */ true, UserHandle.USER_SYSTEM);
+ try {
+ dpm.setProfileOwner(admin2, "owner-name", UserHandle.USER_SYSTEM);
+ fail("IllegalStateException not thrown");
+ } catch (IllegalStateException expected) {
+ assertTrue("Message was: " + expected.getMessage(),
+ expected.getMessage().contains("already has a device owner"));
+ }
+
+ // DO admin can't be deactivated.
+ dpm.removeActiveAdmin(admin1);
+ assertTrue(dpm.isAdminActive(admin1));
+
+ // TODO Test getDeviceOwnerName() too. To do so, we need to change
+ // DPMS.getApplicationLabel() because Context.createPackageContextAsUser() is not mockable.
+ }
+
+ private void setDeviceOwner() throws Exception {
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
mContext.callerPermissions.add(permission.MANAGE_USERS);
mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
@@ -594,24 +615,6 @@
MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
assertEquals(admin1, dpm.getDeviceOwnerComponentOnAnyUser());
-
- // Try to set a profile owner on the same user, which should fail.
- setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_SYSTEM_USER_UID);
- dpm.setActiveAdmin(admin2, /* refreshing= */ true, UserHandle.USER_SYSTEM);
- try {
- dpm.setProfileOwner(admin2, "owner-name", UserHandle.USER_SYSTEM);
- fail("IllegalStateException not thrown");
- } catch (IllegalStateException expected) {
- assertTrue("Message was: " + expected.getMessage(),
- expected.getMessage().contains("already has a device owner"));
- }
-
- // DO admin can't be deactivated.
- dpm.removeActiveAdmin(admin1);
- assertTrue(dpm.isAdminActive(admin1));
-
- // TODO Test getDeviceOwnerName() too. To do so, we need to change
- // DPMS.getApplicationLabel() because Context.createPackageContextAsUser() is not mockable.
}
private void checkGetDeviceOwnerInfoApi(DevicePolicyManager dpm, boolean hasDeviceOwner) {
@@ -1934,5 +1937,211 @@
// TODO Verify calls to settingsGlobalPutInt. Tried but somehow mockito threw
// UnfinishedVerificationException.
}
-}
+ public void testIsProvisioningAllowed_DeviceAdminFeatureOff() throws Exception {
+ when(mContext.packageManager.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN))
+ .thenReturn(false);
+ when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
+ .thenReturn(false);
+ initializeDpms();
+ when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(false);
+ when(mContext.userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true))
+ .thenReturn(true);
+ setUserSetupCompleteForUser(false, UserHandle.USER_SYSTEM);
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, false);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE,
+ false);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_USER, false);
+ }
+
+ public void testIsProvisioningAllowed_ManagedProfileFeatureOff() throws Exception {
+ when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
+ .thenReturn(false);
+ initializeDpms();
+ when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(false);
+ when(mContext.userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true))
+ .thenReturn(true);
+ setUserSetupCompleteForUser(false, UserHandle.USER_SYSTEM);
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, true);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE,
+ false);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_USER, false);
+
+ // Test again when split user is on
+ when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(true);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, true);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE,
+ true);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_USER, false);
+ }
+
+ public void testIsProvisioningAllowed_nonSplitUser_firstBoot_primaryUser() throws Exception {
+ when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
+ .thenReturn(true);
+ when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(false);
+ when(mContext.userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true))
+ .thenReturn(true);
+ setUserSetupCompleteForUser(false, UserHandle.USER_SYSTEM);
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, true);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE,
+ false /* because of non-split user */);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_USER,
+ false /* because of non-split user */);
+ }
+
+ public void testIsProvisioningAllowed_nonSplitUser_afterDeviceSetup_primaryUser()
+ throws Exception {
+ when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
+ .thenReturn(true);
+ when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(false);
+ when(mContext.userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true))
+ .thenReturn(true);
+ setUserSetupCompleteForUser(true, UserHandle.USER_SYSTEM);
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
+ false/* because of completed device setup */);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE,
+ false/* because of non-split user */);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_USER,
+ false/* because of non-split user */);
+ }
+
+ public void testIsProvisioningAllowed_splitUser_firstBoot_systemUser() throws Exception {
+ when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
+ .thenReturn(true);
+ when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(true);
+ when(mContext.userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true))
+ .thenReturn(false);
+ setUserSetupCompleteForUser(false, UserHandle.USER_SYSTEM);
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, true);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
+ false /* because canAddMoreManagedProfiles returns false */);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE,
+ true);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_USER,
+ false/* because calling uid is system user */);
+
+ }
+
+ public void testIsProvisioningAllowed_splitUser_afterDeviceSetup_systemUser() throws Exception {
+ when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
+ .thenReturn(true);
+ when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(true);
+ when(mContext.userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true))
+ .thenReturn(false);
+ setUserSetupCompleteForUser(true, UserHandle.USER_SYSTEM);
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
+ true/* it's undefined behavior. Can be changed into false in the future */);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
+ false /* because canAddMoreManagedProfiles returns false */);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE,
+ true/* it's undefined behavior. Can be changed into false in the future */);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_USER,
+ false/* because calling uid is system user */);
+ }
+
+ public void testIsProvisioningAllowed_splitUser_firstBoot_primaryUser() throws Exception {
+ when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
+ .thenReturn(true);
+ when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(true);
+ when(mContext.userManager.canAddMoreManagedProfiles(DpmMockContext.CALLER_USER_HANDLE,
+ true)).thenReturn(true);
+ setUserSetupCompleteForUser(false, DpmMockContext.CALLER_USER_HANDLE);
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, true);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE,
+ true);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_USER, true);
+
+ }
+
+ public void testIsProvisioningAllowed_splitUser_afterDeviceSetup_primaryUser()
+ throws Exception {
+ when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
+ .thenReturn(true);
+ when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(true);
+ when(mContext.userManager.canAddMoreManagedProfiles(DpmMockContext.CALLER_USER_HANDLE,
+ true)).thenReturn(true);
+ setUserSetupCompleteForUser(true, DpmMockContext.CALLER_USER_HANDLE);
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
+ true/* it's undefined behavior. Can be changed into false in the future */);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE,
+ true/* it's undefined behavior. Can be changed into false in the future */);
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_USER,
+ false/* because user setup completed */);
+ }
+
+ public void testIsProvisioningAllowed_provisionManagedProfileWithDeviceOwner_systemUser()
+ throws Exception {
+ setDeviceOwner();
+
+ when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
+ .thenReturn(true);
+ when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(true);
+ when(mContext.userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true))
+ .thenReturn(false);
+ setUserSetupCompleteForUser(true, UserHandle.USER_SYSTEM);
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
+ false /* can't provision managed profile on system user */);
+ }
+
+ public void testIsProvisioningAllowed_provisionManagedProfileWithDeviceOwner_primaryUser()
+ throws Exception {
+ setDeviceOwner();
+
+ when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
+ .thenReturn(true);
+ when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(true);
+ when(mContext.userManager.canAddMoreManagedProfiles(DpmMockContext.CALLER_USER_HANDLE,
+ true)).thenReturn(true);
+ setUserSetupCompleteForUser(false, DpmMockContext.CALLER_USER_HANDLE);
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
+ }
+
+ private void setUserSetupCompleteForUser(boolean isUserSetupComplete, int userhandle) {
+ when(mContext.settings.settingsSecureGetIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0,
+ userhandle)).thenReturn(isUserSetupComplete ? 1 : 0);
+ dpms.notifyChangeToContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), userhandle);
+ }
+
+ private void assertProvisioningAllowed(String action, boolean expected) {
+ assertEquals("isProvisioningAllowed(" + action + ") returning unexpected result", expected,
+ dpm.isProvisioningAllowed(action));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 8e2ef70..60d7382 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -53,6 +53,7 @@
import java.util.ArrayList;
import java.util.List;
+import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
@@ -172,36 +173,36 @@
}
public static class SettingsForMock {
- int settingsSecureGetIntForUser(String name, int def, int userHandle) {
+ public int settingsSecureGetIntForUser(String name, int def, int userHandle) {
return 0;
}
- void settingsSecurePutIntForUser(String name, int value, int userHandle) {
+ public void settingsSecurePutIntForUser(String name, int value, int userHandle) {
}
- void settingsSecurePutStringForUser(String name, String value, int userHandle) {
+ public void settingsSecurePutStringForUser(String name, String value, int userHandle) {
}
- void settingsGlobalPutStringForUser(String name, String value, int userHandle) {
+ public void settingsGlobalPutStringForUser(String name, String value, int userHandle) {
}
- void settingsSecurePutInt(String name, int value) {
+ public void settingsSecurePutInt(String name, int value) {
}
- void settingsGlobalPutInt(String name, int value) {
+ public void settingsGlobalPutInt(String name, int value) {
}
- void settingsSecurePutString(String name, String value) {
+ public void settingsSecurePutString(String name, String value) {
}
- void settingsGlobalPutString(String name, String value) {
+ public void settingsGlobalPutString(String name, String value) {
}
- int settingsGlobalGetInt(String name, int def) {
+ public int settingsGlobalGetInt(String name, int value) {
return 0;
}
- void securityLogSetLoggingEnabledProperty(boolean enabled) {
+ public void securityLogSetLoggingEnabledProperty(boolean enabled) {
}
public boolean securityLogGetLoggingEnabledProperty() {
@@ -321,6 +322,8 @@
mUserInfos.add(uh);
when(userManager.getUsers()).thenReturn(mUserInfos);
+ when(userManager.getUsers(anyBoolean())).thenReturn(mUserInfos);
+ when(userManager.isUserRunning(eq(new UserHandle(userId)))).thenReturn(true);
when(userManager.getUserInfo(anyInt())).thenAnswer(
new Answer<UserInfo>() {
@Override
diff --git a/services/tests/servicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/servicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
new file mode 100644
index 0000000..83a59fd
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -0,0 +1,541 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.notification;
+
+
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.app.Notification.Builder;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.Vibrator;
+import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.StatusBarNotification;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class BuzzBeepBlinkTest extends AndroidTestCase {
+
+ @Mock AudioManager mAudioManager;
+ @Mock Vibrator mVibrator;
+ @Mock android.media.IRingtonePlayer mRingtonePlayer;
+ @Mock Handler mHandler;
+
+ private NotificationManagerService mService;
+ private String mPkg = "com.android.server.notification";
+ private int mId = 1001;
+ private int mOtherId = 1002;
+ private String mTag = null;
+ private int mUid = 1000;
+ private int mPid = 2000;
+ private int mScore = 10;
+ private android.os.UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser());
+
+ @Override
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mAudioManager.isAudioFocusExclusive()).thenReturn(false);
+ when(mAudioManager.getRingtonePlayer()).thenReturn(mRingtonePlayer);
+ when(mAudioManager.getStreamVolume(anyInt())).thenReturn(10);
+ when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
+
+ mService = new NotificationManagerService(getContext());
+ mService.setAudioManager(mAudioManager);
+ mService.setVibrator(mVibrator);
+ mService.setSystemReady(true);
+ mService.setHandler(mHandler);
+ }
+
+ //
+ // Convenience functions for creating notification records
+ //
+
+ private NotificationRecord getNoisyOtherNotification() {
+ return getNotificationRecord(mOtherId, false /* insistent */, false /* once */,
+ true /* noisy */, true /* buzzy*/);
+ }
+
+ private NotificationRecord getBeepyNotification() {
+ return getNotificationRecord(mId, false /* insistent */, false /* once */,
+ true /* noisy */, false /* buzzy*/);
+ }
+
+ private NotificationRecord getBeepyOnceNotification() {
+ return getNotificationRecord(mId, false /* insistent */, true /* once */,
+ true /* noisy */, false /* buzzy*/);
+ }
+
+ private NotificationRecord getQuietNotification() {
+ return getNotificationRecord(mId, false /* insistent */, false /* once */,
+ false /* noisy */, false /* buzzy*/);
+ }
+
+ private NotificationRecord getQuietOtherNotification() {
+ return getNotificationRecord(mOtherId, false /* insistent */, false /* once */,
+ false /* noisy */, false /* buzzy*/);
+ }
+
+ private NotificationRecord getQuietOnceNotification() {
+ return getNotificationRecord(mId, false /* insistent */, true /* once */,
+ false /* noisy */, false /* buzzy*/);
+ }
+
+ private NotificationRecord getInsistentBeepyNotification() {
+ return getNotificationRecord(mId, true /* insistent */, false /* once */,
+ true /* noisy */, false /* buzzy*/);
+ }
+
+ private NotificationRecord getBuzzyNotification() {
+ return getNotificationRecord(mId, false /* insistent */, false /* once */,
+ false /* noisy */, true /* buzzy*/);
+ }
+
+ private NotificationRecord getBuzzyOnceNotification() {
+ return getNotificationRecord(mId, false /* insistent */, true /* once */,
+ false /* noisy */, true /* buzzy*/);
+ }
+
+ private NotificationRecord getInsistentBuzzyNotification() {
+ return getNotificationRecord(mId, true /* insistent */, false /* once */,
+ false /* noisy */, true /* buzzy*/);
+ }
+
+ private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once,
+ boolean noisy, boolean buzzy) {
+ final Builder builder = new Builder(getContext())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setPriority(Notification.PRIORITY_HIGH)
+ .setOnlyAlertOnce(once);
+
+ int defaults = 0;
+ if (noisy) {
+ defaults |= Notification.DEFAULT_SOUND;
+ }
+ if (buzzy) {
+ defaults |= Notification.DEFAULT_VIBRATE;
+ }
+ builder.setDefaults(defaults);
+
+ Notification n = builder.build();
+ if (insistent) {
+ n.flags |= Notification.FLAG_INSISTENT;
+ }
+ StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid, mPid,
+ mScore, n, mUser, System.currentTimeMillis());
+ return new NotificationRecord(getContext(), sbn);
+ }
+
+ //
+ // Convenience functions for interacting with mocks
+ //
+
+ private void verifyNeverBeep() throws RemoteException {
+ verify(mRingtonePlayer, never()).playAsync((Uri) anyObject(), (UserHandle) anyObject(),
+ anyBoolean(), (AudioAttributes) anyObject());
+ }
+
+ private void verifyBeep() throws RemoteException {
+ verify(mRingtonePlayer, times(1)).playAsync((Uri) anyObject(), (UserHandle) anyObject(),
+ eq(true), (AudioAttributes) anyObject());
+ }
+
+ private void verifyBeepLooped() throws RemoteException {
+ verify(mRingtonePlayer, times(1)).playAsync((Uri) anyObject(), (UserHandle) anyObject(),
+ eq(false), (AudioAttributes) anyObject());
+ }
+
+ private void verifyNeverStopAudio() throws RemoteException {
+ verify(mRingtonePlayer, never()).stopAsync();
+ }
+
+ private void verifyStopAudio() throws RemoteException {
+ verify(mRingtonePlayer, times(1)).stopAsync();
+ }
+
+ private void verifyNeverVibrate() {
+ verify(mVibrator, never()).vibrate(anyInt(), anyString(), (long[]) anyObject(),
+ anyInt(), (AudioAttributes) anyObject());
+ }
+
+ private void verifyVibrate() {
+ verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), (long[]) anyObject(),
+ eq(-1), (AudioAttributes) anyObject());
+ }
+
+ private void verifyVibrateLooped() {
+ verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), (long[]) anyObject(),
+ eq(0), (AudioAttributes) anyObject());
+ }
+
+ private void verifyStopVibrate() {
+ verify(mVibrator, times(1)).cancel();
+ }
+
+ private void verifyNeverStopVibrate() throws RemoteException {
+ verify(mVibrator, never()).cancel();
+ }
+
+ @SmallTest
+ public void testBeep() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+
+ mService.buzzBeepBlinkLocked(r);
+
+ verifyBeepLooped();
+ verifyNeverVibrate();
+ }
+
+ //
+ // Tests
+ //
+
+ @SmallTest
+ public void testBeepInsistently() throws Exception {
+ NotificationRecord r = getInsistentBeepyNotification();
+
+ mService.buzzBeepBlinkLocked(r);
+
+ verifyBeep();
+ }
+
+ @SmallTest
+ public void testNoInterruptionForMin() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ r.setImportance(Ranking.IMPORTANCE_MIN, "foo");
+
+ mService.buzzBeepBlinkLocked(r);
+
+ verifyNeverBeep();
+ verifyNeverVibrate();
+ }
+
+ @SmallTest
+ public void testNoInterruptionForIntercepted() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ r.setIntercepted(true);
+
+ mService.buzzBeepBlinkLocked(r);
+
+ verifyNeverBeep();
+ verifyNeverVibrate();
+ }
+
+ @SmallTest
+ public void testBeepTwice() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+
+ // set up internal state
+ mService.buzzBeepBlinkLocked(r);
+ Mockito.reset(mRingtonePlayer);
+
+ // update should beep
+ r.isUpdate = true;
+ mService.buzzBeepBlinkLocked(r);
+ verifyBeepLooped();
+ }
+
+ @SmallTest
+ public void testHonorAlertOnlyOnceForBeep() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ NotificationRecord s = getBeepyOnceNotification();
+ s.isUpdate = true;
+
+ // set up internal state
+ mService.buzzBeepBlinkLocked(r);
+ Mockito.reset(mRingtonePlayer);
+
+ // update should not beep
+ mService.buzzBeepBlinkLocked(s);
+ verifyNeverBeep();
+ }
+
+ @SmallTest
+ public void testNoisyUpdateDoesNotCancelAudio() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+
+ mService.buzzBeepBlinkLocked(r);
+ r.isUpdate = true;
+ mService.buzzBeepBlinkLocked(r);
+
+ verifyNeverStopAudio();
+ }
+
+ @SmallTest
+ public void testNoisyOnceUpdateDoesNotCancelAudio() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ NotificationRecord s = getBeepyOnceNotification();
+ s.isUpdate = true;
+
+ mService.buzzBeepBlinkLocked(r);
+ mService.buzzBeepBlinkLocked(s);
+
+ verifyNeverStopAudio();
+ }
+
+ @SmallTest
+ public void testQuietUpdateDoesNotCancelAudioFromOther() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ NotificationRecord s = getQuietNotification();
+ s.isUpdate = true;
+ NotificationRecord other = getNoisyOtherNotification();
+
+ // set up internal state
+ mService.buzzBeepBlinkLocked(r);
+ mService.buzzBeepBlinkLocked(other); // this takes the audio stream
+ Mockito.reset(mRingtonePlayer);
+
+ // should not stop noise, since we no longer own it
+ mService.buzzBeepBlinkLocked(s); // this no longer owns the stream
+ verifyNeverStopAudio();
+ }
+
+ @SmallTest
+ public void testQuietInterloperDoesNotCancelAudio() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ NotificationRecord other = getQuietOtherNotification();
+
+ // set up internal state
+ mService.buzzBeepBlinkLocked(r);
+ Mockito.reset(mRingtonePlayer);
+
+ // should not stop noise, since it does not own it
+ mService.buzzBeepBlinkLocked(other);
+ verifyNeverStopAudio();
+ }
+
+ @SmallTest
+ public void testQuietUpdateCancelsAudio() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ NotificationRecord s = getQuietNotification();
+ s.isUpdate = true;
+
+ // set up internal state
+ mService.buzzBeepBlinkLocked(r);
+ Mockito.reset(mRingtonePlayer);
+
+ // quiet update should stop making noise
+ mService.buzzBeepBlinkLocked(s);
+ verifyStopAudio();
+ }
+
+ @SmallTest
+ public void testQuietOnceUpdateCancelsAudio() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ NotificationRecord s = getQuietOnceNotification();
+ s.isUpdate = true;
+
+ // set up internal state
+ mService.buzzBeepBlinkLocked(r);
+ Mockito.reset(mRingtonePlayer);
+
+ // stop making noise - this is a weird corner case, but quiet should override once
+ mService.buzzBeepBlinkLocked(s);
+ verifyStopAudio();
+ }
+
+ @SmallTest
+ public void testDemoteSoundToVibrate() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+
+ // the phone is quiet
+ when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0);
+ when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
+
+ mService.buzzBeepBlinkLocked(r);
+
+ verifyNeverBeep();
+ verifyVibrate();
+ }
+
+ @SmallTest
+ public void testDemotInsistenteSoundToVibrate() throws Exception {
+ NotificationRecord r = getInsistentBeepyNotification();
+
+ // the phone is quiet
+ when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0);
+ when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
+
+ mService.buzzBeepBlinkLocked(r);
+
+ verifyVibrateLooped();
+ }
+
+ @SmallTest
+ public void testVibrate() throws Exception {
+ NotificationRecord r = getBuzzyNotification();
+
+ mService.buzzBeepBlinkLocked(r);
+
+ verifyNeverBeep();
+ verifyVibrate();
+ }
+
+ @SmallTest
+ public void testInsistenteVibrate() throws Exception {
+ NotificationRecord r = getInsistentBuzzyNotification();
+
+ mService.buzzBeepBlinkLocked(r);
+ verifyVibrateLooped();
+ }
+
+ @SmallTest
+ public void testVibratTwice() throws Exception {
+ NotificationRecord r = getBuzzyNotification();
+
+ // set up internal state
+ mService.buzzBeepBlinkLocked(r);
+ Mockito.reset(mVibrator);
+
+ // update should vibrate
+ r.isUpdate = true;
+ mService.buzzBeepBlinkLocked(r);
+ verifyVibrate();
+ }
+
+ @SmallTest
+ public void testHonorAlertOnlyOnceForBuzz() throws Exception {
+ NotificationRecord r = getBuzzyNotification();
+ NotificationRecord s = getBuzzyOnceNotification();
+ s.isUpdate = true;
+
+ // set up internal state
+ mService.buzzBeepBlinkLocked(r);
+ Mockito.reset(mVibrator);
+
+ // update should not beep
+ mService.buzzBeepBlinkLocked(s);
+ verifyNeverVibrate();
+ }
+
+ @SmallTest
+ public void testNoisyUpdateDoesNotCancelVibrate() throws Exception {
+ NotificationRecord r = getBuzzyNotification();
+
+ mService.buzzBeepBlinkLocked(r);
+ r.isUpdate = true;
+ mService.buzzBeepBlinkLocked(r);
+
+ verifyNeverStopVibrate();
+ }
+
+ @SmallTest
+ public void testNoisyOnceUpdateDoesNotCancelVibrate() throws Exception {
+ NotificationRecord r = getBuzzyNotification();
+ NotificationRecord s = getBuzzyOnceNotification();
+ s.isUpdate = true;
+
+ mService.buzzBeepBlinkLocked(r);
+ mService.buzzBeepBlinkLocked(s);
+
+ verifyNeverStopVibrate();
+ }
+
+ @SmallTest
+ public void testQuietUpdateDoesNotCancelVibrateFromOther() throws Exception {
+ NotificationRecord r = getBuzzyNotification();
+ NotificationRecord s = getQuietNotification();
+ s.isUpdate = true;
+ NotificationRecord other = getNoisyOtherNotification();
+
+ // set up internal state
+ mService.buzzBeepBlinkLocked(r);
+ mService.buzzBeepBlinkLocked(other); // this takes the vibrate stream
+ Mockito.reset(mVibrator);
+
+ // should not stop vibrate, since we no longer own it
+ mService.buzzBeepBlinkLocked(s); // this no longer owns the stream
+ verifyNeverStopVibrate();
+ }
+
+ @SmallTest
+ public void testQuietInterloperDoesNotCancelVibrate() throws Exception {
+ NotificationRecord r = getBuzzyNotification();
+ NotificationRecord other = getQuietOtherNotification();
+
+ // set up internal state
+ mService.buzzBeepBlinkLocked(r);
+ Mockito.reset(mVibrator);
+
+ // should not stop noise, since it does not own it
+ mService.buzzBeepBlinkLocked(other);
+ verifyNeverStopVibrate();
+ }
+
+ @SmallTest
+ public void testQuietUpdateCancelsVibrate() throws Exception {
+ NotificationRecord r = getBuzzyNotification();
+ NotificationRecord s = getQuietNotification();
+ s.isUpdate = true;
+
+ // set up internal state
+ mService.buzzBeepBlinkLocked(r);
+
+ // quiet update should stop making noise
+ mService.buzzBeepBlinkLocked(s);
+ verifyStopVibrate();
+ }
+
+ @SmallTest
+ public void testQuietOnceUpdateCancelsvibrate() throws Exception {
+ NotificationRecord r = getBuzzyNotification();
+ NotificationRecord s = getQuietOnceNotification();
+ s.isUpdate = true;
+
+ // set up internal state
+ mService.buzzBeepBlinkLocked(r);
+ Mockito.reset(mVibrator);
+
+ // stop making noise - this is a weird corner case, but quiet should override once
+ mService.buzzBeepBlinkLocked(s);
+ verifyStopVibrate();
+ }
+
+ @SmallTest
+ public void testQuietUpdateCancelsDemotedVibrate() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ NotificationRecord s = getQuietNotification();
+
+ // the phone is quiet
+ when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0);
+ when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
+
+ mService.buzzBeepBlinkLocked(r);
+
+ // quiet update should stop making noise
+ mService.buzzBeepBlinkLocked(s);
+ verifyStopVibrate();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutInfoTest.java
deleted file mode 100644
index eb16a1d..0000000
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutInfoTest.java
+++ /dev/null
@@ -1,44 +0,0 @@
-
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.pm;
-
-import android.content.pm.ShortcutInfo;
-import android.test.AndroidTestCase;
-
-import com.android.server.testutis.TestUtils;
-
-/**
- * Tests for {@link ShortcutInfo}.
-
- m FrameworksServicesTests &&
- adb install \
- -r -g ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
- adb shell am instrument -e class com.android.server.pm.ShortcutInfoTest \
- -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
-
- */
-public class ShortcutInfoTest extends AndroidTestCase {
-
- public void testNoId() {
- TestUtils.assertExpectException(
- IllegalArgumentException.class,
- "ID must be provided",
- () -> new ShortcutInfo.Builder(mContext).build());
- }
-
- // TODO Add more tests.
-}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
index 28966ca..baa5d36 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
@@ -16,15 +16,21 @@
package com.android.server.pm;
import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyList;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.UserIdInt;
import android.app.Activity;
import android.content.BroadcastReceiver;
@@ -32,25 +38,32 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
import android.content.pm.ILauncherApps;
import android.content.pm.LauncherApps;
import android.content.pm.LauncherApps.ShortcutQuery;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.content.pm.ShortcutServiceInternal;
+import android.content.pm.Signature;
+import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Icon;
+import android.net.Uri;
import android.os.BaseBundle;
import android.os.Bundle;
import android.os.FileUtils;
import android.os.Handler;
import android.os.Looper;
+import android.os.Parcel;
import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
@@ -59,6 +72,7 @@
import android.test.suitebuilder.annotation.SmallTest;
import android.util.ArraySet;
import android.util.Log;
+import android.util.Pair;
import android.util.SparseArray;
import com.android.frameworks.servicestests.R;
@@ -68,6 +82,8 @@
import com.android.server.pm.LauncherAppsService.LauncherAppsImpl;
import com.android.server.pm.ShortcutService.ConfigConstants;
import com.android.server.pm.ShortcutService.FileOutputStreamWithPath;
+import com.android.server.pm.ShortcutUser.PackageWithUser;
+import com.android.server.testutis.TestUtils;
import libcore.io.IoUtils;
@@ -88,6 +104,8 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.function.BiPredicate;
+import java.util.function.Consumer;
/**
* Tests for ShortcutService and ShortcutManager.
@@ -99,11 +117,8 @@
-w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
* TODO: Add checks with assertAllNotHaveIcon()
- *
- * TODO: separate, detailed tests for ShortcutInfo (CTS?) *
- *
- * TODO: Cross-user test (do in CTS?)
- *
+ * TODO: Detailed test for hasShortcutPermissionInner().
+ * TODO: Add tests for the command line functions too.
*/
@SmallTest
public class ShortcutManagerTest extends InstrumentationTestCase {
@@ -115,7 +130,10 @@
*/
private static final boolean ENABLE_DUMP = false; // DO NOT SUBMIT WITH true
- private class BaseContext extends MockContext {
+ private static final boolean DUMP_IN_TEARDOWN = false; // DO NOT SUBMIT WITH true
+
+ // public for mockito
+ public class BaseContext extends MockContext {
@Override
public Object getSystemService(String name) {
switch (name) {
@@ -149,7 +167,7 @@
}
/** Context used in the client side */
- private class ClientContext extends BaseContext {
+ public class ClientContext extends BaseContext {
@Override
public String getPackageName() {
return mInjectedClientPackage;
@@ -157,7 +175,7 @@
}
/** Context used in the service side */
- private final class ServiceContext extends BaseContext {
+ public class ServiceContext extends BaseContext {
long injectClearCallingIdentity() {
final int prevCallingUid = mInjectedCallingUid;
mInjectedCallingUid = Process.SYSTEM_UID;
@@ -167,6 +185,11 @@
void injectRestoreCallingIdentity(long token) {
mInjectedCallingUid = (int) token;
}
+
+ @Override
+ public void startActivityAsUser(@RequiresPermission Intent intent, @Nullable Bundle options,
+ UserHandle userId) {
+ }
}
/** ShortcutService with injection override methods. */
@@ -217,8 +240,7 @@
@Override
int injectGetPackageUid(String packageName, int userId) {
- Integer uid = mInjectedPackageUidMap.get(packageName);
- return UserHandle.getUid(getCallingUserId(), (uid != null ? uid : 0));
+ return getInjectedPackageInfo(packageName, userId, false).applicationInfo.uid;
}
@Override
@@ -248,19 +270,25 @@
@Override
boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId) {
- // Sort of hack; do a simpler check.
- return LAUNCHER_1.equals(callingPackage) || LAUNCHER_2.equals(callingPackage);
+ return mDefaultLauncherChecker.test(callingPackage, userId);
+ }
+
+ @Override
+ PackageInfo injectPackageInfo(String packageName, @UserIdInt int userId,
+ boolean getSignatures) {
+ return getInjectedPackageInfo(packageName, userId, getSignatures);
+ }
+
+ @Override
+ ApplicationInfo injectApplicationInfo(String packageName, @UserIdInt int userId) {
+ PackageInfo pi = injectPackageInfo(packageName, userId, /* getSignatures= */ false);
+ return pi != null ? pi.applicationInfo : null;
}
@Override
void postToHandler(Runnable r) {
final long token = mContext.injectClearCallingIdentity();
- super.postToHandler(r);
- try {
- runTestOnUiThread(() -> {});
- } catch (Throwable e) {
- fail("runTestOnUiThread failed: " + e);
- }
+ r.run();
mContext.injectRestoreCallingIdentity(token);
}
@@ -292,33 +320,33 @@
}
@Override
- public void ensureInUserProfiles(UserHandle userToCheck, String message) {
- if (getCallingUserId() == userToCheck.getIdentifier()) {
- return; // okay
- }
-
- assertEquals(Process.SYSTEM_UID, mInjectedCallingUid);
- // SKIP
- }
-
- @Override
public void verifyCallingPackage(String callingPackage) {
// SKIP
}
@Override
- boolean isEnabledProfileOf(UserHandle user, UserHandle listeningUser, String debugMsg) {
- // This requires CROSS_USER
- assertEquals(Process.SYSTEM_UID, mInjectedCallingUid);
- return user.getIdentifier() == listeningUser.getIdentifier();
- }
-
- @Override
void postToPackageMonitorHandler(Runnable r) {
final long token = mContext.injectClearCallingIdentity();
r.run();
mContext.injectRestoreCallingIdentity(token);
}
+
+ @Override
+ int injectBinderCallingUid() {
+ return mInjectedCallingUid;
+ }
+
+ @Override
+ long injectClearCallingIdentity() {
+ final int prevCallingUid = mInjectedCallingUid;
+ mInjectedCallingUid = Process.SYSTEM_UID;
+ return prevCallingUid;
+ }
+
+ @Override
+ void injectRestoreCallingIdentity(long token) {
+ mInjectedCallingUid = (int) token;
+ }
}
private class LauncherAppsTestable extends LauncherApps {
@@ -344,7 +372,11 @@
private ShortcutServiceInternal mInternal;
private LauncherAppImplTestable mLauncherAppImpl;
- private LauncherAppsTestable mLauncherApps;
+
+ // LauncherApps has per-instace state, so we need a differnt instance for each launcher.
+ private final Map<Pair<Integer, String>, LauncherAppsTestable>
+ mLauncherAppsMap = new HashMap<>();
+ private LauncherAppsTestable mLauncherApps; // Current one
private File mInjectedFilePathRoot;
@@ -355,7 +387,9 @@
private int mInjectedCallingUid;
private String mInjectedClientPackage;
- private Map<String, Integer> mInjectedPackageUidMap;
+ private Map<String, PackageInfo> mInjectedPackages;
+
+ private Set<PackageWithUser> mUninstalledPackages;
private PackageManager mMockPackageManager;
private PackageManagerInternal mMockPackageManagerInternal;
@@ -370,15 +404,49 @@
private static final String CALLING_PACKAGE_3 = "com.android.test.3";
private static final int CALLING_UID_3 = 10003;
+ private static final String CALLING_PACKAGE_4 = "com.android.test.4";
+ private static final int CALLING_UID_4 = 10004;
+
private static final String LAUNCHER_1 = "com.android.launcher.1";
private static final int LAUNCHER_UID_1 = 10011;
private static final String LAUNCHER_2 = "com.android.launcher.2";
private static final int LAUNCHER_UID_2 = 10012;
+ private static final String LAUNCHER_3 = "com.android.launcher.3";
+ private static final int LAUNCHER_UID_3 = 10013;
+
+ private static final String LAUNCHER_4 = "com.android.launcher.4";
+ private static final int LAUNCHER_UID_4 = 10014;
+
private static final int USER_0 = UserHandle.USER_SYSTEM;
private static final int USER_10 = 10;
private static final int USER_11 = 11;
+ private static final int USER_P0 = 20; // profile of user 0
+
+ private static final UserHandle HANDLE_USER_0 = UserHandle.of(USER_0);
+ private static final UserHandle HANDLE_USER_10 = UserHandle.of(USER_10);
+ private static final UserHandle HANDLE_USER_11 = UserHandle.of(USER_11);
+ private static final UserHandle HANDLE_USER_P0 = UserHandle.of(USER_P0);
+
+ private static final UserInfo USER_INFO_0 = withProfileGroupId(
+ new UserInfo(USER_0, "user0",
+ UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY | UserInfo.FLAG_INITIALIZED), 10);
+
+ private static final UserInfo USER_INFO_10 =
+ new UserInfo(USER_10, "user10", UserInfo.FLAG_INITIALIZED);
+
+ private static final UserInfo USER_INFO_11 =
+ new UserInfo(USER_11, "user11", UserInfo.FLAG_INITIALIZED);
+
+ private static final UserInfo USER_INFO_P0 = withProfileGroupId(
+ new UserInfo(USER_P0, "userP0",
+ UserInfo.FLAG_MANAGED_PROFILE), 10);
+
+ private BiPredicate<String, Integer> mDefaultLauncherChecker =
+ (callingPackage, userId) ->
+ LAUNCHER_1.equals(callingPackage) || LAUNCHER_2.equals(callingPackage)
+ || LAUNCHER_3.equals(callingPackage) || LAUNCHER_4.equals(callingPackage);
private static final long START_TIME = 1440000000101L;
@@ -392,11 +460,18 @@
private static final int MAX_ICON_DIMENSION_LOWRAM = 32;
+ private static final ShortcutQuery QUERY_ALL = new ShortcutQuery();
+
+ static {
+ QUERY_ALL.setQueryFlags(
+ ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_PINNED);
+ }
+
@Override
protected void setUp() throws Exception {
super.setUp();
- mServiceContext = new ServiceContext();
+ mServiceContext = spy(new ServiceContext());
mClientContext = new ClientContext();
mMockPackageManager = mock(PackageManager.class);
@@ -407,32 +482,87 @@
mInjectedCurrentTimeLillis = START_TIME;
- mInjectedPackageUidMap = new HashMap<>();
- mInjectedPackageUidMap.put(CALLING_PACKAGE_1, CALLING_UID_1);
- mInjectedPackageUidMap.put(CALLING_PACKAGE_2, CALLING_UID_2);
- mInjectedPackageUidMap.put(CALLING_PACKAGE_3, CALLING_UID_3);
- mInjectedPackageUidMap.put(LAUNCHER_1, LAUNCHER_UID_1);
- mInjectedPackageUidMap.put(LAUNCHER_2, LAUNCHER_UID_2);
+ mInjectedPackages = new HashMap<>();;
+ addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 1);
+ addPackage(CALLING_PACKAGE_2, CALLING_UID_2, 2);
+ addPackage(CALLING_PACKAGE_3, CALLING_UID_3, 3);
+ addPackage(CALLING_PACKAGE_4, CALLING_UID_4, 10);
+ addPackage(LAUNCHER_1, LAUNCHER_UID_1, 4);
+ addPackage(LAUNCHER_2, LAUNCHER_UID_2, 5);
+ addPackage(LAUNCHER_3, LAUNCHER_UID_3, 6);
+ addPackage(LAUNCHER_4, LAUNCHER_UID_4, 10);
+
+ // CALLING_PACKAGE_3 / LAUNCHER_3 are not backup target.
+ updatePackageInfo(CALLING_PACKAGE_3,
+ pi -> pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_ALLOW_BACKUP);
+ updatePackageInfo(LAUNCHER_3,
+ pi -> pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_ALLOW_BACKUP);
+
+ mUninstalledPackages = new HashSet<>();
mInjectedFilePathRoot = new File(getTestContext().getCacheDir(), "test-files");
- // Empty the data directory.
- if (mInjectedFilePathRoot.exists()) {
- Assert.assertTrue("failed to delete dir",
- FileUtils.deleteContents(mInjectedFilePathRoot));
- }
- mInjectedFilePathRoot.mkdirs();
+ deleteAllSavedFiles();
+
+ // Set up users.
+ doAnswer(inv -> {
+ assertSystem();
+ return USER_INFO_0;
+ }).when(mMockUserManager).getUserInfo(eq(USER_0));
+
+ doAnswer(inv -> {
+ assertSystem();
+ return USER_INFO_10;
+ }).when(mMockUserManager).getUserInfo(eq(USER_10));
+
+ doAnswer(inv -> {
+ assertSystem();
+ return USER_INFO_11;
+ }).when(mMockUserManager).getUserInfo(eq(USER_11));
+
+ doAnswer(inv -> {
+ assertSystem();
+ return USER_INFO_P0;
+ }).when(mMockUserManager).getUserInfo(eq(USER_P0));
+
+ // User 0 is always running.
+ when(mMockUserManager.isUserRunning(eq(USER_0))).thenReturn(true);
initService();
setCaller(CALLING_PACKAGE_1);
}
+ private static UserInfo withProfileGroupId(UserInfo in, int groupId) {
+ in.profileGroupId = groupId;
+ return in;
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ if (DUMP_IN_TEARDOWN) dumpsysOnLogcat("Teardown");
+
+ shutdownServices();
+
+ super.tearDown();
+ }
+
private Context getTestContext() {
return getInstrumentation().getContext();
}
+ private void deleteAllSavedFiles() {
+ // Empty the data directory.
+ if (mInjectedFilePathRoot.exists()) {
+ Assert.assertTrue("failed to delete dir",
+ FileUtils.deleteContents(mInjectedFilePathRoot));
+ }
+ mInjectedFilePathRoot.mkdirs();
+ }
+
/** (Re-) init the manager and the service. */
private void initService() {
+ shutdownServices();
+
LocalServices.removeServiceForTest(ShortcutServiceInternal.class);
// Instantiate targets.
@@ -442,18 +572,117 @@
mInternal = LocalServices.getService(ShortcutServiceInternal.class);
mLauncherAppImpl = new LauncherAppImplTestable(mServiceContext);
- mLauncherApps = new LauncherAppsTestable(mClientContext, mLauncherAppImpl);
+ mLauncherApps = null;
+ mLauncherAppsMap.clear();
// Load the setting file.
mService.onBootPhase(SystemService.PHASE_LOCK_SETTINGS_READY);
}
+ private void shutdownServices() {
+ if (mService != null) {
+ // Flush all the unsaved data from the previous instance.
+ mService.saveDirtyInfo();
+ }
+ LocalServices.removeServiceForTest(ShortcutServiceInternal.class);
+
+ mService = null;
+ mManager = null;
+ mInternal = null;
+ mLauncherAppImpl = null;
+ mLauncherApps = null;
+ mLauncherAppsMap.clear();
+ }
+
+ private void addPackage(String packageName, int uid, int version) {
+ addPackage(packageName, uid, version, packageName);
+ }
+
+ private <T> List<T> list(T... array) {
+ return Arrays.asList(array);
+ }
+
+ private <T> Set<T> set(Set<T> in) {
+ return new ArraySet<T>(in);
+ }
+
+ private Signature[] genSignatures(String... signatures) {
+ final Signature[] sigs = new Signature[signatures.length];
+ for (int i = 0; i < signatures.length; i++){
+ sigs[i] = new Signature(signatures[i].getBytes());
+ }
+ return sigs;
+ }
+
+ private PackageInfo genPackage(String packageName, int uid, int version, String... signatures) {
+ final PackageInfo pi = new PackageInfo();
+ pi.packageName = packageName;
+ pi.applicationInfo = new ApplicationInfo();
+ pi.applicationInfo.uid = uid;
+ pi.applicationInfo.flags = ApplicationInfo.FLAG_INSTALLED
+ | ApplicationInfo.FLAG_ALLOW_BACKUP;
+ pi.versionCode = version;
+ pi.signatures = genSignatures(signatures);
+
+ return pi;
+ }
+
+ private void addPackage(String packageName, int uid, int version, String... signatures) {
+ mInjectedPackages.put(packageName, genPackage(packageName, uid, version, signatures));
+ }
+
+ private void updatePackageInfo(String packageName, Consumer<PackageInfo> c) {
+ c.accept(mInjectedPackages.get(packageName));
+ }
+
+ private void uninstallPackage(int userId, String packageName) {
+ if (ENABLE_DUMP) {
+ Log.i(TAG, "Unnstall package " + packageName + " / " + userId);
+ }
+ mUninstalledPackages.add(PackageWithUser.of(userId, packageName));
+ }
+
+ private void installPackage(int userId, String packageName) {
+ if (ENABLE_DUMP) {
+ Log.i(TAG, "Install package " + packageName + " / " + userId);
+ }
+ mUninstalledPackages.remove(PackageWithUser.of(userId, packageName));
+ }
+
+ PackageInfo getInjectedPackageInfo(String packageName, @UserIdInt int userId,
+ boolean getSignatures) {
+ final PackageInfo pi = mInjectedPackages.get(packageName);
+ if (pi == null) return null;
+
+ final PackageInfo ret = new PackageInfo();
+ ret.packageName = pi.packageName;
+ ret.versionCode = pi.versionCode;
+ ret.applicationInfo = new ApplicationInfo(pi.applicationInfo);
+ ret.applicationInfo.uid = UserHandle.getUid(userId, pi.applicationInfo.uid);
+ if (mUninstalledPackages.contains(PackageWithUser.of(userId, packageName))) {
+ ret.applicationInfo.flags &= ~ApplicationInfo.FLAG_INSTALLED;
+ }
+
+ if (getSignatures) {
+ ret.signatures = pi.signatures;
+ }
+
+ return ret;
+ }
+
/** Replace the current calling package */
private void setCaller(String packageName, int userId) {
mInjectedClientPackage = packageName;
- mInjectedCallingUid = UserHandle.getUid(userId,
- Preconditions.checkNotNull(mInjectedPackageUidMap.get(packageName),
- "Unknown package"));
+ mInjectedCallingUid =
+ Preconditions.checkNotNull(getInjectedPackageInfo(packageName, userId, false),
+ "Unknown package").applicationInfo.uid;
+
+ // Set up LauncherApps for this caller.
+ final Pair<Integer, String> key = Pair.create(userId, packageName);
+ if (!mLauncherAppsMap.containsKey(key)) {
+ mLauncherAppsMap.put(key, new LauncherAppsTestable(mClientContext, mLauncherAppImpl));
+ }
+ mLauncherApps = mLauncherAppsMap.get(key);
}
private void setCaller(String packageName) {
@@ -464,15 +693,19 @@
return mInjectedClientPackage;
}
+ private void setDefaultLauncherChecker(BiPredicate<String, Integer> p) {
+ mDefaultLauncherChecker = p;
+ }
+
private void runWithCaller(String packageName, int userId, Runnable r) {
final String previousPackage = mInjectedClientPackage;
- final int previousUid = mInjectedCallingUid;
+ final int previousUserId = UserHandle.getUserId(mInjectedCallingUid);
setCaller(packageName, userId);
r.run();
- setCaller(previousPackage, previousUid);
+ setCaller(previousPackage, previousUserId);
}
private int getCallingUserId() {
@@ -485,14 +718,22 @@
/** For debugging */
private void dumpsysOnLogcat() {
- if (!ENABLE_DUMP) return;
+ dumpsysOnLogcat("");
+ }
+
+ private void dumpsysOnLogcat(String message) {
+ dumpsysOnLogcat(message, false);
+ }
+
+ private void dumpsysOnLogcat(String message, boolean force) {
+ if (force || !ENABLE_DUMP) return;
final ByteArrayOutputStream out = new ByteArrayOutputStream();
final PrintWriter pw = new PrintWriter(out);
mService.dumpInner(pw);
pw.close();
- Log.e(TAG, "Dumping ShortcutService:");
+ Log.e(TAG, "Dumping ShortcutService: " + message);
for (String line : out.toString().split("\n")) {
Log.e(TAG, line);
}
@@ -502,9 +743,13 @@
* For debugging, dump arbitrary file on logcat.
*/
private void dumpFileOnLogcat(String path) {
+ dumpFileOnLogcat(path, "");
+ }
+
+ private void dumpFileOnLogcat(String path, String message) {
if (!ENABLE_DUMP) return;
- Log.i(TAG, "Dumping file: " + path);
+ Log.i(TAG, "Dumping file: " + path + " " + message);
final StringBuilder sb = new StringBuilder();
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
String line;
@@ -530,17 +775,21 @@
* For debugging, dump per-user state file on logcat.
*/
private void dumpUserFile(int userId) {
+ dumpUserFile(userId, "");
+ }
+
+ private void dumpUserFile(int userId, String message) {
mService.saveDirtyInfo();
dumpFileOnLogcat(mInjectedFilePathRoot.getAbsolutePath()
+ "/user-" + userId
- + "/" + ShortcutService.FILENAME_USER_PACKAGES);
+ + "/" + ShortcutService.FILENAME_USER_PACKAGES, message);
}
private void waitOnMainThread() throws Throwable {
runTestOnUiThread(() -> {});
}
- private static Bundle makeBundle(Object... keysAndValues) {
+ public static Bundle makeBundle(Object... keysAndValues) {
Preconditions.checkState((keysAndValues.length % 2) == 0);
if (keysAndValues.length == 0) {
@@ -672,6 +921,12 @@
return ret;
}
+ private static void resetAll(Collection<?> mocks) {
+ for (Object o : mocks) {
+ reset(o);
+ }
+ }
+
@NonNull
private ShortcutInfo findById(List<ShortcutInfo> list, String id) {
for (ShortcutInfo s : list) {
@@ -683,6 +938,10 @@
return null;
}
+ private void assertSystem() {
+ assertEquals("Caller must be system", Process.SYSTEM_UID, mInjectedCallingUid);
+ }
+
private void assertResetTimes(long expectedLastResetTime, long expectedNextResetTime) {
assertEquals(expectedLastResetTime, mService.getLastResetTimeLocked());
assertEquals(expectedNextResetTime, mService.getNextResetTimeLocked());
@@ -691,8 +950,7 @@
@NonNull
private List<ShortcutInfo> assertShortcutIds(@NonNull List<ShortcutInfo> actualShortcuts,
String... expectedIds) {
- assertEquals(expectedIds.length, actualShortcuts.size());
- final HashSet<String> expected = new HashSet<>(Arrays.asList(expectedIds));
+ final HashSet<String> expected = new HashSet<>(list(expectedIds));
final HashSet<String> actual = new HashSet<>();
for (ShortcutInfo s : actualShortcuts) {
actual.add(s.getId());
@@ -772,7 +1030,7 @@
private List<ShortcutInfo> assertAllHaveIcon(
@NonNull List<ShortcutInfo> actualShortcuts) {
for (ShortcutInfo s : actualShortcuts) {
- assertTrue("ID " + s.getId(), s.hasIconFile() || s.hasIconResource());
+ assertTrue("ID " + s.getId() + " has no icon ", s.hasIconFile() || s.hasIconResource());
}
return actualShortcuts;
}
@@ -781,7 +1039,8 @@
private List<ShortcutInfo> assertAllHaveFlags(@NonNull List<ShortcutInfo> actualShortcuts,
int shortcutFlags) {
for (ShortcutInfo s : actualShortcuts) {
- assertTrue("ID " + s.getId(), s.hasFlags(shortcutFlags));
+ assertTrue("ID " + s.getId() + " doesn't have flags " + shortcutFlags,
+ s.hasFlags(shortcutFlags));
}
return actualShortcuts;
}
@@ -823,6 +1082,21 @@
return actualShortcuts;
}
+ private void assertDynamicOnly(ShortcutInfo si) {
+ assertTrue(si.isDynamic());
+ assertFalse(si.isPinned());
+ }
+
+ private void assertPinnedOnly(ShortcutInfo si) {
+ assertFalse(si.isDynamic());
+ assertTrue(si.isPinned());
+ }
+
+ private void assertDynamicAndPinned(ShortcutInfo si) {
+ assertTrue(si.isDynamic());
+ assertTrue(si.isPinned());
+ }
+
private void assertBitmapSize(int expectedWidth, int expectedHeight, @NonNull Bitmap bitmap) {
assertEquals("width", expectedWidth, bitmap.getWidth());
assertEquals("height", expectedHeight, bitmap.getHeight());
@@ -864,6 +1138,39 @@
assertTrue(getPackageShortcut(packageName, shortcutId, userId) == null);
}
+ private Intent launchShortcutAndGetIntent(
+ @NonNull String packageName, @NonNull String shortcutId, int userId) {
+ reset(mServiceContext);
+ assertTrue(mLauncherApps.startShortcut(packageName, shortcutId, null, null,
+ UserHandle.of(userId)));
+
+ final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mServiceContext).startActivityAsUser(
+ intentCaptor.capture(),
+ any(Bundle.class),
+ eq(UserHandle.of(userId)));
+ return intentCaptor.getValue();
+ }
+
+ private void assertShortcutLaunchable(@NonNull String packageName, @NonNull String shortcutId,
+ int userId) {
+ assertNotNull(launchShortcutAndGetIntent(packageName, shortcutId, userId));
+ }
+
+ private void assertShortcutNotLaunchable(@NonNull String packageName,
+ @NonNull String shortcutId, int userId) {
+ try {
+ final boolean ok = mLauncherApps.startShortcut(packageName, shortcutId, null, null,
+ UserHandle.of(userId));
+ if (!ok) {
+ return; // didn't launch, okay.
+ }
+ fail();
+ } catch (SecurityException expected) {
+ // security exception is okay too.
+ }
+ }
+
private ShortcutInfo getPackageShortcut(String packageName, String shortcutId) {
return getPackageShortcut(packageName, shortcutId, getCallingUserId());
}
@@ -886,11 +1193,29 @@
return getLauncherShortcuts(launcher, userId, ShortcutQuery.FLAG_GET_PINNED);
}
- /**
- * Wrap a set in an ArraySet just to get a better toString.
- */
- private <T> Set<T> set(Set<T> in) {
- return new ArraySet<T>(in);
+
+ private Intent genPackageDeleteIntent(String pakcageName, int userId) {
+ Intent i = new Intent(Intent.ACTION_PACKAGE_REMOVED);
+ i.setData(Uri.parse("package:" + pakcageName));
+ i.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ return i;
+ }
+
+ private Intent genPackageUpdateIntent(String pakcageName, int userId) {
+ Intent i = new Intent(Intent.ACTION_PACKAGE_ADDED);
+ i.setData(Uri.parse("package:" + pakcageName));
+ i.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ i.putExtra(Intent.EXTRA_REPLACING, true);
+ return i;
+ }
+
+ private ShortcutInfo parceled(ShortcutInfo si) {
+ Parcel p = Parcel.obtain();
+ p.writeParcelable(si, 0);
+ p.setDataPosition(0);
+ ShortcutInfo si2 = p.readParcelable(getClass().getClassLoader());
+ p.recycle();
+ return si2;
}
/**
@@ -1017,6 +1342,8 @@
}
public void testSetDynamicShortcuts() {
+ setCaller(CALLING_PACKAGE_1, USER_0);
+
final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.icon1);
final Icon icon2 = Icon.createWithBitmap(BitmapFactory.decodeResource(
getTestContext().getResources(), R.drawable.icon2));
@@ -1039,7 +1366,7 @@
/* weight */ 12);
final ShortcutInfo si3 = makeShortcut("shortcut3");
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1, si2)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1, si2)));
assertShortcutIds(assertAllNotKeyFieldsOnly(
mManager.getDynamicShortcuts()),
"shortcut1", "shortcut2");
@@ -1047,13 +1374,13 @@
// TODO: Check fields
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
assertShortcutIds(assertAllNotKeyFieldsOnly(
mManager.getDynamicShortcuts()),
"shortcut1");
assertEquals(1, mManager.getRemainingCallCount());
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList()));
+ assertTrue(mManager.setDynamicShortcuts(list()));
assertEquals(0, mManager.getDynamicShortcuts().size());
assertEquals(0, mManager.getRemainingCallCount());
@@ -1064,20 +1391,26 @@
dumpsysOnLogcat();
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si2, si3)));
+ assertTrue(mManager.setDynamicShortcuts(list(si2, si3)));
assertEquals(2, mManager.getDynamicShortcuts().size());
// TODO Check max number
+
+ runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"))));
+ });
}
public void testAddDynamicShortcuts() {
+ setCaller(CALLING_PACKAGE_1, USER_0);
+
final ShortcutInfo si1 = makeShortcut("shortcut1");
final ShortcutInfo si2 = makeShortcut("shortcut2");
final ShortcutInfo si3 = makeShortcut("shortcut3");
assertEquals(3, mManager.getRemainingCallCount());
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
assertEquals(2, mManager.getRemainingCallCount());
assertShortcutIds(assertAllNotKeyFieldsOnly(
mManager.getDynamicShortcuts()),
@@ -1099,6 +1432,10 @@
// TODO Check max number
// TODO Check fields.
+
+ runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
+ assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
+ });
}
public void testDeleteDynamicShortcut() {
@@ -1106,7 +1443,7 @@
final ShortcutInfo si2 = makeShortcut("shortcut2");
final ShortcutInfo si3 = makeShortcut("shortcut3");
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1, si2, si3)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1, si2, si3)));
assertShortcutIds(assertAllNotKeyFieldsOnly(
mManager.getDynamicShortcuts()),
"shortcut1", "shortcut2", "shortcut3");
@@ -1139,8 +1476,6 @@
// Still 2 calls left.
assertEquals(2, mManager.getRemainingCallCount());
-
- // TODO Make sure pinned shortcuts won't be deleted.
}
public void testDeleteAllDynamicShortcuts() {
@@ -1148,7 +1483,7 @@
final ShortcutInfo si2 = makeShortcut("shortcut2");
final ShortcutInfo si3 = makeShortcut("shortcut3");
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1, si2, si3)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1, si2, si3)));
assertShortcutIds(assertAllNotKeyFieldsOnly(
mManager.getDynamicShortcuts()),
"shortcut1", "shortcut2", "shortcut3");
@@ -1165,74 +1500,72 @@
assertEquals(0, mManager.getDynamicShortcuts().size());
// This should still work.
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1, si2, si3)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1, si2, si3)));
assertEquals(3, mManager.getDynamicShortcuts().size());
// Still 1 call left
assertEquals(1, mManager.getRemainingCallCount());
-
- // TODO Make sure pinned shortcuts won't be deleted.
}
public void testThrottling() {
final ShortcutInfo si1 = makeShortcut("shortcut1");
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
assertEquals(2, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
mInjectedCurrentTimeLillis++;
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
assertEquals(1, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
mInjectedCurrentTimeLillis++;
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
assertEquals(0, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
// Reached the max
mInjectedCurrentTimeLillis++;
- assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertFalse(mManager.setDynamicShortcuts(list(si1)));
assertEquals(0, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
// Still throttled
mInjectedCurrentTimeLillis = START_TIME + INTERVAL - 1;
- assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertFalse(mManager.setDynamicShortcuts(list(si1)));
assertEquals(0, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
// Now it should work.
mInjectedCurrentTimeLillis++;
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1))); // fail
+ assertTrue(mManager.setDynamicShortcuts(list(si1))); // fail
assertEquals(2, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL * 2, mManager.getRateLimitResetTime());
mInjectedCurrentTimeLillis++;
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
assertEquals(1, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL * 2, mManager.getRateLimitResetTime());
mInjectedCurrentTimeLillis++;
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
assertEquals(0, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL * 2, mManager.getRateLimitResetTime());
mInjectedCurrentTimeLillis++;
- assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertFalse(mManager.setDynamicShortcuts(list(si1)));
assertEquals(0, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL * 2, mManager.getRateLimitResetTime());
// 4 days later...
mInjectedCurrentTimeLillis = START_TIME + 4 * INTERVAL;
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
assertEquals(2, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL * 5, mManager.getRateLimitResetTime());
mInjectedCurrentTimeLillis++;
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
assertEquals(1, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL * 5, mManager.getRateLimitResetTime());
@@ -1242,7 +1575,7 @@
assertEquals(START_TIME + INTERVAL * 9, mManager.getRateLimitResetTime());
mInjectedCurrentTimeLillis++;
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
assertEquals(2, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL * 9, mManager.getRateLimitResetTime());
}
@@ -1250,7 +1583,7 @@
public void testThrottling_rewind() {
final ShortcutInfo si1 = makeShortcut("shortcut1");
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
assertEquals(2, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
@@ -1269,7 +1602,7 @@
mInjectedCurrentTimeLillis = START_TIME - 100000;
assertEquals(3, mManager.getRemainingCallCount());
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
assertEquals(2, mManager.getRemainingCallCount());
// Forward again, should be reset now.
@@ -1280,21 +1613,21 @@
public void testThrottling_perPackage() {
final ShortcutInfo si1 = makeShortcut("shortcut1");
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
assertEquals(2, mManager.getRemainingCallCount());
mInjectedCurrentTimeLillis++;
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
assertEquals(1, mManager.getRemainingCallCount());
mInjectedCurrentTimeLillis++;
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
assertEquals(0, mManager.getRemainingCallCount());
// Reached the max
mInjectedCurrentTimeLillis++;
- assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertFalse(mManager.setDynamicShortcuts(list(si1)));
// Try from a different caller.
mInjectedClientPackage = CALLING_PACKAGE_2;
@@ -1305,11 +1638,11 @@
assertEquals(3, mManager.getRemainingCallCount());
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si2)));
+ assertTrue(mManager.setDynamicShortcuts(list(si2)));
assertEquals(2, mManager.getRemainingCallCount());
mInjectedCurrentTimeLillis++;
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si2)));
+ assertTrue(mManager.setDynamicShortcuts(list(si2)));
assertEquals(1, mManager.getRemainingCallCount());
// Back to the original caller, still throttled.
@@ -1318,37 +1651,37 @@
mInjectedCurrentTimeLillis = START_TIME + INTERVAL - 1;
assertEquals(0, mManager.getRemainingCallCount());
- assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertFalse(mManager.setDynamicShortcuts(list(si1)));
assertEquals(0, mManager.getRemainingCallCount());
// Now it should work.
mInjectedCurrentTimeLillis++;
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
mInjectedCurrentTimeLillis++;
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
mInjectedCurrentTimeLillis++;
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
mInjectedCurrentTimeLillis++;
- assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertFalse(mManager.setDynamicShortcuts(list(si1)));
mInjectedCurrentTimeLillis = START_TIME + 4 * INTERVAL;
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
- assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
+ assertFalse(mManager.setDynamicShortcuts(list(si1)));
mInjectedClientPackage = CALLING_PACKAGE_2;
mInjectedCallingUid = CALLING_UID_2;
assertEquals(3, mManager.getRemainingCallCount());
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si2)));
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si2)));
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si2)));
- assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si2)));
+ assertTrue(mManager.setDynamicShortcuts(list(si2)));
+ assertTrue(mManager.setDynamicShortcuts(list(si2)));
+ assertTrue(mManager.setDynamicShortcuts(list(si2)));
+ assertFalse(mManager.setDynamicShortcuts(list(si2)));
}
public void testIcons() {
@@ -1365,7 +1698,7 @@
// Set from package 1
setCaller(CALLING_PACKAGE_1);
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+ assertTrue(mManager.setDynamicShortcuts(list(
makeShortcutWithIcon("res32x32", res32x32),
makeShortcutWithIcon("res64x64", res64x64),
makeShortcutWithIcon("bmp32x32", bmp32x32),
@@ -1385,7 +1718,7 @@
// Call from another caller with the same ID, just to make sure storage is per-package.
setCaller(CALLING_PACKAGE_2);
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+ assertTrue(mManager.setDynamicShortcuts(list(
makeShortcutWithIcon("res32x32", res512x512),
makeShortcutWithIcon("res64x64", res512x512),
makeShortcutWithIcon("none", res512x512)
@@ -1403,26 +1736,25 @@
Bitmap bmp;
setCaller(LAUNCHER_1);
-
// Check hasIconResource()/hasIconFile().
assertShortcutIds(assertAllHaveIconResId(mLauncherApps.getShortcutInfo(
- CALLING_PACKAGE_1, Arrays.asList("res32x32"),
+ CALLING_PACKAGE_1, list("res32x32"),
getCallingUser())), "res32x32");
assertShortcutIds(assertAllHaveIconResId(mLauncherApps.getShortcutInfo(
- CALLING_PACKAGE_1, Arrays.asList("res64x64"), getCallingUser())),
+ CALLING_PACKAGE_1, list("res64x64"), getCallingUser())),
"res64x64");
assertShortcutIds(assertAllHaveIconFile(mLauncherApps.getShortcutInfo(
- CALLING_PACKAGE_1, Arrays.asList("bmp32x32"), getCallingUser())),
+ CALLING_PACKAGE_1, list("bmp32x32"), getCallingUser())),
"bmp32x32");
assertShortcutIds(assertAllHaveIconFile(mLauncherApps.getShortcutInfo(
- CALLING_PACKAGE_1, Arrays.asList("bmp64x64"), getCallingUser())),
+ CALLING_PACKAGE_1, list("bmp64x64"), getCallingUser())),
"bmp64x64");
assertShortcutIds(assertAllHaveIconFile(mLauncherApps.getShortcutInfo(
- CALLING_PACKAGE_1, Arrays.asList("bmp512x512"), getCallingUser())),
+ CALLING_PACKAGE_1, list("bmp512x512"), getCallingUser())),
"bmp512x512");
// Check
@@ -1517,7 +1849,7 @@
final File p11_1_3 = openIconFileForWriteAndGetPath(11, CALLING_PACKAGE_1);
// Make sure their paths are all unique
- assertAllUnique(Arrays.asList(
+ assertAllUnique(list(
p10_1_1,
p10_1_2,
p10_1_3,
@@ -1546,7 +1878,7 @@
assertEquals(p11_1_1.getParent(), p11_1_3.getParent());
// Check the parents are still unique.
- assertAllUnique(Arrays.asList(
+ assertAllUnique(list(
p10_1_1.getParent(),
p10_2_1.getParent(),
p11_1_1.getParent()
@@ -1571,7 +1903,7 @@
public void testUpdateShortcuts() {
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+ assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1"),
makeShortcut("s2"),
makeShortcut("s3"),
@@ -1580,7 +1912,7 @@
)));
});
runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+ assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1"),
makeShortcut("s2"),
makeShortcut("s3"),
@@ -1589,9 +1921,9 @@
)));
});
runWithCaller(LAUNCHER_1, UserHandle.USER_SYSTEM, () -> {
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, Arrays.asList("s2", "s3"),
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s2", "s3"),
getCallingUser());
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, Arrays.asList("s4", "s5"),
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("s4", "s5"),
getCallingUser());
});
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
@@ -1631,7 +1963,7 @@
.setTitle("new title")
.build();
- mManager.updateShortcuts(Arrays.asList(s2, s4));
+ mManager.updateShortcuts(list(s2, s4));
});
runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
ShortcutInfo s2 = makeShortcutBuilder()
@@ -1645,7 +1977,7 @@
.setIntent(new Intent(Intent.ACTION_ALL_APPS))
.build();
- mManager.updateShortcuts(Arrays.asList(s2, s4));
+ mManager.updateShortcuts(list(s2, s4));
});
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
@@ -1691,10 +2023,11 @@
// TODO Check with other fields too.
// TODO Check bitmap removal too.
- }
- // TODO: updateShortcuts()
- // TODO: getPinnedShortcuts()
+ runWithCaller(CALLING_PACKAGE_2, USER_11, () -> {
+ mManager.updateShortcuts(list());
+ });
+ }
// === Test for launcher side APIs ===
@@ -1709,6 +2042,20 @@
return q;
}
+ private static ShortcutQuery buildAllQuery(String packageName) {
+ final ShortcutQuery q = new ShortcutQuery();
+ q.setPackage(packageName);
+ q.setQueryFlags(ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_PINNED);
+ return q;
+ }
+
+ private static ShortcutQuery buildPinnedQuery(String packageName) {
+ final ShortcutQuery q = new ShortcutQuery();
+ q.setPackage(packageName);
+ q.setQueryFlags(ShortcutQuery.FLAG_GET_PINNED);
+ return q;
+ }
+
public void testGetShortcuts() {
// Set up shortcuts.
@@ -1717,17 +2064,17 @@
final ShortcutInfo s1_1 = makeShortcutWithTimestamp("s1", 5000);
final ShortcutInfo s1_2 = makeShortcutWithTimestamp("s2", 1000);
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s1_1, s1_2)));
+ assertTrue(mManager.setDynamicShortcuts(list(s1_1, s1_2)));
setCaller(CALLING_PACKAGE_2);
final ShortcutInfo s2_2 = makeShortcutWithTimestamp("s2", 1500);
final ShortcutInfo s2_3 = makeShortcutWithTimestamp("s3", 3000);
final ShortcutInfo s2_4 = makeShortcutWithTimestamp("s4", 500);
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s2_2, s2_3, s2_4)));
+ assertTrue(mManager.setDynamicShortcuts(list(s2_2, s2_3, s2_4)));
setCaller(CALLING_PACKAGE_3);
- final ShortcutInfo s3_2 = makeShortcutWithTimestamp("s3", 5000);
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s3_2)));
+ final ShortcutInfo s3_2 = makeShortcutWithTimestamp("s3", START_TIME + 5000);
+ assertTrue(mManager.setDynamicShortcuts(list(s3_2)));
setCaller(LAUNCHER_1);
@@ -1765,7 +2112,7 @@
// Pin some shortcuts.
mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
- Arrays.asList("s3", "s4"), getCallingUser());
+ list("s3", "s4"), getCallingUser());
// Pinned ones only
assertAllPinned(assertAllHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
@@ -1808,7 +2155,7 @@
makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
/* weight */ 12);
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s1_1, s1_2)));
+ assertTrue(mManager.setDynamicShortcuts(list(s1_1, s1_2)));
dumpsysOnLogcat();
setCaller(CALLING_PACKAGE_2);
@@ -1820,14 +2167,14 @@
makeIntent(Intent.ACTION_ANSWER, ShortcutActivity2.class,
"key1", "val1", "nest", makeBundle("key", 123)),
/* weight */ 10);
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s2_1)));
+ assertTrue(mManager.setDynamicShortcuts(list(s2_1)));
dumpsysOnLogcat();
// Pin some.
setCaller(LAUNCHER_1);
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
- Arrays.asList("s2"), getCallingUser());
+ list("s2"), getCallingUser());
dumpsysOnLogcat();
@@ -1846,19 +2193,19 @@
list = assertShortcutIds(assertAllHaveTitle(assertAllNotHaveIntents(
assertAllNotKeyFieldsOnly(
mLauncherApps.getShortcutInfo(CALLING_PACKAGE_1,
- Arrays.asList("s2", "s1", "s3", null), getCallingUser())))),
+ list("s2", "s1", "s3", null), getCallingUser())))),
"s1", "s2");
assertEquals("Title 1", findById(list, "s1").getTitle());
assertEquals("Title 2", findById(list, "s2").getTitle());
assertShortcutIds(assertAllHaveTitle(assertAllNotHaveIntents(
mLauncherApps.getShortcutInfo(CALLING_PACKAGE_1,
- Arrays.asList("s3"), getCallingUser())))
+ list("s3"), getCallingUser())))
/* none */);
list = assertShortcutIds(assertAllHaveTitle(assertAllNotHaveIntents(
mLauncherApps.getShortcutInfo(CALLING_PACKAGE_2,
- Arrays.asList("s1", "s2", "s3"), getCallingUser()))),
+ list("s1", "s2", "s3"), getCallingUser()))),
"s1");
assertEquals("ABC", findById(list, "s1").getTitle());
}
@@ -1869,31 +2216,31 @@
final ShortcutInfo s1_1 = makeShortcutWithTimestamp("s1", 1000);
final ShortcutInfo s1_2 = makeShortcutWithTimestamp("s2", 2000);
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s1_1, s1_2)));
+ assertTrue(mManager.setDynamicShortcuts(list(s1_1, s1_2)));
});
runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
final ShortcutInfo s2_2 = makeShortcutWithTimestamp("s2", 1500);
final ShortcutInfo s2_3 = makeShortcutWithTimestamp("s3", 3000);
final ShortcutInfo s2_4 = makeShortcutWithTimestamp("s4", 500);
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s2_2, s2_3, s2_4)));
+ assertTrue(mManager.setDynamicShortcuts(list(s2_2, s2_3, s2_4)));
});
runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
final ShortcutInfo s3_2 = makeShortcutWithTimestamp("s2", 1000);
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s3_2)));
+ assertTrue(mManager.setDynamicShortcuts(list(s3_2)));
});
// Pin some.
runWithCaller(LAUNCHER_1, USER_0, () -> {
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
- Arrays.asList("s2", "s3"), getCallingUser());
+ list("s2", "s3"), getCallingUser());
mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
- Arrays.asList("s3", "s4", "s5"), getCallingUser());
+ list("s3", "s4", "s5"), getCallingUser());
mLauncherApps.pinShortcuts(CALLING_PACKAGE_3,
- Arrays.asList("s3"), getCallingUser()); // Note ID doesn't exist
+ list("s3"), getCallingUser()); // Note ID doesn't exist
});
// Delete some.
@@ -1938,12 +2285,12 @@
public void testPinShortcutAndGetPinnedShortcuts_multi() {
// Create some shortcuts.
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+ assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
});
runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+ assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
});
@@ -1952,10 +2299,10 @@
// Pin some.
runWithCaller(LAUNCHER_1, USER_0, () -> {
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
- Arrays.asList("s3", "s4"), getCallingUser());
+ list("s3", "s4"), getCallingUser());
mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
- Arrays.asList("s1", "s2", "s4"), getCallingUser());
+ list("s1", "s2", "s4"), getCallingUser());
});
dumpsysOnLogcat();
@@ -2029,10 +2376,10 @@
// Now pin some.
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
- Arrays.asList("s1", "s2"), getCallingUser());
+ list("s1", "s2"), getCallingUser());
mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
- Arrays.asList("s1", "s2"), getCallingUser());
+ list("s1", "s2"), getCallingUser());
assertShortcutIds(assertAllDynamic(
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
@@ -2052,6 +2399,10 @@
mService.saveDirtyInfo();
initService();
+ // Load from file.
+ mService.handleUnlockUser(USER_0);
+
+ // Make sure package info is restored too.
runWithCaller(LAUNCHER_1, USER_0, () -> {
assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
@@ -2108,7 +2459,7 @@
// Update pined. Note s2 and s3 are actually available, but not visible to this
// launcher, so still can't be pinned.
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, Arrays.asList("s1", "s2", "s3", "s4"),
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1", "s2", "s3", "s4"),
getCallingUser());
assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
@@ -2130,7 +2481,7 @@
"s3");
// Now "s1" is visible, so can be pinned.
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, Arrays.asList("s1", "s2", "s3", "s4"),
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1", "s2", "s3", "s4"),
getCallingUser());
assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
@@ -2141,8 +2492,8 @@
// Now clear pinned shortcuts. First, from launcher 1.
runWithCaller(LAUNCHER_1, USER_0, () -> {
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, Arrays.asList(), getCallingUser());
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, Arrays.asList(), getCallingUser());
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list(), getCallingUser());
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list(), getCallingUser());
assertEquals(0,
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
@@ -2162,8 +2513,8 @@
// Clear all pins from launcher 2.
runWithCaller(LAUNCHER_2, USER_0, () -> {
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, Arrays.asList(), getCallingUser());
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, Arrays.asList(), getCallingUser());
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list(), getCallingUser());
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list(), getCallingUser());
assertEquals(0,
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
@@ -2182,7 +2533,502 @@
});
}
- public void testCreateShortcutIntent() {
+ public void testPinShortcutAndGetPinnedShortcuts_crossProfile_plusLaunch() {
+ // Create some shortcuts.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"),
+ makeShortcut("s4"), makeShortcut("s5"), makeShortcut("s6"))));
+ });
+
+ // Pin some shortcuts and see the result.
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s1"), HANDLE_USER_0);
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
+ list("s1", "s2", "s3"), HANDLE_USER_0);
+ });
+
+ runWithCaller(LAUNCHER_1, USER_P0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s2"), HANDLE_USER_0);
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
+ list("s2", "s3"), HANDLE_USER_0);
+ });
+
+ runWithCaller(LAUNCHER_2, USER_P0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s3"), HANDLE_USER_0);
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
+ list("s3"), HANDLE_USER_0);
+ });
+
+ runWithCaller(LAUNCHER_2, USER_10, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s1", "s2", "s3"), HANDLE_USER_10);
+ });
+
+ // Cross profile pinning.
+ final int PIN_AND_DYNAMIC = ShortcutQuery.FLAG_GET_PINNED | ShortcutQuery.FLAG_GET_DYNAMIC;
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
+
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
+
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10);
+ });
+ runWithCaller(LAUNCHER_1, USER_P0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s2");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s2", "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
+
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
+
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10);
+ });
+ runWithCaller(LAUNCHER_2, USER_P0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
+
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
+
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10);
+ });
+ runWithCaller(LAUNCHER_2, USER_10, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_10)),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_10)),
+ "s1", "s2", "s3", "s4", "s5", "s6");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_10)),
+ "s1", "s2", "s3", "s4", "s5", "s6");
+ });
+
+ // Remove some dynamic shortcuts.
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"))));
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"))));
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"))));
+ });
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
+
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
+
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10);
+ });
+ runWithCaller(LAUNCHER_1, USER_P0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s2");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2");
+
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s2", "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
+
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
+
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10);
+ });
+ runWithCaller(LAUNCHER_2, USER_P0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s3");
+
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s3");
+
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
+
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
+
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10);
+ });
+ runWithCaller(LAUNCHER_2, USER_10, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_10)),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_10)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_10)),
+ "s1", "s2", "s3");
+
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
+
+ assertShortcutNotLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
+
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_10);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s2", USER_10);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s3", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10);
+ });
+
+ // Save & load and make sure we still have the same information.
+ mService.saveDirtyInfo();
+ initService();
+ mService.handleUnlockUser(USER_0);
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
+
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
+
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10);
+ });
+ runWithCaller(LAUNCHER_1, USER_P0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s2");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2");
+
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s2", "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
+
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
+
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10);
+ });
+ runWithCaller(LAUNCHER_2, USER_P0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s3");
+
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s3");
+
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
+
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
+
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10);
+ });
+ }
+
+ public void testStartShortcut() {
// Create some shortcuts.
setCaller(CALLING_PACKAGE_1);
final ShortcutInfo s1_1 = makeShortcut(
@@ -2202,7 +3048,7 @@
makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
/* weight */ 12);
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s1_1, s1_2)));
+ assertTrue(mManager.setDynamicShortcuts(list(s1_1, s1_2)));
setCaller(CALLING_PACKAGE_2);
final ShortcutInfo s2_1 = makeShortcut(
@@ -2213,15 +3059,15 @@
makeIntent(Intent.ACTION_ANSWER, ShortcutActivity.class,
"key1", "val1", "nest", makeBundle("key", 123)),
/* weight */ 10);
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s2_1)));
+ assertTrue(mManager.setDynamicShortcuts(list(s2_1)));
// Pin all.
setCaller(LAUNCHER_1);
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
- Arrays.asList("s1", "s2"), getCallingUser());
+ list("s1", "s2"), getCallingUser());
mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
- Arrays.asList("s1"), getCallingUser());
+ list("s1"), getCallingUser());
// Just to make it complicated, delete some.
setCaller(CALLING_PACKAGE_1);
@@ -2229,29 +3075,22 @@
// intent and check.
setCaller(LAUNCHER_1);
+
Intent intent;
- intent = mInternal.createShortcutIntent(getCallingPackage(),
- CALLING_PACKAGE_1, "s1", getCallingUserId());
+ intent = launchShortcutAndGetIntent(CALLING_PACKAGE_1, "s1", USER_0);
assertEquals(ShortcutActivity2.class.getName(), intent.getComponent().getClassName());
- intent = mInternal.createShortcutIntent(getCallingPackage(),
- CALLING_PACKAGE_1, "s2", getCallingUserId());
+
+ intent = launchShortcutAndGetIntent(CALLING_PACKAGE_1, "s2", USER_0);
assertEquals(ShortcutActivity3.class.getName(), intent.getComponent().getClassName());
- intent = mInternal.createShortcutIntent(getCallingPackage(),
- CALLING_PACKAGE_2, "s1", getCallingUserId());
+ intent = launchShortcutAndGetIntent(CALLING_PACKAGE_2, "s1", USER_0);
assertEquals(ShortcutActivity.class.getName(), intent.getComponent().getClassName());
// TODO Check extra, etc
}
public void testLauncherCallback() throws Throwable {
-
- // TODO Add "multi" version -- run the test with two launchers and make sure the callback
- // argument only contains the ones that are actually visible to each launcher.
-
- when(mMockUserManager.isUserRunning(eq(USER_0))).thenReturn(true);
-
LauncherApps.Callback c0 = mock(LauncherApps.Callback.class);
// Set listeners
@@ -2261,7 +3100,7 @@
});
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+ assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
});
@@ -2270,7 +3109,7 @@
verify(c0).onShortcutsChanged(
eq(CALLING_PACKAGE_1),
shortcuts.capture(),
- eq(UserHandle.of(USER_0))
+ eq(HANDLE_USER_0)
);
assertShortcutIds(assertAllDynamic(shortcuts.getValue()),
"s1", "s2", "s3");
@@ -2278,7 +3117,7 @@
// From different package.
reset(c0);
runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+ assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
});
waitOnMainThread();
@@ -2286,7 +3125,7 @@
verify(c0).onShortcutsChanged(
eq(CALLING_PACKAGE_2),
shortcuts.capture(),
- eq(UserHandle.of(USER_0))
+ eq(HANDLE_USER_0)
);
assertShortcutIds(assertAllDynamic(shortcuts.getValue()),
"s1", "s2", "s3");
@@ -2294,7 +3133,7 @@
// Different user, callback shouldn't be called.
reset(c0);
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+ assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
});
waitOnMainThread();
@@ -2307,6 +3146,7 @@
// Test for addDynamicShortcut.
reset(c0);
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
+ dumpsysOnLogcat("before addDynamicShortcut");
assertTrue(mManager.addDynamicShortcut(makeShortcut("s4")));
});
@@ -2315,7 +3155,7 @@
verify(c0).onShortcutsChanged(
eq(CALLING_PACKAGE_1),
shortcuts.capture(),
- eq(UserHandle.of(USER_0))
+ eq(HANDLE_USER_0)
);
assertShortcutIds(assertAllDynamic(shortcuts.getValue()),
"s1", "s2", "s3", "s4");
@@ -2331,7 +3171,7 @@
verify(c0).onShortcutsChanged(
eq(CALLING_PACKAGE_1),
shortcuts.capture(),
- eq(UserHandle.of(USER_0))
+ eq(HANDLE_USER_0)
);
assertShortcutIds(assertAllDynamic(shortcuts.getValue()),
"s2", "s3", "s4");
@@ -2339,7 +3179,7 @@
// Test for update
reset(c0);
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
- assertTrue(mManager.updateShortcuts(Arrays.asList(
+ assertTrue(mManager.updateShortcuts(list(
makeShortcut("s1"), makeShortcut("s2"))));
});
@@ -2348,7 +3188,7 @@
verify(c0).onShortcutsChanged(
eq(CALLING_PACKAGE_1),
shortcuts.capture(),
- eq(UserHandle.of(USER_0))
+ eq(HANDLE_USER_0)
);
assertShortcutIds(assertAllDynamic(shortcuts.getValue()),
"s2", "s3", "s4");
@@ -2364,13 +3204,14 @@
verify(c0).onShortcutsChanged(
eq(CALLING_PACKAGE_1),
shortcuts.capture(),
- eq(UserHandle.of(USER_0))
+ eq(HANDLE_USER_0)
);
assertEquals(0, shortcuts.getValue().size());
// Remove CALLING_PACKAGE_2
reset(c0);
- mService.cleanUpPackageLocked(CALLING_PACKAGE_2, USER_0);
+ uninstallPackage(USER_0, CALLING_PACKAGE_2);
+ mService.cleanUpPackageLocked(CALLING_PACKAGE_2, USER_0, USER_0);
// Should get a callback with an empty list.
waitOnMainThread();
@@ -2378,15 +3219,165 @@
verify(c0).onShortcutsChanged(
eq(CALLING_PACKAGE_2),
shortcuts.capture(),
- eq(UserHandle.of(USER_0))
+ eq(HANDLE_USER_0)
);
assertEquals(0, shortcuts.getValue().size());
}
+ private void assertCallbackNotReceived(LauncherApps.Callback mock) {
+ verify(mock, times(0)).onShortcutsChanged(anyString(), anyList(),
+ any(UserHandle.class));
+ }
+
+ private void assertCallbackReceived(LauncherApps.Callback mock,
+ UserHandle user, String packageName, String... ids) {
+ ArgumentCaptor<List> shortcutsCaptor = ArgumentCaptor.forClass(List.class);
+
+ verify(mock, times(1)).onShortcutsChanged(eq(packageName), shortcutsCaptor.capture(),
+ eq(user));
+ assertShortcutIds(shortcutsCaptor.getValue(), ids);
+ }
+
+ public void testLauncherCallback_crossProfile() throws Throwable {
+ prepareCrossProfileDataSet();
+
+ final Handler h = new Handler(Looper.getMainLooper());
+
+ final LauncherApps.Callback c0_1 = mock(LauncherApps.Callback.class);
+ final LauncherApps.Callback c0_2 = mock(LauncherApps.Callback.class);
+ final LauncherApps.Callback c0_3 = mock(LauncherApps.Callback.class);
+ final LauncherApps.Callback c0_4 = mock(LauncherApps.Callback.class);
+
+ final LauncherApps.Callback cP0_1 = mock(LauncherApps.Callback.class);
+ final LauncherApps.Callback c10_1 = mock(LauncherApps.Callback.class);
+ final LauncherApps.Callback c10_2 = mock(LauncherApps.Callback.class);
+ final LauncherApps.Callback c11_1 = mock(LauncherApps.Callback.class);
+
+ final List<LauncherApps.Callback> all =
+ list(c0_1, c0_2, c0_3, c0_4, cP0_1, c10_1, c11_1);
+
+ setDefaultLauncherChecker((pkg, userId) -> {
+ switch (userId) {
+ case USER_0:
+ return LAUNCHER_2.equals(pkg);
+ case USER_P0:
+ return LAUNCHER_1.equals(pkg);
+ case USER_10:
+ return LAUNCHER_1.equals(pkg);
+ case USER_11:
+ return LAUNCHER_1.equals(pkg);
+ default:
+ return false;
+ }
+ });
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> mLauncherApps.registerCallback(c0_1, h));
+ runWithCaller(LAUNCHER_2, USER_0, () -> mLauncherApps.registerCallback(c0_2, h));
+ runWithCaller(LAUNCHER_3, USER_0, () -> mLauncherApps.registerCallback(c0_3, h));
+ runWithCaller(LAUNCHER_4, USER_0, () -> mLauncherApps.registerCallback(c0_4, h));
+ runWithCaller(LAUNCHER_1, USER_P0, () -> mLauncherApps.registerCallback(cP0_1, h));
+ runWithCaller(LAUNCHER_1, USER_10, () -> mLauncherApps.registerCallback(c10_1, h));
+ runWithCaller(LAUNCHER_2, USER_10, () -> mLauncherApps.registerCallback(c10_2, h));
+ runWithCaller(LAUNCHER_1, USER_11, () -> mLauncherApps.registerCallback(c11_1, h));
+
+ // User 0.
+
+ resetAll(all);
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.deleteDynamicShortcut("x");
+ });
+ waitOnMainThread();
+
+ assertCallbackNotReceived(c0_1);
+ assertCallbackNotReceived(c0_3);
+ assertCallbackNotReceived(c0_4);
+ assertCallbackNotReceived(c10_1);
+ assertCallbackNotReceived(c10_2);
+ assertCallbackNotReceived(c11_1);
+ assertCallbackReceived(c0_2, HANDLE_USER_0, CALLING_PACKAGE_1, "s1", "s2", "s3");
+ assertCallbackReceived(cP0_1, HANDLE_USER_0, CALLING_PACKAGE_1, "s1", "s2", "s3", "s4");
+
+ // User 0, different package.
+
+ resetAll(all);
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ mManager.deleteDynamicShortcut("x");
+ });
+ waitOnMainThread();
+
+ assertCallbackNotReceived(c0_1);
+ assertCallbackNotReceived(c0_3);
+ assertCallbackNotReceived(c0_4);
+ assertCallbackNotReceived(c10_1);
+ assertCallbackNotReceived(c10_2);
+ assertCallbackNotReceived(c11_1);
+ assertCallbackReceived(c0_2, HANDLE_USER_0, CALLING_PACKAGE_3, "s1", "s2", "s3", "s4");
+ assertCallbackReceived(cP0_1, HANDLE_USER_0, CALLING_PACKAGE_3,
+ "s1", "s2", "s3", "s4", "s5", "s6");
+
+ // Work profile, but not running, so don't send notifications.
+
+ resetAll(all);
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ mManager.deleteDynamicShortcut("x");
+ });
+ waitOnMainThread();
+
+ assertCallbackNotReceived(c0_1);
+ assertCallbackNotReceived(c0_2);
+ assertCallbackNotReceived(c0_3);
+ assertCallbackNotReceived(c0_4);
+ assertCallbackNotReceived(cP0_1);
+ assertCallbackNotReceived(c10_1);
+ assertCallbackNotReceived(c10_2);
+ assertCallbackNotReceived(c11_1);
+
+ // Work profile, now running.
+
+ when(mMockUserManager.isUserRunning(anyInt())).thenReturn(false);
+ when(mMockUserManager.isUserRunning(eq(USER_P0))).thenReturn(true);
+
+ resetAll(all);
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ mManager.deleteDynamicShortcut("x");
+ });
+ waitOnMainThread();
+
+ assertCallbackNotReceived(c0_1);
+ assertCallbackNotReceived(c0_3);
+ assertCallbackNotReceived(c0_4);
+ assertCallbackNotReceived(c10_1);
+ assertCallbackNotReceived(c10_2);
+ assertCallbackNotReceived(c11_1);
+ assertCallbackReceived(c0_2, HANDLE_USER_P0, CALLING_PACKAGE_1, "s1", "s2", "s3", "s5");
+ assertCallbackReceived(cP0_1, HANDLE_USER_P0, CALLING_PACKAGE_1, "s1", "s2", "s3", "s4");
+
+ // Normal secondary user.
+
+ when(mMockUserManager.isUserRunning(anyInt())).thenReturn(false);
+ when(mMockUserManager.isUserRunning(eq(USER_10))).thenReturn(true);
+
+ resetAll(all);
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ mManager.deleteDynamicShortcut("x");
+ });
+ waitOnMainThread();
+
+ assertCallbackNotReceived(c0_1);
+ assertCallbackNotReceived(c0_2);
+ assertCallbackNotReceived(c0_3);
+ assertCallbackNotReceived(c0_4);
+ assertCallbackNotReceived(cP0_1);
+ assertCallbackNotReceived(c10_2);
+ assertCallbackNotReceived(c11_1);
+ assertCallbackReceived(c10_1, HANDLE_USER_10, CALLING_PACKAGE_1,
+ "x1", "x2", "x3", "x4", "x5");
+ }
+
// === Test for persisting ===
public void testSaveAndLoadUser_empty() {
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList()));
+ assertTrue(mManager.setDynamicShortcuts(list()));
Log.i(TAG, "Saved state");
dumpsysOnLogcat();
@@ -2426,7 +3417,7 @@
makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
/* weight */ 12);
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1, si2)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1, si2)));
assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
assertEquals(2, mManager.getRemainingCallCount());
@@ -2453,7 +3444,7 @@
makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
/* weight */ 12);
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1, si2)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1, si2)));
assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
assertEquals(2, mManager.getRemainingCallCount());
@@ -2480,7 +3471,7 @@
makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
/* weight */ 12);
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1, si2)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1, si2)));
assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
assertEquals(2, mManager.getRemainingCallCount());
@@ -2549,45 +3540,45 @@
public void testCleanupPackage() {
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+ assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s0_1"))));
});
runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+ assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s0_2"))));
});
runWithCaller(LAUNCHER_1, USER_0, () -> {
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, Arrays.asList("s0_1"),
- UserHandle.of(USER_0));
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, Arrays.asList("s0_2"),
- UserHandle.of(USER_0));
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s0_1"),
+ HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("s0_2"),
+ HANDLE_USER_0);
});
runWithCaller(LAUNCHER_2, USER_0, () -> {
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, Arrays.asList("s0_1"),
- UserHandle.of(USER_0));
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, Arrays.asList("s0_2"),
- UserHandle.of(USER_0));
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s0_1"),
+ HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("s0_2"),
+ HANDLE_USER_0);
});
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+ assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s10_1"))));
});
runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+ assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s10_2"))));
});
runWithCaller(LAUNCHER_1, USER_10, () -> {
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, Arrays.asList("s10_1"),
- UserHandle.of(USER_10));
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, Arrays.asList("s10_2"),
- UserHandle.of(USER_10));
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s10_1"),
+ HANDLE_USER_10);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("s10_2"),
+ HANDLE_USER_10);
});
runWithCaller(LAUNCHER_2, USER_10, () -> {
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, Arrays.asList("s10_1"),
- UserHandle.of(USER_10));
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, Arrays.asList("s10_2"),
- UserHandle.of(USER_10));
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s10_1"),
+ HANDLE_USER_10);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("s10_2"),
+ HANDLE_USER_10);
});
// Remove all dynamic shortcuts; now all shortcuts are just pinned.
@@ -2615,15 +3606,19 @@
// Check the registered packages.
-
+ dumpsysOnLogcat();
assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
- set(user0.getPackages().keySet()));
+ set(user0.getAllPackages().keySet()));
assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
- set(user10.getPackages().keySet()));
- assertEquals(makeSet(LAUNCHER_1, LAUNCHER_2),
- set(user0.getLaunchers().keySet()));
- assertEquals(makeSet(LAUNCHER_1, LAUNCHER_2),
- set(user10.getLaunchers().keySet()));
+ set(user10.getAllPackages().keySet()));
+ assertEquals(
+ makeSet(PackageWithUser.of(USER_0, LAUNCHER_1),
+ PackageWithUser.of(USER_0, LAUNCHER_2)),
+ set(user0.getAllLaunchers().keySet()));
+ assertEquals(
+ makeSet(PackageWithUser.of(USER_10, LAUNCHER_1),
+ PackageWithUser.of(USER_10, LAUNCHER_2)),
+ set(user10.getAllLaunchers().keySet()));
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
"s0_1", "s0_2");
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
@@ -2640,17 +3635,22 @@
mService.saveDirtyInfo();
// Nonexistent package.
- mService.cleanUpPackageLocked("abc", USER_0);
+ uninstallPackage(USER_0, "abc");
+ mService.cleanUpPackageLocked("abc", USER_0, USER_0);
// No changes.
assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
- set(user0.getPackages().keySet()));
+ set(user0.getAllPackages().keySet()));
assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
- set(user10.getPackages().keySet()));
- assertEquals(makeSet(LAUNCHER_1, LAUNCHER_2),
- set(user0.getLaunchers().keySet()));
- assertEquals(makeSet(LAUNCHER_1, LAUNCHER_2),
- set(user10.getLaunchers().keySet()));
+ set(user10.getAllPackages().keySet()));
+ assertEquals(
+ makeSet(PackageWithUser.of(USER_0, LAUNCHER_1),
+ PackageWithUser.of(USER_0, LAUNCHER_2)),
+ set(user0.getAllLaunchers().keySet()));
+ assertEquals(
+ makeSet(PackageWithUser.of(USER_10, LAUNCHER_1),
+ PackageWithUser.of(USER_10, LAUNCHER_2)),
+ set(user10.getAllLaunchers().keySet()));
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
"s0_1", "s0_2");
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
@@ -2667,16 +3667,21 @@
mService.saveDirtyInfo();
// Remove a package.
- mService.cleanUpPackageLocked(CALLING_PACKAGE_1, USER_0);
+ uninstallPackage(USER_0, CALLING_PACKAGE_1);
+ mService.cleanUpPackageLocked(CALLING_PACKAGE_1, USER_0, USER_0);
assertEquals(makeSet(CALLING_PACKAGE_2),
- set(user0.getPackages().keySet()));
+ set(user0.getAllPackages().keySet()));
assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
- set(user10.getPackages().keySet()));
- assertEquals(makeSet(LAUNCHER_1, LAUNCHER_2),
- set(user0.getLaunchers().keySet()));
- assertEquals(makeSet(LAUNCHER_1, LAUNCHER_2),
- set(user10.getLaunchers().keySet()));
+ set(user10.getAllPackages().keySet()));
+ assertEquals(
+ makeSet(PackageWithUser.of(USER_0, LAUNCHER_1),
+ PackageWithUser.of(USER_0, LAUNCHER_2)),
+ set(user0.getAllLaunchers().keySet()));
+ assertEquals(
+ makeSet(PackageWithUser.of(USER_10, LAUNCHER_1),
+ PackageWithUser.of(USER_10, LAUNCHER_2)),
+ set(user10.getAllLaunchers().keySet()));
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
"s0_2");
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
@@ -2693,16 +3698,20 @@
mService.saveDirtyInfo();
// Remove a launcher.
- mService.cleanUpPackageLocked(LAUNCHER_1, USER_10);
+ uninstallPackage(USER_10, LAUNCHER_1);
+ mService.cleanUpPackageLocked(LAUNCHER_1, USER_10, USER_10);
assertEquals(makeSet(CALLING_PACKAGE_2),
- set(user0.getPackages().keySet()));
+ set(user0.getAllPackages().keySet()));
assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
- set(user10.getPackages().keySet()));
- assertEquals(makeSet(LAUNCHER_1, LAUNCHER_2),
- set(user0.getLaunchers().keySet()));
- assertEquals(makeSet(LAUNCHER_2),
- set(user10.getLaunchers().keySet()));
+ set(user10.getAllPackages().keySet()));
+ assertEquals(
+ makeSet(PackageWithUser.of(USER_0, LAUNCHER_1),
+ PackageWithUser.of(USER_0, LAUNCHER_2)),
+ set(user0.getAllLaunchers().keySet()));
+ assertEquals(
+ makeSet(PackageWithUser.of(USER_10, LAUNCHER_2)),
+ set(user10.getAllLaunchers().keySet()));
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
"s0_2");
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
@@ -2717,16 +3726,20 @@
mService.saveDirtyInfo();
// Remove a package.
- mService.cleanUpPackageLocked(CALLING_PACKAGE_2, USER_10);
+ uninstallPackage(USER_10, CALLING_PACKAGE_2);
+ mService.cleanUpPackageLocked(CALLING_PACKAGE_2, USER_10, USER_10);
assertEquals(makeSet(CALLING_PACKAGE_2),
- set(user0.getPackages().keySet()));
+ set(user0.getAllPackages().keySet()));
assertEquals(makeSet(CALLING_PACKAGE_1),
- set(user10.getPackages().keySet()));
- assertEquals(makeSet(LAUNCHER_1, LAUNCHER_2),
- set(user0.getLaunchers().keySet()));
- assertEquals(makeSet(LAUNCHER_2),
- set(user10.getLaunchers().keySet()));
+ set(user10.getAllPackages().keySet()));
+ assertEquals(
+ makeSet(PackageWithUser.of(USER_0, LAUNCHER_1),
+ PackageWithUser.of(USER_0, LAUNCHER_2)),
+ set(user0.getAllLaunchers().keySet()));
+ assertEquals(
+ makeSet(PackageWithUser.of(USER_10, LAUNCHER_2)),
+ set(user10.getAllLaunchers().keySet()));
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
"s0_2");
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
@@ -2741,16 +3754,20 @@
mService.saveDirtyInfo();
// Remove the other launcher from user 10 too.
- mService.cleanUpPackageLocked(LAUNCHER_2, USER_10);
+ uninstallPackage(USER_10, LAUNCHER_2);
+ mService.cleanUpPackageLocked(LAUNCHER_2, USER_10, USER_10);
assertEquals(makeSet(CALLING_PACKAGE_2),
- set(user0.getPackages().keySet()));
+ set(user0.getAllPackages().keySet()));
assertEquals(makeSet(CALLING_PACKAGE_1),
- set(user10.getPackages().keySet()));
- assertEquals(makeSet(LAUNCHER_1, LAUNCHER_2),
- set(user0.getLaunchers().keySet()));
- assertEquals(makeSet(),
- set(user10.getLaunchers().keySet()));
+ set(user10.getAllPackages().keySet()));
+ assertEquals(
+ makeSet(PackageWithUser.of(USER_0, LAUNCHER_1),
+ PackageWithUser.of(USER_0, LAUNCHER_2)),
+ set(user0.getAllLaunchers().keySet()));
+ assertEquals(
+ makeSet(),
+ set(user10.getAllLaunchers().keySet()));
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
"s0_2");
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
@@ -2765,16 +3782,19 @@
mService.saveDirtyInfo();
// More remove.
- mService.cleanUpPackageLocked(CALLING_PACKAGE_1, USER_10);
+ uninstallPackage(USER_10, CALLING_PACKAGE_1);
+ mService.cleanUpPackageLocked(CALLING_PACKAGE_1, USER_10, USER_10);
assertEquals(makeSet(CALLING_PACKAGE_2),
- set(user0.getPackages().keySet()));
+ set(user0.getAllPackages().keySet()));
assertEquals(makeSet(),
- set(user10.getPackages().keySet()));
- assertEquals(makeSet(LAUNCHER_1, LAUNCHER_2),
- set(user0.getLaunchers().keySet()));
+ set(user10.getAllPackages().keySet()));
+ assertEquals(
+ makeSet(PackageWithUser.of(USER_0, LAUNCHER_1),
+ PackageWithUser.of(USER_0, LAUNCHER_2)),
+ set(user0.getAllLaunchers().keySet()));
assertEquals(makeSet(),
- set(user10.getLaunchers().keySet()));
+ set(user10.getAllLaunchers().keySet()));
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
"s0_2");
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
@@ -2789,7 +3809,1567 @@
mService.saveDirtyInfo();
}
- // TODO Detailed test for hasShortcutPermissionInner().
+ public void testHandleGonePackage_crossProfile() {
+ // Create some shortcuts.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
- // TODO Add tests for the command line functions too.
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_P0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_P0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_P0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s2", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s3", USER_0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_10));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
+
+ // Pin some.
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s1"), HANDLE_USER_0);
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s2"), UserHandle.of(USER_P0));
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
+ list("s3"), HANDLE_USER_0);
+ });
+
+ runWithCaller(LAUNCHER_1, USER_P0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s2"), HANDLE_USER_0);
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s3"), UserHandle.of(USER_P0));
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
+ list("s1"), HANDLE_USER_0);
+ });
+
+ runWithCaller(LAUNCHER_1, USER_10, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s3"), HANDLE_USER_10);
+ });
+
+ // Check the state.
+
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_P0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_P0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_P0));
+
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_2, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s2", USER_0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_2, "s3", USER_0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_10));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
+
+ // Make sure all the information is persisted.
+ mService.saveDirtyInfo();
+ initService();
+ mService.handleUnlockUser(USER_0);
+ mService.handleUnlockUser(USER_P0);
+ mService.handleUnlockUser(USER_10);
+
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_P0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_P0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_P0));
+
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_2, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s2", USER_0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_2, "s3", USER_0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_10));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
+
+ // Start uninstalling.
+ uninstallPackage(USER_10, LAUNCHER_1);
+ mService.cleanupGonePackages(USER_10);
+
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_P0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_P0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_P0));
+
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_2, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s2", USER_0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_2, "s3", USER_0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_10));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
+
+ // Uninstall.
+ uninstallPackage(USER_10, CALLING_PACKAGE_1);
+ mService.cleanupGonePackages(USER_10);
+
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_P0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_P0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_P0));
+
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_2, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s2", USER_0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_2, "s3", USER_0));
+
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
+
+ uninstallPackage(USER_P0, LAUNCHER_1);
+ mService.cleanupGonePackages(USER_0);
+
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_P0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_P0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_P0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s2", USER_0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_2, "s3", USER_0));
+
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
+
+ mService.cleanupGonePackages(USER_P0);
+
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_P0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_P0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_P0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s2", USER_0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_2, "s3", USER_0));
+
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
+
+ uninstallPackage(USER_P0, CALLING_PACKAGE_1);
+
+ mService.saveDirtyInfo();
+ initService();
+ mService.handleUnlockUser(USER_0);
+ mService.handleUnlockUser(USER_P0);
+ mService.handleUnlockUser(USER_10);
+
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_0));
+
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_P0));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_P0));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_P0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s2", USER_0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_2, "s3", USER_0));
+
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
+
+ // Uninstall
+ uninstallPackage(USER_0, LAUNCHER_1);
+
+ mService.saveDirtyInfo();
+ initService();
+ mService.handleUnlockUser(USER_0);
+ mService.handleUnlockUser(USER_P0);
+ mService.handleUnlockUser(USER_10);
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_0));
+
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_P0));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_P0));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_P0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s2", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s3", USER_0));
+
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
+
+ uninstallPackage(USER_0, CALLING_PACKAGE_2);
+
+ mService.saveDirtyInfo();
+ initService();
+ mService.handleUnlockUser(USER_0);
+ mService.handleUnlockUser(USER_P0);
+ mService.handleUnlockUser(USER_10);
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_0));
+
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_P0));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_P0));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_P0));
+
+ assertNull(getPackageShortcut(CALLING_PACKAGE_2, "s1", USER_0));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_2, "s2", USER_0));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_2, "s3", USER_0));
+
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
+ }
+
+ private void checkCanRestoreTo(boolean expected, ShortcutPackageInfo spi,
+ int version, String... signatures) {
+ assertEquals(expected, spi.canRestoreTo(mService, genPackage(
+ "dummy", /* uid */ 0, version, signatures)));
+ }
+
+ public void testCanRestoreTo() {
+ addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 10, "sig1");
+ addPackage(CALLING_PACKAGE_2, CALLING_UID_1, 10, "sig1", "sig2");
+
+ final ShortcutPackageInfo spi1 = ShortcutPackageInfo.generateForInstalledPackage(
+ mService, CALLING_PACKAGE_1, USER_0);
+ final ShortcutPackageInfo spi2 = ShortcutPackageInfo.generateForInstalledPackage(
+ mService, CALLING_PACKAGE_2, USER_0);
+
+ checkCanRestoreTo(true, spi1, 10, "sig1");
+ checkCanRestoreTo(true, spi1, 10, "x", "sig1");
+ checkCanRestoreTo(true, spi1, 10, "sig1", "y");
+ checkCanRestoreTo(true, spi1, 10, "x", "sig1", "y");
+ checkCanRestoreTo(true, spi1, 11, "sig1");
+
+ checkCanRestoreTo(false, spi1, 10 /* empty */);
+ checkCanRestoreTo(false, spi1, 10, "x");
+ checkCanRestoreTo(false, spi1, 10, "x", "y");
+ checkCanRestoreTo(false, spi1, 10, "x");
+ checkCanRestoreTo(false, spi1, 9, "sig1");
+
+ checkCanRestoreTo(true, spi2, 10, "sig1", "sig2");
+ checkCanRestoreTo(true, spi2, 10, "sig2", "sig1");
+ checkCanRestoreTo(true, spi2, 10, "x", "sig1", "sig2");
+ checkCanRestoreTo(true, spi2, 10, "x", "sig2", "sig1");
+ checkCanRestoreTo(true, spi2, 10, "sig1", "sig2", "y");
+ checkCanRestoreTo(true, spi2, 10, "sig2", "sig1", "y");
+ checkCanRestoreTo(true, spi2, 10, "x", "sig1", "sig2", "y");
+ checkCanRestoreTo(true, spi2, 10, "x", "sig2", "sig1", "y");
+ checkCanRestoreTo(true, spi2, 11, "x", "sig2", "sig1", "y");
+
+ checkCanRestoreTo(false, spi2, 10, "sig1", "sig2x");
+ checkCanRestoreTo(false, spi2, 10, "sig2", "sig1x");
+ checkCanRestoreTo(false, spi2, 10, "x", "sig1x", "sig2");
+ checkCanRestoreTo(false, spi2, 10, "x", "sig2x", "sig1");
+ checkCanRestoreTo(false, spi2, 10, "sig1", "sig2x", "y");
+ checkCanRestoreTo(false, spi2, 10, "sig2", "sig1x", "y");
+ checkCanRestoreTo(false, spi2, 10, "x", "sig1x", "sig2", "y");
+ checkCanRestoreTo(false, spi2, 10, "x", "sig2x", "sig1", "y");
+ checkCanRestoreTo(false, spi2, 11, "x", "sig2x", "sig1", "y");
+ }
+
+ public void testHandlePackageDelete() {
+ setCaller(CALLING_PACKAGE_1, USER_0);
+ assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
+
+ setCaller(CALLING_PACKAGE_2, USER_0);
+ assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
+
+ setCaller(CALLING_PACKAGE_3, USER_0);
+ assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
+
+ setCaller(CALLING_PACKAGE_1, USER_10);
+ assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
+
+ setCaller(CALLING_PACKAGE_2, USER_10);
+ assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
+
+ setCaller(CALLING_PACKAGE_3, USER_10);
+ assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
+
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10));
+
+ uninstallPackage(USER_0, CALLING_PACKAGE_1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageDeleteIntent(CALLING_PACKAGE_1, USER_0));
+
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10));
+
+ uninstallPackage(USER_10, CALLING_PACKAGE_2);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageDeleteIntent(CALLING_PACKAGE_2, USER_10));
+
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10));
+
+ mInjectedPackages.remove(CALLING_PACKAGE_1);
+ mInjectedPackages.remove(CALLING_PACKAGE_3);
+
+ mService.handleUnlockUser(USER_0);
+
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0));
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10));
+
+ mService.handleUnlockUser(USER_10);
+
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0));
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_0));
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10));
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10));
+ }
+
+ private void backupAndRestore() {
+ int prevUid = mInjectedCallingUid;
+
+ mInjectedCallingUid = Process.SYSTEM_UID; // Only system can call it.
+
+ dumpsysOnLogcat("Before backup");
+
+ final byte[] payload = mService.getBackupPayload(USER_0);
+ if (ENABLE_DUMP) {
+ final String xml = new String(payload);
+ Log.i(TAG, "Backup payload:");
+ for (String line : xml.split("\n")) {
+ Log.i(TAG, line);
+ }
+ }
+
+ // Before doing anything else, uninstall all packages.
+ for (int userId : list(USER_0, USER_P0)) {
+ for (String pkg : list(CALLING_PACKAGE_1, CALLING_PACKAGE_2, CALLING_PACKAGE_3,
+ LAUNCHER_1, LAUNCHER_2, LAUNCHER_3)) {
+ uninstallPackage(userId, pkg);
+ }
+ }
+
+ shutdownServices();
+
+ deleteAllSavedFiles();
+
+ initService();
+ mService.applyRestore(payload, USER_0);
+
+ // handleUnlockUser will perform the gone package check, but it shouldn't remove
+ // shadow information.
+ mService.handleUnlockUser(USER_0);
+
+ dumpsysOnLogcat("After restore");
+
+ mInjectedCallingUid = prevUid;
+ }
+
+ private void prepareCrossProfileDataSet() {
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"),
+ makeShortcut("s4"), makeShortcut("s5"), makeShortcut("s6"))));
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"),
+ makeShortcut("s4"), makeShortcut("s5"), makeShortcut("s6"))));
+ });
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"),
+ makeShortcut("s4"), makeShortcut("s5"), makeShortcut("s6"))));
+ });
+ runWithCaller(CALLING_PACKAGE_4, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list()));
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"),
+ makeShortcut("s4"), makeShortcut("s5"), makeShortcut("s6"))));
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("x1"), makeShortcut("x2"), makeShortcut("x3"),
+ makeShortcut("x4"), makeShortcut("x5"), makeShortcut("x6"))));
+ });
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1"), HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("s1", "s2"), HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_3, list("s1", "s2", "s3"), HANDLE_USER_0);
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1", "s4"), HANDLE_USER_P0);
+ });
+ runWithCaller(LAUNCHER_2, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("s2", "s3"), HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_3, list("s2", "s3", "s4"), HANDLE_USER_0);
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s2", "s5"), HANDLE_USER_P0);
+ });
+ runWithCaller(LAUNCHER_3, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("s3", "s4"), HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_3, list("s3", "s4", "s5"), HANDLE_USER_0);
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3", "s6"), HANDLE_USER_P0);
+ });
+ runWithCaller(LAUNCHER_4, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list(), HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list(), HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_3, list(), HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_4, list(), HANDLE_USER_0);
+ });
+
+ // Launcher on a managed profile is referring ot user 0!
+ runWithCaller(LAUNCHER_1, USER_P0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3", "s4"), HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("s3", "s4", "s5"), HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_3, list("s3", "s4", "s5", "s6"),
+ HANDLE_USER_0);
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s4", "s1"), HANDLE_USER_P0);
+ });
+ runWithCaller(LAUNCHER_1, USER_10, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("x4", "x5"), HANDLE_USER_10);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("x4", "x5", "x6"), HANDLE_USER_10);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_3, list("x4", "x5", "x6", "x1"),
+ HANDLE_USER_10);
+ });
+
+ // Then remove some dynamic shortcuts.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+ runWithCaller(CALLING_PACKAGE_4, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list()));
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("x1"), makeShortcut("x2"), makeShortcut("x3"))));
+ });
+ }
+
+ private void prepareForBackupTest() {
+
+ prepareCrossProfileDataSet();
+
+ backupAndRestore();
+ }
+
+ private void assertExistsAndShadow(ShortcutPackageItem spi) {
+ assertNotNull(spi);
+ assertTrue(spi.getPackageInfo().isShadow());
+ }
+
+ /**
+ * Make sure the backup data doesn't have the following information:
+ * - Launchers on other users.
+ * - Non-backup app information.
+ *
+ * But restores all other infomation.
+ *
+ * It also omits the following pieces of information, but that's tested in
+ * {@link #testShortcutInfoSaveAndLoad_forBackup}.
+ * - Unpinned dynamic shortcuts
+ * - Bitmaps
+ */
+ public void testBackupAndRestore() {
+ prepareForBackupTest();
+
+ checkBackupAndRestore_success();
+ }
+
+ public void testBackupAndRestore_backupRestoreTwice() {
+ prepareForBackupTest();
+
+ // Note doing a backup & restore again here shouldn't affect the result.
+ dumpsysOnLogcat("Before second backup");
+
+ backupAndRestore();
+
+ dumpsysOnLogcat("After second backup");
+
+ checkBackupAndRestore_success();
+ }
+
+ public void testBackupAndRestore_backupRestoreMultiple() {
+ prepareForBackupTest();
+
+ // Note doing a backup & restore again here shouldn't affect the result.
+ backupAndRestore();
+
+ // This also shouldn't affect the result.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"),
+ makeShortcut("s4"), makeShortcut("s5"), makeShortcut("s6"))));
+ });
+
+ backupAndRestore();
+
+ checkBackupAndRestore_success();
+ }
+
+ public void testBackupAndRestore_restoreToNewVersion() {
+ prepareForBackupTest();
+
+ // Note doing a backup & restore again here shouldn't affect the result.
+ backupAndRestore();
+
+ addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 2);
+ addPackage(LAUNCHER_1, LAUNCHER_UID_1, 5);
+
+ checkBackupAndRestore_success();
+ }
+
+ public void testBackupAndRestore_restoreToSuperSetSignatures() {
+ prepareForBackupTest();
+
+ // Note doing a backup & restore again here shouldn't affect the result.
+ backupAndRestore();
+
+ // Change package signatures.
+ addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 1, "sigx", CALLING_PACKAGE_1);
+ addPackage(LAUNCHER_1, LAUNCHER_UID_1, 4, LAUNCHER_1, "sigy");
+
+ checkBackupAndRestore_success();
+ }
+
+ private void checkBackupAndRestore_success() {
+ // Make sure non-system user is not restored.
+ final ShortcutUser userP0 = mService.getUserShortcutsLocked(USER_P0);
+ assertEquals(0, userP0.getAllPackages().size());
+ assertEquals(0, userP0.getAllLaunchers().size());
+
+ // Make sure only "allowBackup" apps are restored, and are shadow.
+ final ShortcutUser user0 = mService.getUserShortcutsLocked(USER_0);
+ assertExistsAndShadow(user0.getAllPackages().get(CALLING_PACKAGE_1));
+ assertExistsAndShadow(user0.getAllPackages().get(CALLING_PACKAGE_2));
+ assertExistsAndShadow(user0.getAllLaunchers().get(PackageWithUser.of(USER_0, LAUNCHER_1)));
+ assertExistsAndShadow(user0.getAllLaunchers().get(PackageWithUser.of(USER_0, LAUNCHER_2)));
+
+ assertNull(user0.getAllPackages().get(CALLING_PACKAGE_3));
+ assertNull(user0.getAllLaunchers().get(PackageWithUser.of(USER_0, LAUNCHER_3)));
+ assertNull(user0.getAllLaunchers().get(PackageWithUser.of(USER_P0, LAUNCHER_1)));
+
+ installPackage(USER_0, CALLING_PACKAGE_1);
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertShortcutIds(assertAllPinned(
+ mManager.getPinnedShortcuts()),
+ "s1", "s2");
+ });
+
+ installPackage(USER_0, LAUNCHER_1);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
+ /* empty, not restored */ );
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+ /* empty, not restored */ );
+
+ assertEquals(0, mLauncherApps.getShortcuts(QUERY_ALL, HANDLE_USER_P0).size());
+ });
+
+ installPackage(USER_0, CALLING_PACKAGE_2);
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertShortcutIds(assertAllPinned(
+ mManager.getPinnedShortcuts()),
+ "s1", "s2", "s3");
+ });
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
+ "s1", "s2");
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+ /* empty, not restored */ );
+
+ assertEquals(0, mLauncherApps.getShortcuts(QUERY_ALL, HANDLE_USER_P0).size());
+ });
+
+ // 3 shouldn't be backed up, so no pinned shortcuts.
+ installPackage(USER_0, CALLING_PACKAGE_3);
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertEquals(0, mManager.getPinnedShortcuts().size());
+ });
+
+ // Launcher on a different profile shouldn't be restored.
+ runWithCaller(LAUNCHER_1, USER_P0, () -> {
+ assertEquals(0,
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)
+ .size());
+ assertEquals(0,
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)
+ .size());
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+ /* wasn't restored, so still empty */ );
+ });
+
+ // Package on a different profile, no restore.
+ installPackage(USER_P0, CALLING_PACKAGE_1);
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertEquals(0, mManager.getPinnedShortcuts().size());
+ });
+
+ // Restore launcher 2 on user 0.
+ installPackage(USER_0, LAUNCHER_2);
+ runWithCaller(LAUNCHER_2, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)),
+ "s2");
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
+ "s2", "s3");
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+ /* wasn't restored, so still empty */ );
+
+ assertEquals(0, mLauncherApps.getShortcuts(QUERY_ALL, HANDLE_USER_P0).size());
+ });
+
+
+ // Restoration of launcher2 shouldn't affect other packages; so do the same checks and
+ // make sure they still have the same result.
+ installPackage(USER_0, CALLING_PACKAGE_1);
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertShortcutIds(assertAllPinned(
+ mManager.getPinnedShortcuts()),
+ "s1", "s2");
+ });
+
+ installPackage(USER_0, LAUNCHER_1);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
+ "s1", "s2");
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+ /* wasn't restored, so still empty */ );
+
+ assertEquals(0, mLauncherApps.getShortcuts(QUERY_ALL, HANDLE_USER_P0).size());
+ });
+
+ installPackage(USER_0, CALLING_PACKAGE_2);
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertShortcutIds(assertAllPinned(
+ mManager.getPinnedShortcuts()),
+ "s1", "s2", "s3");
+ });
+ }
+
+ public void testBackupAndRestore_publisherLowerVersion() {
+ prepareForBackupTest();
+
+ // Note doing a backup & restore again here shouldn't affect the result.
+ backupAndRestore();
+
+ addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 0); // Lower version
+
+ checkBackupAndRestore_publisherNotRestored();
+ }
+
+ public void testBackupAndRestore_publisherWrongSignature() {
+ prepareForBackupTest();
+
+ // Note doing a backup & restore again here shouldn't affect the result.
+ backupAndRestore();
+
+ addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 10, "sigx"); // different signature
+
+ checkBackupAndRestore_publisherNotRestored();
+ }
+
+ public void testBackupAndRestore_publisherNoLongerBackupTarget() {
+ prepareForBackupTest();
+
+ // Note doing a backup & restore again here shouldn't affect the result.
+ backupAndRestore();
+
+ updatePackageInfo(CALLING_PACKAGE_1,
+ pi -> pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_ALLOW_BACKUP);
+
+ checkBackupAndRestore_publisherNotRestored();
+ }
+
+ private void checkBackupAndRestore_publisherNotRestored() {
+ installPackage(USER_0, CALLING_PACKAGE_1);
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertEquals(0, mManager.getPinnedShortcuts().size());
+ });
+
+ installPackage(USER_0, CALLING_PACKAGE_2);
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertShortcutIds(assertAllPinned(
+ mManager.getPinnedShortcuts()),
+ "s1", "s2", "s3");
+ });
+
+ installPackage(USER_0, LAUNCHER_1);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+ /* empty */);
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
+ "s1", "s2");
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+ /* empty */);
+ });
+ installPackage(USER_0, LAUNCHER_2);
+ runWithCaller(LAUNCHER_2, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+ /* empty */);
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
+ "s2", "s3");
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+ /* empty */);
+ });
+
+ installPackage(USER_0, CALLING_PACKAGE_3);
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertEquals(0, mManager.getPinnedShortcuts().size());
+ });
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+ /* empty */);
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
+ "s1", "s2");
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+ /* empty */);
+ });
+ runWithCaller(LAUNCHER_2, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+ /* empty */);
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
+ "s2", "s3");
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+ /* empty */);
+ });
+ }
+
+ public void testBackupAndRestore_launcherLowerVersion() {
+ prepareForBackupTest();
+
+ // Note doing a backup & restore again here shouldn't affect the result.
+ backupAndRestore();
+
+ addPackage(LAUNCHER_1, LAUNCHER_UID_1, 0); // Lower version
+
+ checkBackupAndRestore_launcherNotRestored();
+ }
+
+ public void testBackupAndRestore_launcherWrongSignature() {
+ prepareForBackupTest();
+
+ // Note doing a backup & restore again here shouldn't affect the result.
+ backupAndRestore();
+
+ addPackage(LAUNCHER_1, LAUNCHER_UID_1, 10, "sigx"); // different signature
+
+ checkBackupAndRestore_launcherNotRestored();
+ }
+
+ public void testBackupAndRestore_launcherNoLongerBackupTarget() {
+ prepareForBackupTest();
+
+ // Note doing a backup & restore again here shouldn't affect the result.
+ backupAndRestore();
+
+ updatePackageInfo(LAUNCHER_1,
+ pi -> pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_ALLOW_BACKUP);
+
+ checkBackupAndRestore_launcherNotRestored();
+ }
+
+ private void checkBackupAndRestore_launcherNotRestored() {
+ installPackage(USER_0, CALLING_PACKAGE_1);
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+
+ // s1 was pinned by launcher 1, which is not restored, yet, so we still see "s1" here.
+ assertShortcutIds(assertAllPinned(
+ mManager.getPinnedShortcuts()),
+ "s1", "s2");
+ });
+
+ installPackage(USER_0, CALLING_PACKAGE_2);
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertShortcutIds(assertAllPinned(
+ mManager.getPinnedShortcuts()),
+ "s1", "s2", "s3");
+ });
+
+ // Now we try to restore launcher 1. Then we realize it's not restorable, so L1 has no pinned
+ // shortcuts.
+ installPackage(USER_0, LAUNCHER_1);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+ /* empty */);
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
+ /* empty */);
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+ /* empty */);
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+
+ // Now CALLING_PACKAGE_1 realizes "s1" is no longer pinned.
+ assertShortcutIds(assertAllPinned(
+ mManager.getPinnedShortcuts()),
+ "s2");
+ });
+
+ installPackage(USER_0, LAUNCHER_2);
+ runWithCaller(LAUNCHER_2, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)),
+ "s2");
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
+ "s2", "s3");
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+ /* empty */);
+ });
+
+ installPackage(USER_0, CALLING_PACKAGE_3);
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertEquals(0, mManager.getPinnedShortcuts().size());
+ });
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+ /* empty */);
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
+ /* empty */);
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+ /* empty */);
+ });
+ runWithCaller(LAUNCHER_2, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0)),
+ "s2");
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
+ "s2", "s3");
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+ /* empty */);
+ });
+ }
+
+ public void testBackupAndRestore_launcherAndPackageNoLongerBackupTarget() {
+ prepareForBackupTest();
+
+ // Note doing a backup & restore again here shouldn't affect the result.
+ backupAndRestore();
+
+ updatePackageInfo(CALLING_PACKAGE_1,
+ pi -> pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_ALLOW_BACKUP);
+
+ updatePackageInfo(LAUNCHER_1,
+ pi -> pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_ALLOW_BACKUP);
+
+ checkBackupAndRestore_publisherAndLauncherNotRestored();
+ }
+
+ private void checkBackupAndRestore_publisherAndLauncherNotRestored() {
+ installPackage(USER_0, CALLING_PACKAGE_1);
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertEquals(0, mManager.getPinnedShortcuts().size());
+ });
+
+ installPackage(USER_0, CALLING_PACKAGE_2);
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertShortcutIds(assertAllPinned(
+ mManager.getPinnedShortcuts()),
+ "s1", "s2", "s3");
+ });
+
+ installPackage(USER_0, LAUNCHER_1);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+ /* empty */);
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
+ /* empty */);
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+ /* empty */);
+ });
+ installPackage(USER_0, LAUNCHER_2);
+ runWithCaller(LAUNCHER_2, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+ /* empty */);
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
+ "s2", "s3");
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+ /* empty */);
+ });
+
+ // Because launcher 1 wasn't restored, "s1" is no longer pinned.
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertShortcutIds(assertAllPinned(
+ mManager.getPinnedShortcuts()),
+ "s2", "s3");
+ });
+
+ installPackage(USER_0, CALLING_PACKAGE_3);
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertEquals(0, mManager.getPinnedShortcuts().size());
+ });
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+ /* empty */);
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0))
+ /* empty */);
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+ /* empty */);
+ });
+ runWithCaller(LAUNCHER_2, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0))
+ /* empty */);
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_2), HANDLE_USER_0)),
+ "s2", "s3");
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildAllQuery(CALLING_PACKAGE_3), HANDLE_USER_0))
+ /* empty */);
+ });
+ }
+
+ public void testSaveAndLoad_crossProfile() {
+ prepareCrossProfileDataSet();
+
+ dumpsysOnLogcat("Before save & load");
+
+ mService.saveDirtyInfo();
+ initService();
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts()),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()),
+ "s1", "s2", "s3", "s4");
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts()),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()),
+ "s1", "s2", "s3", "s4", "s5");
+ });
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts()),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()),
+ "s1", "s2", "s3", "s4", "s5", "s6");
+ });
+ runWithCaller(CALLING_PACKAGE_4, USER_0, () -> {
+ assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts())
+ /* empty */);
+ assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts())
+ /* empty */);
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts()),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()),
+ "s1", "s2", "s3", "s4", "s5", "s6");
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_P0, () -> {
+ assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts())
+ /* empty */);
+ assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts())
+ /* empty */);
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertShortcutIds(assertAllDynamic(mManager.getDynamicShortcuts()),
+ "x1", "x2", "x3");
+ assertShortcutIds(assertAllPinned(mManager.getPinnedShortcuts()),
+ "x4", "x5");
+ });
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_0),
+ "s1");
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_0),
+ "s1", "s2");
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_3), HANDLE_USER_0),
+ "s1", "s2", "s3");
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_4), HANDLE_USER_0)
+ /* empty */);
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_P0),
+ "s1", "s4");
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_P0)
+ /* empty */);
+ TestUtils.assertExpectException(
+ SecurityException.class, "", () -> {
+ mLauncherApps.getShortcuts(
+ buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_10);
+ });
+ });
+ runWithCaller(LAUNCHER_2, USER_0, () -> {
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_0),
+ "s2");
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_0),
+ "s2", "s3");
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_3), HANDLE_USER_0),
+ "s2", "s3", "s4");
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_4), HANDLE_USER_0)
+ /* empty */);
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_P0),
+ "s2", "s5");
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_P0)
+ /* empty */);
+ });
+ runWithCaller(LAUNCHER_3, USER_0, () -> {
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_0),
+ "s3");
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_0),
+ "s3", "s4");
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_3), HANDLE_USER_0),
+ "s3", "s4", "s5");
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_4), HANDLE_USER_0)
+ /* empty */);
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_P0),
+ "s3", "s6");
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_P0)
+ /* empty */);
+ });
+ runWithCaller(LAUNCHER_4, USER_0, () -> {
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_0)
+ /* empty */);
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_0)
+ /* empty */);
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_3), HANDLE_USER_0)
+ /* empty */);
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_4), HANDLE_USER_0)
+ /* empty */);
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_P0)
+ /* empty */);
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_P0)
+ /* empty */);
+ });
+ runWithCaller(LAUNCHER_1, USER_P0, () -> {
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_0),
+ "s3", "s4");
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_0),
+ "s3", "s4", "s5");
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_3), HANDLE_USER_0),
+ "s3", "s4", "s5", "s6");
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_P0),
+ "s1", "s4");
+ TestUtils.assertExpectException(
+ SecurityException.class, "unrelated profile", () -> {
+ mLauncherApps.getShortcuts(
+ buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_10);
+ });
+ });
+ runWithCaller(LAUNCHER_1, USER_10, () -> {
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_1), HANDLE_USER_10),
+ "x4", "x5");
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_2), HANDLE_USER_10)
+ /* empty */);
+ assertShortcutIds(
+ mLauncherApps.getShortcuts(buildPinnedQuery(CALLING_PACKAGE_3), HANDLE_USER_10)
+ /* empty */);
+ TestUtils.assertExpectException(
+ SecurityException.class, "unrelated profile", () -> {
+ mLauncherApps.getShortcuts(
+ buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_0);
+ });
+ TestUtils.assertExpectException(
+ SecurityException.class, "unrelated profile", () -> {
+ mLauncherApps.getShortcuts(
+ buildAllQuery(CALLING_PACKAGE_1), HANDLE_USER_P0);
+ });
+ });
+ }
+
+ // ShortcutInfo tests
+
+ public void testShortcutInfoMissingMandatoryFields() {
+ TestUtils.assertExpectException(
+ IllegalArgumentException.class,
+ "ID must be provided",
+ () -> new ShortcutInfo.Builder(getTestContext()).build());
+ TestUtils.assertExpectException(
+ IllegalArgumentException.class,
+ "title must be provided",
+ () -> new ShortcutInfo.Builder(getTestContext()).setId("id").build()
+ .enforceMandatoryFields());
+ TestUtils.assertExpectException(
+ NullPointerException.class,
+ "Intent must be provided",
+ () -> new ShortcutInfo.Builder(getTestContext()).setId("id").setTitle("x").build()
+ .enforceMandatoryFields());
+ }
+
+ public void testShortcutInfoParcel() {
+ ShortcutInfo si = parceled(new ShortcutInfo.Builder(getTestContext())
+ .setId("id")
+ .setTitle("title")
+ .setIntent(makeIntent("action", ShortcutActivity.class))
+ .build());
+ assertEquals(getTestContext().getPackageName(), si.getPackageName());
+ assertEquals("id", si.getId());
+ assertEquals("title", si.getTitle());
+ assertEquals("action", si.getIntent().getAction());
+
+ PersistableBundle pb = new PersistableBundle();
+ pb.putInt("k", 1);
+
+ si = new ShortcutInfo.Builder(getTestContext())
+ .setId("id")
+ .setActivityComponent(new ComponentName("a", "b"))
+ .setIcon(Icon.createWithContentUri("content://a.b.c/"))
+ .setTitle("title")
+ .setText("text")
+ .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+ .setWeight(123)
+ .setExtras(pb)
+ .build();
+ si.addFlags(ShortcutInfo.FLAG_PINNED);
+ si.setBitmapPath("abc");
+ si.setIconResourceId(456);
+
+ si = parceled(si);
+
+ assertEquals(getTestContext().getPackageName(), si.getPackageName());
+ assertEquals("id", si.getId());
+ assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
+ assertEquals("content://a.b.c/", si.getIcon().getUriString());
+ assertEquals("title", si.getTitle());
+ assertEquals("text", si.getText());
+ assertEquals("action", si.getIntent().getAction());
+ assertEquals("val", si.getIntent().getStringExtra("key"));
+ assertEquals(123, si.getWeight());
+ assertEquals(1, si.getExtras().getInt("k"));
+
+ assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
+ assertEquals("abc", si.getBitmapPath());
+ assertEquals(456, si.getIconResourceId());
+ }
+
+ public void testShortcutInfoClone() {
+ PersistableBundle pb = new PersistableBundle();
+ pb.putInt("k", 1);
+ ShortcutInfo sorig = new ShortcutInfo.Builder(getTestContext())
+ .setId("id")
+ .setActivityComponent(new ComponentName("a", "b"))
+ .setIcon(Icon.createWithContentUri("content://a.b.c/"))
+ .setTitle("title")
+ .setText("text")
+ .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+ .setWeight(123)
+ .setExtras(pb)
+ .build();
+ sorig.addFlags(ShortcutInfo.FLAG_PINNED);
+ sorig.setBitmapPath("abc");
+ sorig.setIconResourceId(456);
+
+ ShortcutInfo si = sorig.clone(/* clone flags*/ 0);
+
+ assertEquals(getTestContext().getPackageName(), si.getPackageName());
+ assertEquals("id", si.getId());
+ assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
+ assertEquals("content://a.b.c/", si.getIcon().getUriString());
+ assertEquals("title", si.getTitle());
+ assertEquals("text", si.getText());
+ assertEquals("action", si.getIntent().getAction());
+ assertEquals("val", si.getIntent().getStringExtra("key"));
+ assertEquals(123, si.getWeight());
+ assertEquals(1, si.getExtras().getInt("k"));
+
+ assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
+ assertEquals("abc", si.getBitmapPath());
+ assertEquals(456, si.getIconResourceId());
+
+ si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR);
+
+ assertEquals(getTestContext().getPackageName(), si.getPackageName());
+ assertEquals("id", si.getId());
+ assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
+ assertEquals(null, si.getIcon());
+ assertEquals("title", si.getTitle());
+ assertEquals("text", si.getText());
+ assertEquals("action", si.getIntent().getAction());
+ assertEquals("val", si.getIntent().getStringExtra("key"));
+ assertEquals(123, si.getWeight());
+ assertEquals(1, si.getExtras().getInt("k"));
+
+ assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
+ assertEquals(null, si.getBitmapPath());
+ assertEquals(0, si.getIconResourceId());
+
+ si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
+
+ assertEquals(getTestContext().getPackageName(), si.getPackageName());
+ assertEquals("id", si.getId());
+ assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
+ assertEquals(null, si.getIcon());
+ assertEquals("title", si.getTitle());
+ assertEquals("text", si.getText());
+ assertEquals(null, si.getIntent());
+ assertEquals(123, si.getWeight());
+ assertEquals(1, si.getExtras().getInt("k"));
+
+ assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
+ assertEquals(null, si.getBitmapPath());
+ assertEquals(0, si.getIconResourceId());
+
+ si = sorig.clone(ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
+
+ assertEquals(getTestContext().getPackageName(), si.getPackageName());
+ assertEquals("id", si.getId());
+ assertEquals(null, si.getActivityComponent());
+ assertEquals(null, si.getIcon());
+ assertEquals(null, si.getTitle());
+ assertEquals(null, si.getText());
+ assertEquals(null, si.getIntent());
+ assertEquals(0, si.getWeight());
+ assertEquals(null, si.getExtras());
+
+ assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_KEY_FIELDS_ONLY, si.getFlags());
+ assertEquals(null, si.getBitmapPath());
+ assertEquals(0, si.getIconResourceId());
+ }
+
+ public void testShortcutInfoCopyNonNullFieldsFrom() throws InterruptedException {
+ PersistableBundle pb = new PersistableBundle();
+ pb.putInt("k", 1);
+ ShortcutInfo sorig = new ShortcutInfo.Builder(getTestContext())
+ .setId("id")
+ .setActivityComponent(new ComponentName("a", "b"))
+ .setIcon(Icon.createWithContentUri("content://a.b.c/"))
+ .setTitle("title")
+ .setText("text")
+ .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+ .setWeight(123)
+ .setExtras(pb)
+ .build();
+ sorig.addFlags(ShortcutInfo.FLAG_PINNED);
+ sorig.setBitmapPath("abc");
+ sorig.setIconResourceId(456);
+
+ ShortcutInfo si;
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setActivityComponent(new ComponentName("x", "y")).build());
+ assertEquals(new ComponentName("x", "y"), si.getActivityComponent());
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setIcon(Icon.createWithContentUri("content://x.y.z/")).build());
+ assertEquals("content://x.y.z/", si.getIcon().getUriString());
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setTitle("xyz").build());
+ assertEquals("xyz", si.getTitle());
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setText("xxx").build());
+ assertEquals("xxx", si.getText());
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setIntent(makeIntent("action2", ShortcutActivity.class)).build());
+ assertEquals("action2", si.getIntent().getAction());
+ assertEquals(null, si.getIntent().getStringExtra("key"));
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setIntent(makeIntent("action3", ShortcutActivity.class, "key", "x")).build());
+ assertEquals("action3", si.getIntent().getAction());
+ assertEquals("x", si.getIntent().getStringExtra("key"));
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setWeight(999).build());
+ assertEquals(999, si.getWeight());
+
+
+ PersistableBundle pb2 = new PersistableBundle();
+ pb2.putInt("x", 99);
+
+ si = sorig.clone(/* flags=*/ 0);
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setExtras(pb2).build());
+ assertEquals(99, si.getExtras().getInt("x"));
+
+ final long timestamp = si.getLastChangedTimestamp();
+ Thread.sleep(2);
+
+ si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
+ .setTitle("xyz").build());
+
+ assertTrue(si.getLastChangedTimestamp() > timestamp);
+ }
+
+ public void testShortcutInfoSaveAndLoad() throws InterruptedException {
+ setCaller(CALLING_PACKAGE_1, USER_0);
+
+ final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+ getTestContext().getResources(), R.drawable.black_32x32));
+
+ PersistableBundle pb = new PersistableBundle();
+ pb.putInt("k", 1);
+ ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext)
+ .setId("id")
+ .setActivityComponent(new ComponentName(mClientContext, ShortcutActivity2.class))
+ .setIcon(bmp32x32)
+ .setTitle("title")
+ .setText("text")
+ .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+ .setWeight(123)
+ .setExtras(pb)
+ .build();
+
+ mManager.addDynamicShortcut(sorig);
+
+ Thread.sleep(2);
+ final long now = System.currentTimeMillis();
+
+ // Save and load.
+ mService.saveDirtyInfo();
+ initService();
+ mService.handleUnlockUser(USER_0);
+
+ ShortcutInfo si;
+ si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id", USER_0);
+
+ assertEquals(CALLING_PACKAGE_1, si.getPackageName());
+ assertEquals("id", si.getId());
+ assertEquals(ShortcutActivity2.class.getName(), si.getActivityComponent().getClassName());
+ assertEquals(null, si.getIcon());
+ assertEquals("title", si.getTitle());
+ assertEquals("text", si.getText());
+ assertEquals("action", si.getIntent().getAction());
+ assertEquals("val", si.getIntent().getStringExtra("key"));
+ assertEquals(123, si.getWeight());
+ assertEquals(1, si.getExtras().getInt("k"));
+
+ assertEquals(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_HAS_ICON_FILE, si.getFlags());
+ assertNotNull(si.getBitmapPath()); // Something should be set.
+ assertEquals(0, si.getIconResourceId());
+ assertTrue(si.getLastChangedTimestamp() < now);
+ }
+
+ public void testShortcutInfoSaveAndLoad_forBackup() {
+ setCaller(CALLING_PACKAGE_1, USER_0);
+
+ final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+ getTestContext().getResources(), R.drawable.black_32x32));
+
+ PersistableBundle pb = new PersistableBundle();
+ pb.putInt("k", 1);
+ ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext)
+ .setId("id")
+ .setActivityComponent(new ComponentName(mClientContext, ShortcutActivity2.class))
+ .setIcon(bmp32x32)
+ .setTitle("title")
+ .setText("text")
+ .setIntent(makeIntent("action", ShortcutActivity.class, "key", "val"))
+ .setWeight(123)
+ .setExtras(pb)
+ .build();
+
+ mManager.addDynamicShortcut(sorig);
+
+ // Dynamic shortcuts won't be backed up, so we need to pin it.
+ setCaller(LAUNCHER_1, USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("id"), HANDLE_USER_0);
+
+ // Do backup & restore.
+ backupAndRestore();
+
+ ShortcutInfo si;
+ si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id", USER_0);
+
+ assertEquals(CALLING_PACKAGE_1, si.getPackageName());
+ assertEquals("id", si.getId());
+ assertEquals(ShortcutActivity2.class.getName(), si.getActivityComponent().getClassName());
+ assertEquals(null, si.getIcon());
+ assertEquals("title", si.getTitle());
+ assertEquals("text", si.getText());
+ assertEquals("action", si.getIntent().getAction());
+ assertEquals("val", si.getIntent().getStringExtra("key"));
+ assertEquals(123, si.getWeight());
+ assertEquals(1, si.getExtras().getInt("k"));
+
+ assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags());
+ assertNull(si.getBitmapPath()); // No icon.
+ assertEquals(0, si.getIconResourceId());
+ }
+
+ public void testDumpsys_crossProfile() {
+ prepareCrossProfileDataSet();
+ dumpsysOnLogcat("test1", /* force= */ true);
+ }
+
+ public void testDumpsys_withIcons() {
+ testIcons();
+ // Dump after having some icons.
+ dumpsysOnLogcat("test1", /* force= */ true);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java
new file mode 100644
index 0000000..c016e61
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.pm.backup;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageParser.Package;
+import android.content.pm.Signature;
+import android.test.AndroidTestCase;
+import android.test.MoreAsserts;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.backup.BackupUtils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+@SmallTest
+public class BackupUtilsTest extends AndroidTestCase {
+
+ private Signature[] genSignatures(String... signatures) {
+ final Signature[] sigs = new Signature[signatures.length];
+ for (int i = 0; i < signatures.length; i++){
+ sigs[i] = new Signature(signatures[i].getBytes());
+ }
+ return sigs;
+ }
+
+ private PackageInfo genPackage(String... signatures) {
+ final PackageInfo pi = new PackageInfo();
+ pi.packageName = "package";
+ pi.applicationInfo = new ApplicationInfo();
+ pi.signatures = genSignatures(signatures);
+
+ return pi;
+ }
+
+ public void testSignaturesMatch() {
+ final ArrayList<byte[]> stored1 = BackupUtils.hashSignatureArray(Arrays.asList(
+ "abc".getBytes()));
+ final ArrayList<byte[]> stored2 = BackupUtils.hashSignatureArray(Arrays.asList(
+ "abc".getBytes(), "def".getBytes()));
+
+ PackageInfo pi;
+
+ // False for null package.
+ assertFalse(BackupUtils.signaturesMatch(stored1, null));
+
+ // If it's a system app, signatures don't matter.
+ pi = genPackage("xyz");
+ pi.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+ assertTrue(BackupUtils.signaturesMatch(stored1, pi));
+
+ // Non system apps.
+ assertTrue(BackupUtils.signaturesMatch(stored1, genPackage("abc")));
+
+ // Superset is okay.
+ assertTrue(BackupUtils.signaturesMatch(stored1, genPackage("abc", "xyz")));
+ assertTrue(BackupUtils.signaturesMatch(stored1, genPackage("xyz", "abc")));
+
+ assertFalse(BackupUtils.signaturesMatch(stored1, genPackage("xyz")));
+ assertFalse(BackupUtils.signaturesMatch(stored1, genPackage("xyz", "def")));
+
+ assertTrue(BackupUtils.signaturesMatch(stored2, genPackage("def", "abc")));
+ assertTrue(BackupUtils.signaturesMatch(stored2, genPackage("x", "def", "abc", "y")));
+
+ // Subset is not okay.
+ assertFalse(BackupUtils.signaturesMatch(stored2, genPackage("abc")));
+ assertFalse(BackupUtils.signaturesMatch(stored2, genPackage("def")));
+ }
+
+ public void testHashSignature() {
+ final byte[] sig1 = "abc".getBytes();
+ final byte[] sig2 = "def".getBytes();
+
+ final byte[] hash1a = BackupUtils.hashSignature(sig1);
+ final byte[] hash1b = BackupUtils.hashSignature(new Signature(sig1));
+
+ final byte[] hash2a = BackupUtils.hashSignature(sig2);
+ final byte[] hash2b = BackupUtils.hashSignature(new Signature(sig2));
+
+ assertEquals(32, hash1a.length);
+ MoreAsserts.assertEquals(hash1a, hash1b);
+
+ assertEquals(32, hash2a.length);
+ MoreAsserts.assertEquals(hash2a, hash2b);
+
+ assertFalse(Arrays.equals(hash1a, hash2a));
+
+ final ArrayList<byte[]> listA = BackupUtils.hashSignatureArray(Arrays.asList(
+ "abc".getBytes(), "def".getBytes()));
+
+ final ArrayList<byte[]> listB = BackupUtils.hashSignatureArray(new Signature[]{
+ new Signature("abc".getBytes()), new Signature("def".getBytes())});
+
+ assertEquals(2, listA.size());
+ assertEquals(2, listB.size());
+
+ MoreAsserts.assertEquals(hash1a, listA.get(0));
+ MoreAsserts.assertEquals(hash1a, listB.get(0));
+
+ MoreAsserts.assertEquals(hash2a, listA.get(1));
+ MoreAsserts.assertEquals(hash2a, listB.get(1));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/testutis/TestUtils.java b/services/tests/servicestests/src/com/android/server/testutis/TestUtils.java
index 52e8f37..d2a4484 100644
--- a/services/tests/servicestests/src/com/android/server/testutis/TestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/testutis/TestUtils.java
@@ -24,19 +24,14 @@
}
public static void assertExpectException(Class<? extends Throwable> expectedExceptionType,
- Runnable r) {
- assertExpectException(expectedExceptionType, null, r);
- }
-
- public static void assertExpectException(Class<? extends Throwable> expectedExceptionType,
String expectedExceptionMessageRegex, Runnable r) {
try {
r.run();
- Assert.fail("Expected exception type " + expectedExceptionType.getClass().getName()
+ Assert.fail("Expected exception type " + expectedExceptionType.getName()
+ " was not thrown");
} catch (Throwable e) {
Assert.assertTrue(
- "Expected exception type was " + expectedExceptionType.getClass().getName()
+ "Expected exception type was " + expectedExceptionType.getName()
+ " but caught " + e.getClass().getName(),
expectedExceptionType.isAssignableFrom(e.getClass()));
if (expectedExceptionMessageRegex != null) {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index beec40f..0aeb96f 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -144,6 +144,7 @@
private long mLastAppIdleParoledTime;
private volatile boolean mPendingOneTimeCheckIdleStates;
+ private boolean mSystemServicesReady = false;
@GuardedBy("mLock")
private AppIdleHistory mAppIdleHistory;
@@ -232,6 +233,8 @@
if (mPendingOneTimeCheckIdleStates) {
postOneTimeCheckIdleStates();
}
+
+ mSystemServicesReady = true;
} else if (phase == PHASE_BOOT_COMPLETED) {
setAppIdleParoled(getContext().getSystemService(BatteryManager.class).isCharging());
}
@@ -810,28 +813,30 @@
// retain this for safety).
return false;
}
- try {
- // We allow all whitelisted apps, including those that don't want to be whitelisted
- // for idle mode, because app idle (aka app standby) is really not as big an issue
- // for controlling who participates vs. doze mode.
- if (mDeviceIdleController.isPowerSaveWhitelistExceptIdleApp(packageName)) {
+ if (mSystemServicesReady) {
+ try {
+ // We allow all whitelisted apps, including those that don't want to be whitelisted
+ // for idle mode, because app idle (aka app standby) is really not as big an issue
+ // for controlling who participates vs. doze mode.
+ if (mDeviceIdleController.isPowerSaveWhitelistExceptIdleApp(packageName)) {
+ return false;
+ }
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+
+ if (isActiveDeviceAdmin(packageName, userId)) {
return false;
}
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
- if (isActiveDeviceAdmin(packageName, userId)) {
- return false;
- }
+ if (isActiveNetworkScorer(packageName)) {
+ return false;
+ }
- if (isActiveNetworkScorer(packageName)) {
- return false;
- }
-
- if (mAppWidgetManager != null
- && mAppWidgetManager.isBoundWidgetPackage(packageName, userId)) {
- return false;
+ if (mAppWidgetManager != null
+ && mAppWidgetManager.isBoundWidgetPackage(packageName, userId)) {
+ return false;
+ }
}
if (!isAppIdleUnfiltered(packageName, userId, elapsedRealtime)) {
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index 129e537..058de05 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -385,6 +385,7 @@
UsbAudioDevice audioDevice = selectAudioCard(addedCard);
if (audioDevice != null) {
mAudioDevices.put(usbDevice, audioDevice);
+ Slog.i(TAG, "USB Audio Device Added: " + audioDevice);
}
// look for MIDI devices
@@ -441,6 +442,7 @@
}
UsbAudioDevice audioDevice = mAudioDevices.remove(usbDevice);
+ Slog.i(TAG, "USB Audio Device Removed: " + audioDevice);
if (audioDevice != null) {
if (audioDevice.mHasPlayback || audioDevice.mHasCapture) {
notifyDeviceState(audioDevice, false);
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 5fe944c..08cbcf7 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -780,7 +780,7 @@
|| ("0".equals(SystemProperties.get("persist.charging.notify")))) return;
int id = 0;
Resources r = mContext.getResources();
- if (mConnected || mHostConnected) {
+ if (mConnected) {
if (!mUsbDataUnlocked) {
id = com.android.internal.R.string.usb_charging_notification_title;
} else if (UsbManager.containsFunction(mCurrentFunctions,
@@ -798,6 +798,8 @@
} else {
id = com.android.internal.R.string.usb_charging_notification_title;
}
+ } else if (mHostConnected) {
+ id = com.android.internal.R.string.usb_supplying_notification_title;
}
if (id != mUsbNotificationId) {
// clear notification if title needs changing
diff --git a/services/usb/java/com/android/server/usb/UsbMidiDevice.java b/services/usb/java/com/android/server/usb/UsbMidiDevice.java
index 38ede87..46ce7a0 100644
--- a/services/usb/java/com/android/server/usb/UsbMidiDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbMidiDevice.java
@@ -127,6 +127,14 @@
public void setReceiver(MidiReceiver receiver) {
mReceiver = receiver;
}
+
+ @Override
+ public void onFlush() throws IOException {
+ MidiReceiver receiver = mReceiver;
+ if (receiver != null) {
+ receiver.flush();
+ }
+ }
}
public static UsbMidiDevice create(Context context, Bundle properties, int card, int device) {
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index e26e54b..b4c6e6a 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -250,7 +250,7 @@
* in its manifest.
* <p>
* See {@link Connection#CAPABILITY_CAN_PULL_CALL} and
- * {@link Connection#CAPABILITY_IS_EXTERNAL_CALL}.
+ * {@link Connection#PROPERTY_IS_EXTERNAL_CALL}.
*/
public static final int CAPABILITY_CAN_PULL_CALL = 0x00800000;
@@ -296,13 +296,13 @@
* Consider, for example, a scenario where a user has two phones with the same phone number.
* When a user places a call on one device, the telephony stack can represent that call on
* the other device by adding it to the {@link ConnectionService} with the
- * {@link Connection#CAPABILITY_IS_EXTERNAL_CALL} capability set.
+ * {@link Connection#PROPERTY_IS_EXTERNAL_CALL} property set.
* <p>
* An {@link InCallService} will only see calls with this property if it has the
* {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true}
* in its manifest.
* <p>
- * See {@link Connection#CAPABILITY_IS_EXTERNAL_CALL}.
+ * See {@link Connection#PROPERTY_IS_EXTERNAL_CALL}.
*/
public static final int PROPERTY_IS_EXTERNAL_CALL = 0x00000040;
@@ -686,7 +686,7 @@
sb.append(", caps: ");
sb.append(capabilitiesToString(mCallCapabilities));
sb.append(", props: ");
- sb.append(mCallProperties);
+ sb.append(propertiesToString(mCallProperties));
sb.append("]");
return sb.toString();
}
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index 00e07af..1ce4ade 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -53,6 +53,8 @@
public void onDestroyed(Conference conference) {}
public void onConnectionCapabilitiesChanged(
Conference conference, int connectionCapabilities) {}
+ public void onConnectionPropertiesChanged(
+ Conference conference, int connectionProperties) {}
public void onVideoStateChanged(Conference c, int videoState) { }
public void onVideoProviderChanged(Conference c, Connection.VideoProvider videoProvider) {}
public void onStatusHintsChanged(Conference conference, StatusHints statusHints) {}
@@ -74,6 +76,7 @@
private int mState = Connection.STATE_NEW;
private DisconnectCause mDisconnectCause;
private int mConnectionCapabilities;
+ private int mConnectionProperties;
private String mDisconnectMessage;
private long mConnectTimeMillis = CONNECT_TIME_NOT_SPECIFIED;
private StatusHints mStatusHints;
@@ -156,6 +159,16 @@
}
/**
+ * Returns the properties of the conference. See {@code PROPERTY_*} constants in class
+ * {@link Connection} for valid values.
+ *
+ * @return A bitmask of the properties of the conference call.
+ */
+ public final int getConnectionProperties() {
+ return mConnectionProperties;
+ }
+
+ /**
* Whether the given capabilities support the specified capability.
*
* @param capabilities A capability bit field.
@@ -364,7 +377,7 @@
* Sets the capabilities of a conference. See {@code CAPABILITY_*} constants of class
* {@link Connection} for valid values.
*
- * @param connectionCapabilities A bitmask of the {@code PhoneCapabilities} of the conference call.
+ * @param connectionCapabilities A bitmask of the {@code Capabilities} of the conference call.
*/
public final void setConnectionCapabilities(int connectionCapabilities) {
if (connectionCapabilities != mConnectionCapabilities) {
@@ -377,6 +390,22 @@
}
/**
+ * Sets the properties of a conference. See {@code PROPERTY_*} constants of class
+ * {@link Connection} for valid values.
+ *
+ * @param connectionProperties A bitmask of the {@code Properties} of the conference call.
+ */
+ public final void setConnectionProperties(int connectionProperties) {
+ if (connectionProperties != mConnectionProperties) {
+ mConnectionProperties = connectionProperties;
+
+ for (Listener l : mListeners) {
+ l.onConnectionPropertiesChanged(this, mConnectionProperties);
+ }
+ }
+ }
+
+ /**
* Adds the specified connection as a child of this conference.
*
* @param connection The connection to add.
@@ -665,7 +694,7 @@
if (mPreviousExtraKeys != null) {
List<String> toRemove = new ArrayList<String>();
for (String oldKey : mPreviousExtraKeys) {
- if (!extras.containsKey(oldKey)) {
+ if (extras == null || !extras.containsKey(oldKey)) {
toRemove.add(oldKey);
}
}
@@ -681,7 +710,9 @@
mPreviousExtraKeys = new ArraySet<String>();
}
mPreviousExtraKeys.clear();
- mPreviousExtraKeys.addAll(extras.keySet());
+ if (extras != null) {
+ mPreviousExtraKeys.addAll(extras.keySet());
+ }
}
/**
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 3ea1c6a..d83cdb8 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -98,7 +98,7 @@
* The state of an external connection which is in the process of being pulled from a remote
* device to the local device.
* <p>
- * A connection can only be in this state if the {@link #CAPABILITY_IS_EXTERNAL_CALL} and
+ * A connection can only be in this state if the {@link #PROPERTY_IS_EXTERNAL_CALL} property and
* {@link #CAPABILITY_CAN_PULL_CALL} capability bits are set on the connection.
*/
public static final int STATE_PULLING_CALL = 7;
@@ -194,31 +194,28 @@
public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 0x00002000;
/**
- * Whether the call is a generic conference, where we do not know the precise state of
- * participants in the conference (eg. on CDMA).
- *
+ * Un-used.
* @hide
*/
- public static final int CAPABILITY_GENERIC_CONFERENCE = 0x00004000;
+ public static final int CAPABILITY_UNUSED_2 = 0x00004000;
/**
- * Connection is using high definition audio.
+ * Un-used.
* @hide
*/
- public static final int CAPABILITY_HIGH_DEF_AUDIO = 0x00008000;
+ public static final int CAPABILITY_UNUSED_3 = 0x00008000;
/**
- * Connection is using WIFI.
+ * Un-used.
* @hide
*/
- public static final int CAPABILITY_WIFI = 0x00010000;
+ public static final int CAPABILITY_UNUSED_4 = 0x00010000;
/**
- * Indicates that the current device callback number should be shown.
- *
+ * Un-used.
* @hide
*/
- public static final int CAPABILITY_SHOW_CALLBACK_NUMBER = 0x00020000;
+ public static final int CAPABILITY_UNUSED_5 = 0x00020000;
/**
* Speed up audio setup for MT call.
@@ -281,32 +278,64 @@
public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 0x00800000;
/**
+ * When set for an external connection, indicates that this {@code Connection} can be pulled
+ * from a remote device to the current device.
+ * <p>
+ * Should only be set on a {@code Connection} where {@link #PROPERTY_IS_EXTERNAL_CALL}
+ * is set.
+ */
+ public static final int CAPABILITY_CAN_PULL_CALL = 0x01000000;
+
+ //**********************************************************************************************
+ // Next CAPABILITY value: 0x02000000
+ //**********************************************************************************************
+
+ /**
+ * Indicates that the current device callback number should be shown.
+ *
+ * @hide
+ */
+ public static final int PROPERTY_SHOW_CALLBACK_NUMBER = 1<<0;
+
+ /**
+ * Whether the call is a generic conference, where we do not know the precise state of
+ * participants in the conference (eg. on CDMA).
+ *
+ * @hide
+ */
+ public static final int PROPERTY_GENERIC_CONFERENCE = 1<<1;
+
+ /**
+ * Connection is using high definition audio.
+ * @hide
+ */
+ public static final int PROPERTY_HIGH_DEF_AUDIO = 1<<2;
+
+ /**
+ * Connection is using WIFI.
+ * @hide
+ */
+ public static final int PROPERTY_WIFI = 1<<3;
+
+ /**
* When set, indicates that the {@code Connection} does not actually exist locally for the
* {@link ConnectionService}.
* <p>
* Consider, for example, a scenario where a user has two devices with the same phone number.
* When a user places a call on one devices, the telephony stack can represent that call on the
* other device by adding is to the {@link ConnectionService} with the
- * {@code CAPABILITY_IS_EXTERNAL_CALL} capability set.
+ * {@link #PROPERTY_IS_EXTERNAL_CALL} capability set.
* <p>
* An {@link ConnectionService} should not assume that all {@link InCallService}s will handle
* external connections. Only those {@link InCallService}s which have the
* {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true} in its
* manifest will see external connections.
*/
- public static final int CAPABILITY_IS_EXTERNAL_CALL = 0x01000000;
+ public static final int PROPERTY_IS_EXTERNAL_CALL = 1<<4;
- /**
- * When set for an external connection, indicates that this {@code Connection} can be pulled
- * from a remote device to the current device.
- * <p>
- * Should only be set on a {@code Connection} where {@link #CAPABILITY_IS_EXTERNAL_CALL}
- * is set.
- */
- public static final int CAPABILITY_CAN_PULL_CALL = 0x02000000;
//**********************************************************************************************
- // Next CAPABILITY value: 0x04000000
+ // Next PROPERTY value: 1<<5
//**********************************************************************************************
/**
@@ -454,18 +483,6 @@
if (can(capabilities, CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO)) {
builder.append(" CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO");
}
- if (can(capabilities, CAPABILITY_HIGH_DEF_AUDIO)) {
- builder.append(" CAPABILITY_HIGH_DEF_AUDIO");
- }
- if (can(capabilities, CAPABILITY_WIFI)) {
- builder.append(" CAPABILITY_WIFI");
- }
- if (can(capabilities, CAPABILITY_GENERIC_CONFERENCE)) {
- builder.append(" CAPABILITY_GENERIC_CONFERENCE");
- }
- if (can(capabilities, CAPABILITY_SHOW_CALLBACK_NUMBER)) {
- builder.append(" CAPABILITY_SHOW_CALLBACK_NUMBER");
- }
if (can(capabilities, CAPABILITY_SPEED_UP_MT_AUDIO)) {
builder.append(" CAPABILITY_SPEED_UP_MT_AUDIO");
}
@@ -481,9 +498,6 @@
if (can(capabilities, CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION)) {
builder.append(" CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION");
}
- if (can(capabilities, CAPABILITY_IS_EXTERNAL_CALL)) {
- builder.append(" CAPABILITY_IS_EXTERNAL_CALL");
- }
if (can(capabilities, CAPABILITY_CAN_PULL_CALL)) {
builder.append(" CAPABILITY_CAN_PULL_CALL");
}
@@ -492,6 +506,34 @@
return builder.toString();
}
+ public static String propertiesToString(int properties) {
+ StringBuilder builder = new StringBuilder();
+ builder.append("[Properties:");
+
+ if (can(properties, PROPERTY_SHOW_CALLBACK_NUMBER)) {
+ builder.append(" PROPERTY_SHOW_CALLBACK_NUMBER");
+ }
+
+ if (can(properties, PROPERTY_HIGH_DEF_AUDIO)) {
+ builder.append(" PROPERTY_HIGH_DEF_AUDIO");
+ }
+
+ if (can(properties, PROPERTY_WIFI)) {
+ builder.append(" PROPERTY_WIFI");
+ }
+
+ if (can(properties, PROPERTY_GENERIC_CONFERENCE)) {
+ builder.append(" PROPERTY_GENERIC_CONFERENCE");
+ }
+
+ if (can(properties, PROPERTY_IS_EXTERNAL_CALL)) {
+ builder.append(" PROPERTY_IS_EXTERNAL_CALL");
+ }
+
+ builder.append("]");
+ return builder.toString();
+ }
+
/** @hide */
public abstract static class Listener {
public void onStateChanged(Connection c, int state) {}
@@ -505,6 +547,7 @@
public void onRingbackRequested(Connection c, boolean ringback) {}
public void onDestroyed(Connection c) {}
public void onConnectionCapabilitiesChanged(Connection c, int capabilities) {}
+ public void onConnectionPropertiesChanged(Connection c, int properties) {}
public void onVideoProviderChanged(
Connection c, VideoProvider videoProvider) {}
public void onAudioModeIsVoipChanged(Connection c, boolean isVoip) {}
@@ -1175,6 +1218,7 @@
private int mCallerDisplayNamePresentation;
private boolean mRingbackRequested = false;
private int mConnectionCapabilities;
+ private int mConnectionProperties;
private VideoProvider mVideoProvider;
private boolean mAudioModeIsVoip;
private long mConnectTimeMillis = Conference.CONNECT_TIME_NOT_SPECIFIED;
@@ -1439,6 +1483,13 @@
}
/**
+ * Returns the connection's properties, as a bit mask of the {@code PROPERTY_*} constants.
+ */
+ public final int getConnectionProperties() {
+ return mConnectionProperties;
+ }
+
+ /**
* Sets the value of the {@link #getAddress()} property.
*
* @param address The new address.
@@ -1635,6 +1686,21 @@
}
/**
+ * Sets the connection's properties as a bit mask of the {@code PROPERTY_*} constants.
+ *
+ * @param connectionProperties The new connection properties.
+ */
+ public final void setConnectionProperties(int connectionProperties) {
+ checkImmutable();
+ if (mConnectionProperties != connectionProperties) {
+ mConnectionProperties = connectionProperties;
+ for (Listener l : mListeners) {
+ l.onConnectionPropertiesChanged(this, mConnectionProperties);
+ }
+ }
+ }
+
+ /**
* Tears down the Connection object.
*/
public final void destroy() {
@@ -1821,7 +1887,7 @@
if (mPreviousExtraKeys != null) {
List<String> toRemove = new ArrayList<String>();
for (String oldKey : mPreviousExtraKeys) {
- if (!extras.containsKey(oldKey)) {
+ if (extras == null || !extras.containsKey(oldKey)) {
toRemove.add(oldKey);
}
}
@@ -1836,7 +1902,9 @@
mPreviousExtraKeys = new ArraySet<String>();
}
mPreviousExtraKeys.clear();
- mPreviousExtraKeys.addAll(extras.keySet());
+ if (extras != null) {
+ mPreviousExtraKeys.addAll(extras.keySet());
+ }
}
/**
@@ -2042,10 +2110,10 @@
* The {@link InCallService} issues a request to pull an external call to the local device via
* {@link Call#pullExternalCall()}.
* <p>
- * For a Connection to be pulled, both the {@link Connection#CAPABILITY_CAN_PULL_CALL} and
- * {@link Connection#CAPABILITY_IS_EXTERNAL_CALL} capability bits must be set.
+ * For a Connection to be pulled, both the {@link Connection#CAPABILITY_CAN_PULL_CALL}
+ * capability and {@link Connection#PROPERTY_IS_EXTERNAL_CALL} property bits must be set.
* <p>
- * For more information on external calls, see {@link Connection#CAPABILITY_IS_EXTERNAL_CALL}.
+ * For more information on external calls, see {@link Connection#PROPERTY_IS_EXTERNAL_CALL}.
*/
public void onPullExternalCall() {}
@@ -2251,7 +2319,7 @@
*/
public void sendConnectionEvent(String event, Bundle extras) {
for (Listener l : mListeners) {
- l.onConnectionEvent(this, event, null);
+ l.onConnectionEvent(this, event, extras);
}
}
}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index e092095..4cab7f0 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -495,6 +495,16 @@
}
@Override
+ public void onConnectionPropertiesChanged(
+ Conference conference,
+ int connectionProperties) {
+ String id = mIdByConference.get(conference);
+ Log.d(this, "call capabilities: conference: %s",
+ Connection.propertiesToString(connectionProperties));
+ mAdapter.setConnectionProperties(id, connectionProperties);
+ }
+
+ @Override
public void onVideoStateChanged(Conference c, int videoState) {
String id = mIdByConference.get(c);
Log.d(this, "onVideoStateChanged set video state %d", videoState);
@@ -623,6 +633,14 @@
}
@Override
+ public void onConnectionPropertiesChanged(Connection c, int properties) {
+ String id = mIdByConnection.get(c);
+ Log.d(this, "properties: parcelableconnection: %s",
+ Connection.propertiesToString(properties));
+ mAdapter.setConnectionProperties(id, properties);
+ }
+
+ @Override
public void onVideoProviderChanged(Connection c, Connection.VideoProvider videoProvider) {
String id = mIdByConnection.get(c);
Log.d(this, "onVideoProviderChanged: Connection: %s, VideoProvider: %s", c,
@@ -740,10 +758,11 @@
Uri address = connection.getAddress();
String number = address == null ? "null" : address.getSchemeSpecificPart();
- Log.v(this, "createConnection, number: %s, state: %s, capabilities: %s",
+ Log.v(this, "createConnection, number: %s, state: %s, capabilities: %s, properties: %s",
Connection.toLogSafePhoneNumber(number),
Connection.stateToString(connection.getState()),
- Connection.capabilitiesToString(connection.getConnectionCapabilities()));
+ Connection.capabilitiesToString(connection.getConnectionCapabilities()),
+ Connection.propertiesToString(connection.getConnectionProperties()));
Log.d(this, "createConnection, calling handleCreateConnectionSuccessful %s", callId);
mAdapter.handleCreateConnectionComplete(
@@ -753,6 +772,7 @@
request.getAccountHandle(),
connection.getState(),
connection.getConnectionCapabilities(),
+ connection.getConnectionProperties(),
connection.getAddress(),
connection.getAddressPresentation(),
connection.getCallerDisplayName(),
@@ -1110,6 +1130,7 @@
conference.getPhoneAccountHandle(),
conference.getState(),
conference.getConnectionCapabilities(),
+ conference.getConnectionProperties(),
connectionIds,
conference.getVideoProvider() == null ?
null : conference.getVideoProvider().getInterface(),
@@ -1150,6 +1171,7 @@
phoneAccountHandle,
connection.getState(),
connection.getConnectionCapabilities(),
+ connection.getConnectionProperties(),
connection.getAddress(),
connection.getAddressPresentation(),
connection.getCallerDisplayName(),
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
index 81e4c22..c8cd3c0 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -196,6 +196,15 @@
}
}
+ void setConnectionProperties(String callId, int properties) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.setConnectionProperties(callId, properties);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
/**
* Indicates whether or not the specified call is currently conferenced into the specified
* conference call.
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
index 3e46557..bf28feb 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -64,6 +64,7 @@
private static final int MSG_PUT_EXTRAS = 24;
private static final int MSG_REMOVE_EXTRAS = 25;
private static final int MSG_ON_CONNECTION_EVENT = 26;
+ private static final int MSG_SET_CONNECTION_PROPERTIES = 27;
private final IConnectionServiceAdapter mDelegate;
@@ -118,6 +119,9 @@
case MSG_SET_CONNECTION_CAPABILITIES:
mDelegate.setConnectionCapabilities((String) msg.obj, msg.arg1);
break;
+ case MSG_SET_CONNECTION_PROPERTIES:
+ mDelegate.setConnectionProperties((String) msg.obj, msg.arg1);
+ break;
case MSG_SET_IS_CONFERENCED: {
SomeArgs args = (SomeArgs) msg.obj;
try {
@@ -322,6 +326,13 @@
}
@Override
+ public void setConnectionProperties(String connectionId, int connectionProperties) {
+ mHandler.obtainMessage(
+ MSG_SET_CONNECTION_PROPERTIES, connectionProperties, 0, connectionId)
+ .sendToTarget();
+ }
+
+ @Override
public void setConferenceMergeFailed(String callId) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = callId;
diff --git a/telecomm/java/android/telecom/ParcelableConference.java b/telecomm/java/android/telecom/ParcelableConference.java
index 870f5ee..f5689d8 100644
--- a/telecomm/java/android/telecom/ParcelableConference.java
+++ b/telecomm/java/android/telecom/ParcelableConference.java
@@ -34,6 +34,7 @@
private PhoneAccountHandle mPhoneAccount;
private int mState;
private int mConnectionCapabilities;
+ private int mConnectionProperties;
private List<String> mConnectionIds;
private long mConnectTimeMillis = Conference.CONNECT_TIME_NOT_SPECIFIED;
private final IVideoProvider mVideoProvider;
@@ -45,6 +46,7 @@
PhoneAccountHandle phoneAccount,
int state,
int connectionCapabilities,
+ int connectionProperties,
List<String> connectionIds,
IVideoProvider videoProvider,
int videoState,
@@ -54,6 +56,7 @@
mPhoneAccount = phoneAccount;
mState = state;
mConnectionCapabilities = connectionCapabilities;
+ mConnectionProperties = connectionProperties;
mConnectionIds = connectionIds;
mConnectTimeMillis = Conference.CONNECT_TIME_NOT_SPECIFIED;
mVideoProvider = videoProvider;
@@ -72,6 +75,8 @@
.append(Connection.stateToString(mState))
.append(", capabilities: ")
.append(Connection.capabilitiesToString(mConnectionCapabilities))
+ .append(", properties: ")
+ .append(Connection.propertiesToString(mConnectionProperties))
.append(", connectTime: ")
.append(mConnectTimeMillis)
.append(", children: ")
@@ -95,6 +100,10 @@
return mConnectionCapabilities;
}
+ public int getConnectionProperties() {
+ return mConnectionProperties;
+ }
+
public List<String> getConnectionIds() {
return mConnectionIds;
}
@@ -134,9 +143,11 @@
int videoState = source.readInt();
StatusHints statusHints = source.readParcelable(classLoader);
Bundle extras = source.readBundle(classLoader);
+ int properties = source.readInt();
- return new ParcelableConference(phoneAccount, state, capabilities, connectionIds,
- videoCallProvider, videoState, connectTimeMillis, statusHints, extras);
+ return new ParcelableConference(phoneAccount, state, capabilities, properties,
+ connectionIds, videoCallProvider, videoState, connectTimeMillis, statusHints,
+ extras);
}
@Override
@@ -164,5 +175,6 @@
destination.writeInt(mVideoState);
destination.writeParcelable(mStatusHints, 0);
destination.writeBundle(mExtras);
+ destination.writeInt(mConnectionProperties);
}
}
diff --git a/telecomm/java/android/telecom/ParcelableConnection.java b/telecomm/java/android/telecom/ParcelableConnection.java
index ce51c96..540f388 100644
--- a/telecomm/java/android/telecom/ParcelableConnection.java
+++ b/telecomm/java/android/telecom/ParcelableConnection.java
@@ -36,6 +36,7 @@
private final PhoneAccountHandle mPhoneAccount;
private final int mState;
private final int mConnectionCapabilities;
+ private final int mConnectionProperties;
private final Uri mAddress;
private final int mAddressPresentation;
private final String mCallerDisplayName;
@@ -55,6 +56,7 @@
PhoneAccountHandle phoneAccount,
int state,
int capabilities,
+ int properties,
Uri address,
int addressPresentation,
String callerDisplayName,
@@ -71,6 +73,7 @@
mPhoneAccount = phoneAccount;
mState = state;
mConnectionCapabilities = capabilities;
+ mConnectionProperties = properties;
mAddress = address;
mAddressPresentation = addressPresentation;
mCallerDisplayName = callerDisplayName;
@@ -94,11 +97,26 @@
return mState;
}
- // Bit mask of actions a call supports, values are defined in {@link CallCapabilities}.
+ /**
+ * Returns the current connection capabilities bit-mask. Connection capabilities are defined as
+ * {@code CAPABILITY_*} constants in {@link Connection}.
+ *
+ * @return Bit-mask containing capabilities of the connection.
+ */
public int getConnectionCapabilities() {
return mConnectionCapabilities;
}
+ /**
+ * Returns the current connection properties bit-mask. Connection properties are defined as
+ * {@code PROPERTY_*} constants in {@link Connection}.
+ *
+ * @return Bit-mask containing properties of the connection.
+ */
+ public int getConnectionProperties() {
+ return mConnectionProperties;
+ }
+
public Uri getHandle() {
return mAddress;
}
@@ -160,6 +178,8 @@
.append(mState)
.append(", capabilities:")
.append(Connection.capabilitiesToString(mConnectionCapabilities))
+ .append(", properties:")
+ .append(Connection.propertiesToString(mConnectionProperties))
.append(", extras:")
.append(mExtras)
.toString();
@@ -189,11 +209,13 @@
List<String> conferenceableConnectionIds = new ArrayList<>();
source.readStringList(conferenceableConnectionIds);
Bundle extras = Bundle.setDefusable(source.readBundle(classLoader), true);
+ int properties = source.readInt();
return new ParcelableConnection(
phoneAccount,
state,
capabilities,
+ properties,
address,
addressPresentation,
callerDisplayName,
@@ -241,5 +263,6 @@
destination.writeParcelable(mDisconnectCause, 0);
destination.writeStringList(mConferenceableConnectionIds);
destination.writeBundle(mExtras);
+ destination.writeInt(mConnectionProperties);
}
}
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index b56ce73..dbc2b0c 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -170,6 +170,15 @@
public static final int CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE = 0x100;
/**
+ * Flag indicating that for this {@link PhoneAccount}, emergency video calling is allowed.
+ * <p>
+ * When set, Telecom will allow emergency video calls to be placed. When not set, Telecom will
+ * convert all outgoing video calls to emergency numbers to audio-only.
+ * @hide
+ */
+ public static final int CAPABILITY_EMERGENCY_VIDEO_CALLING = 0x200;
+
+ /**
* URI scheme for telephone number URIs.
*/
public static final String SCHEME_TEL = "tel";
@@ -731,6 +740,9 @@
if (hasCapabilities(CAPABILITY_PLACE_EMERGENCY_CALLS)) {
sb.append("PlaceEmerg ");
}
+ if (hasCapabilities(CAPABILITY_EMERGENCY_VIDEO_CALLING)) {
+ sb.append("EmergVideo ");
+ }
if (hasCapabilities(CAPABILITY_SIM_SUBSCRIPTION)) {
sb.append("SimSub ");
}
diff --git a/telecomm/java/android/telecom/RemoteConference.java b/telecomm/java/android/telecom/RemoteConference.java
index b03cb51..943da6d 100644
--- a/telecomm/java/android/telecom/RemoteConference.java
+++ b/telecomm/java/android/telecom/RemoteConference.java
@@ -92,6 +92,18 @@
int connectionCapabilities) {}
/**
+ * Indicates that the call properties of this {@code RemoteConference} have changed.
+ * See {@link #getConnectionProperties()}.
+ *
+ * @param conference The {@code RemoteConference} invoking this method.
+ * @param connectionProperties The new properties of the {@code RemoteConference}.
+ */
+ public void onConnectionPropertiesChanged(
+ RemoteConference conference,
+ int connectionProperties) {}
+
+
+ /**
* Invoked when the set of {@link RemoteConnection}s which can be added to this conference
* call have changed.
*
@@ -133,6 +145,7 @@
private int mState = Connection.STATE_NEW;
private DisconnectCause mDisconnectCause;
private int mConnectionCapabilities;
+ private int mConnectionProperties;
private Bundle mExtras;
/** @hide */
@@ -244,6 +257,24 @@
}
/** @hide */
+ void setConnectionProperties(final int connectionProperties) {
+ if (mConnectionProperties != connectionProperties) {
+ mConnectionProperties = connectionProperties;
+ for (CallbackRecord<Callback> record : mCallbackRecords) {
+ final RemoteConference conference = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onConnectionPropertiesChanged(
+ conference, mConnectionProperties);
+ }
+ });
+ }
+ }
+ }
+
+ /** @hide */
void setConferenceableConnections(List<RemoteConnection> conferenceableConnections) {
mConferenceableConnections.clear();
mConferenceableConnections.addAll(conferenceableConnections);
@@ -342,6 +373,16 @@
}
/**
+ * Returns the properties of the conference. See {@code PROPERTY_*} constants in class
+ * {@link Connection} for valid values.
+ *
+ * @return A bitmask of the properties of the conference call.
+ */
+ public final int getConnectionProperties() {
+ return mConnectionProperties;
+ }
+
+ /**
* Obtain the extras associated with this {@code RemoteConnection}.
*
* @return The extras for this connection.
diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java
index 7df6678..dc8eaf6 100644
--- a/telecomm/java/android/telecom/RemoteConnection.java
+++ b/telecomm/java/android/telecom/RemoteConnection.java
@@ -90,6 +90,17 @@
int connectionCapabilities) {}
/**
+ * Indicates that the call properties of this {@code RemoteConnection} have changed.
+ * See {@link #getConnectionProperties()}.
+ *
+ * @param connection The {@code RemoteConnection} invoking this method.
+ * @param connectionProperties The new properties of the {@code RemoteConnection}.
+ */
+ public void onConnectionPropertiesChanged(
+ RemoteConnection connection,
+ int connectionProperties) {}
+
+ /**
* Invoked when the post-dial sequence in the outgoing {@code Connection} has reached a
* pause character. This causes the post-dial signals to stop pending user confirmation. An
* implementation should present this choice to the user and invoke
@@ -588,6 +599,7 @@
private boolean mRingbackRequested;
private boolean mConnected;
private int mConnectionCapabilities;
+ private int mConnectionProperties;
private int mVideoState;
private VideoProvider mVideoProvider;
private boolean mIsVoipAudioMode;
@@ -624,6 +636,7 @@
mDisconnectCause = connection.getDisconnectCause();
mRingbackRequested = connection.isRingbackRequested();
mConnectionCapabilities = connection.getConnectionCapabilities();
+ mConnectionProperties = connection.getConnectionProperties();
mVideoState = connection.getVideoState();
mVideoProvider = new RemoteConnection.VideoProvider(connection.getVideoProvider());
mIsVoipAudioMode = connection.getIsVoipAudioMode();
@@ -719,6 +732,16 @@
}
/**
+ * Obtains the properties of this {@code RemoteConnection}.
+ *
+ * @return A bitmask of the properties of the {@code RemoteConnection}, as defined in the
+ * {@code PROPERTY_*} constants in class {@link Connection}.
+ */
+ public int getConnectionProperties() {
+ return mConnectionProperties;
+ }
+
+ /**
* Determines if the audio mode of this {@code RemoteConnection} is VOIP.
*
* @return {@code true} if the {@code RemoteConnection}'s current audio mode is VOIP.
@@ -1114,6 +1137,23 @@
/**
* @hide
*/
+ void setConnectionProperties(final int connectionProperties) {
+ mConnectionProperties = connectionProperties;
+ for (CallbackRecord record : mCallbackRecords) {
+ final RemoteConnection connection = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onConnectionPropertiesChanged(connection, connectionProperties);
+ }
+ });
+ }
+ }
+
+ /**
+ * @hide
+ */
void setDestroyed() {
if (!mCallbackRecords.isEmpty()) {
// Make sure that the callbacks are notified that the call is destroyed first.
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index d88d007..21a7706 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -61,6 +61,7 @@
mPendingConnections.remove(connection);
// Unconditionally initialize the connection ...
connection.setConnectionCapabilities(parcel.getConnectionCapabilities());
+ connection.setConnectionProperties(parcel.getConnectionProperties());
if (parcel.getHandle() != null
|| parcel.getState() != Connection.STATE_DISCONNECTED) {
connection.setAddress(parcel.getHandle(), parcel.getHandlePresentation());
@@ -156,6 +157,17 @@
}
@Override
+ public void setConnectionProperties(String callId, int connectionProperties) {
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "setConnectionProperties")
+ .setConnectionProperties(connectionProperties);
+ } else {
+ findConferenceForAction(callId, "setConnectionProperties")
+ .setConnectionProperties(connectionProperties);
+ }
+ }
+
+ @Override
public void setIsConferenced(String callId, String conferenceCallId) {
// Note: callId should not be null; conferenceCallId may be null
RemoteConnection connection =
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 4fa8fe9..6eafb90 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1433,25 +1433,6 @@
}
/**
- * Launches the {@link android.app.Activity} to manage blocked numbers.
- * <p> This method displays the UI to manage blocked numbers only if
- * {@link android.provider.BlockedNumberContract#canCurrentUserBlockNumbers(Context)} returns
- * {@code true} for the current user.
- * @deprecated Use {@link #createManageBlockedNumbersIntent()} instead.
- */
- // TODO: Delete this.
- public void launchManageBlockedNumbersActivity() {
- ITelecomService service = getTelecomService();
- if (service != null) {
- try {
- service.launchManageBlockedNumbersActivity(mContext.getPackageName());
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelecomService#manageBlockedNumbers", e);
- }
- }
- }
-
- /**
* Creates the {@link Intent} which can be used with {@link Context#startActivity(Intent)} to
* launch the activity to manage blocked numbers.
* <p> The activity will display the UI to manage blocked numbers only if
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
index 6804805..9bc8ffe 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
@@ -55,6 +55,8 @@
void setConnectionCapabilities(String callId, int connectionCapabilities);
+ void setConnectionProperties(String callId, int connectionProperties);
+
void setIsConferenced(String callId, String conferenceCallId);
void setConferenceMergeFailed(String callId);
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 3c250f1..871565d 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -246,12 +246,6 @@
boolean setDefaultDialer(in String packageName);
/**
- * @see TelecomServiceImpl#launchManageBlockedNumbersActivity
- **/
- // TODO: Delete this.
- void launchManageBlockedNumbersActivity(in String callingPackageName);
-
- /**
* @see TelecomServiceImpl#createManageBlockedNumbersIntent
**/
Intent createManageBlockedNumbersIntent();
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index c69a360..461611d 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -252,6 +252,16 @@
public static final String KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL = "carrier_wfc_ims_available_bool";
/**
+ * Flag specifying whether WFC over IMS supports the "wifi only" option. If false, the wifi
+ * calling settings will not include an option for "wifi only". If true, the wifi calling
+ * settings will include an option for "wifi only"
+ * <p>
+ * By default, it is assumed that WFC supports "wifi only".
+ */
+ public static final String KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL =
+ "carrier_wfc_supports_wifi_only_bool";
+
+ /**
* Default WFC_IMS_mode 0: WIFI_ONLY
* 1: CELLULAR_PREFERRED
* 2: WIFI_PREFERRED
@@ -628,6 +638,7 @@
sDefaults.putBoolean(KEY_CARRIER_VOLTE_AVAILABLE_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_VT_AVAILABLE_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL, false);
+ sDefaults.putBoolean(KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL, true);
sDefaults.putBoolean(KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL, false);
sDefaults.putInt(KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT, 2);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index b482811..865af78 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -3550,7 +3550,7 @@
* @return the response of ISIM Authetification, or null if not available
* @hide
* @deprecated
- * @see getIccSimChallengeResponse with appType=PhoneConstants.APPTYPE_ISIM
+ * @see getIccAuthentication with appType=PhoneConstants.APPTYPE_ISIM
*/
public String getIsimChallengeResponse(String nonce){
try {
@@ -3566,21 +3566,60 @@
}
}
+ // ICC SIM Application Types
+ public static final int APPTYPE_SIM = PhoneConstants.APPTYPE_SIM;
+ public static final int APPTYPE_USIM = PhoneConstants.APPTYPE_USIM;
+ public static final int APPTYPE_RUIM = PhoneConstants.APPTYPE_RUIM;
+ public static final int APPTYPE_CSIM = PhoneConstants.APPTYPE_CSIM;
+ public static final int APPTYPE_ISIM = PhoneConstants.APPTYPE_ISIM;
+ // authContext (parameter P2) when doing SIM challenge,
+ // per 3GPP TS 31.102 (Section 7.1.2)
+ public static final int AUTHTYPE_EAP_SIM = PhoneConstants.AUTH_CONTEXT_EAP_SIM;
+ public static final int AUTHTYPE_EAP_AKA = PhoneConstants.AUTH_CONTEXT_EAP_AKA;
+
/**
- * Returns the response of SIM Authentication through RIL.
- * Returns null if the Authentication hasn't been successful
- * @param subId subscription ID to be queried
- * @param appType ICC application type (@see com.android.internal.telephony.PhoneConstants#APPTYPE_xxx)
- * @param data authentication challenge data
- * @return the response of SIM Authentication, or null if not available
- * @hide
+ * Returns the response of authentication for the default subscription.
+ * Returns null if the authentication hasn't been successful
+ *
+ * <p>Requires that the calling app has carrier privileges or READ_PRIVILEGED_PHONE_STATE
+ * permission.
+ *
+ * @param appType the icc application type, like {@link #APPTYPE_USIM}
+ * @param authType the authentication type, {@link #AUTHTYPE_EAP_AKA} or
+ * {@link #AUTHTYPE_EAP_SIM}
+ * @param data authentication challenge data, base64 encoded.
+ * See 3GPP TS 31.102 7.1.2 for more details.
+ * @return the response of authentication, or null if not available
+ *
+ * @see #hasCarrierPrivileges
*/
- public String getIccSimChallengeResponse(int subId, int appType, String data) {
+ public String getIccAuthentication(int appType, int authType, String data) {
+ return getIccAuthentication(getDefaultSubscription(), appType, authType, data);
+ }
+
+ /**
+ * Returns the response of USIM Authentication for specified subId.
+ * Returns null if the authentication hasn't been successful
+ *
+ * <p>Requires that the calling app has carrier privileges.
+ *
+ * @param subId subscription ID used for authentication
+ * @param appType the icc application type, like {@link #APPTYPE_USIM}
+ * @param authType the authentication type, {@link #AUTHTYPE_EAP_AKA} or
+ * {@link #AUTHTYPE_EAP_SIM}
+ * @param data authentication challenge data, base64 encoded.
+ * See 3GPP TS 31.102 7.1.2 for more details.
+ * @return the response of authentication, or null if not available
+ *
+ * @see #hasCarrierPrivileges
+ */
+
+ public String getIccAuthentication(int subId, int appType, int authType, String data) {
try {
IPhoneSubInfo info = getSubscriberInfo();
if (info == null)
return null;
- return info.getIccSimChallengeResponse(subId, appType, data);
+ return info.getIccSimChallengeResponse(subId, appType, authType, data);
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -3599,9 +3638,10 @@
* @param appType ICC application type (@see com.android.internal.telephony.PhoneConstants#APPTYPE_xxx)
* @param data authentication challenge data
* @return the response of SIM Authentication, or null if not available
+ * @hide
*/
public String getIccSimChallengeResponse(int appType, String data) {
- return getIccSimChallengeResponse(getDefaultSubscription(), appType, data);
+ return getIccAuthentication(getDefaultSubscription(), appType, AUTHTYPE_EAP_SIM, data);
}
/**
diff --git a/telephony/java/com/android/ims/ImsCallProfile.java b/telephony/java/com/android/ims/ImsCallProfile.java
index 96c6243..303746c 100644
--- a/telephony/java/com/android/ims/ImsCallProfile.java
+++ b/telephony/java/com/android/ims/ImsCallProfile.java
@@ -201,7 +201,7 @@
* "14" vs (int) 14).
* Note: This is used by {@link com.android.internal.telephony.imsphone.ImsPhoneConnection#
* updateWifiStateFromExtras(Bundle)} to determine whether to set the
- * {@link android.telecom.Connection#CAPABILITY_WIFI} capability on a connection.
+ * {@link android.telecom.Connection#PROPERTY_WIFI} property on a connection.
*/
public static final String EXTRA_CALL_RAT_TYPE = "CallRadioTech";
diff --git a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
index dc2b297..02baa34 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
@@ -196,8 +196,9 @@
*
* @param subId subscription ID to be queried
* @param appType ICC application type (@see com.android.internal.telephony.PhoneConstants#APPTYPE_xxx)
+ * @param authType Authentication type, see PhoneConstants#AUTHTYPE_xxx
* @param data authentication challenge data
* @return challenge response
*/
- String getIccSimChallengeResponse(int subId, int appType, String data);
+ String getIccSimChallengeResponse(int subId, int appType, int authType, String data);
}
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index ecd89ed..1680fe3 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -202,4 +202,10 @@
public static final int AUDIO_OUTPUT_ENABLE_SPEAKER = 0;
public static final int AUDIO_OUTPUT_DISABLE_SPEAKER = 1;
public static final int AUDIO_OUTPUT_DEFAULT = AUDIO_OUTPUT_ENABLE_SPEAKER;
+
+ // authContext (parameter P2) when doing SIM challenge,
+ // per 3GPP TS 31.102 (Section 7.1.2)
+ public static final int AUTH_CONTEXT_EAP_SIM = 128;
+ public static final int AUTH_CONTEXT_EAP_AKA = 129;
+ public static final int AUTH_CONTEXT_UNDEFINED = -1;
}
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index b739ead..c7cbf97 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -145,6 +145,7 @@
throw new UnsupportedOperationException();
}
+ /** @removed */
@Override
public SharedPreferences getSharedPreferences(File file, int mode) {
throw new UnsupportedOperationException();
@@ -180,6 +181,7 @@
throw new UnsupportedOperationException();
}
+ /** @removed */
@Override
public File getSharedPreferencesPath(String name) {
throw new UnsupportedOperationException();
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index ed7351f8..033312b 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -62,7 +62,7 @@
private static final String KEY_REQUIRED_ACCOUNTS = "required_accounts";
private static final String WEARABLE_ACTION_GOOGLE =
"com.google.android.wearable.action.GOOGLE";
- private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 7500; //7.5s to allow app to idle
+ private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 60000; //60s to allow app to idle
private static final int POST_LAUNCH_IDLE_TIMEOUT = 750; //750ms idle for non initial launches
private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 2000; //2s between launching apps
diff --git a/tests/LocationTracker/src/com/android/locationtracker/SettingsActivity.java b/tests/LocationTracker/src/com/android/locationtracker/SettingsActivity.java
index cb77118..a169b18 100644
--- a/tests/LocationTracker/src/com/android/locationtracker/SettingsActivity.java
+++ b/tests/LocationTracker/src/com/android/locationtracker/SettingsActivity.java
@@ -1,35 +1,35 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.locationtracker;
-
-import android.os.Bundle;
-import android.preference.PreferenceActivity;
-
-/**
- * Settings preference screen for location tracker
- */
-public class SettingsActivity extends PreferenceActivity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- // Load the preferences from an XML resource
- addPreferencesFromResource(R.xml.preferences);
- }
-
-}
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.locationtracker;
+
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+
+/**
+ * Settings preference screen for location tracker
+ */
+public class SettingsActivity extends PreferenceActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Load the preferences from an XML resource
+ addPreferencesFromResource(R.xml.preferences);
+ }
+
+}
diff --git a/tests/LocationTracker/src/com/android/locationtracker/data/TrackerListHelper.java b/tests/LocationTracker/src/com/android/locationtracker/data/TrackerListHelper.java
index 55d4d1e..adc39b3 100644
--- a/tests/LocationTracker/src/com/android/locationtracker/data/TrackerListHelper.java
+++ b/tests/LocationTracker/src/com/android/locationtracker/data/TrackerListHelper.java
@@ -1,75 +1,75 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES 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.locationtracker.data;
-
-import android.app.ListActivity;
-import android.content.Context;
-import android.database.Cursor;
-import android.view.View;
-import android.widget.ResourceCursorAdapter;
-import android.widget.TextView;
-
-import com.android.locationtracker.R;
-
-/**
- * Used to bind Tracker data to a list view UI
- */
-public class TrackerListHelper extends TrackerDataHelper {
-
- private ListActivity mActivity;
-
- // sort entries by most recent first
- private static final String SORT_ORDER = TrackerEntry.ID_COL + " DESC";
-
- public TrackerListHelper(ListActivity activity) {
- super(activity, TrackerDataHelper.CSV_FORMATTER);
- mActivity = activity;
- }
-
- /**
- * Helper method for binding the list activities UI to the tracker data
- * Tracker data will be sorted in most-recent first order
- * Will enable automatic UI changes as tracker data changes
- *
- * @param layout - layout to populate data
- */
- public void bindListUI(int layout) {
- Cursor cursor = mActivity.managedQuery(TrackerProvider.CONTENT_URI,
- TrackerEntry.ATTRIBUTES, null, null, SORT_ORDER);
- // Used to map tracker entries from the database to views
- TrackerAdapter adapter = new TrackerAdapter(mActivity, layout, cursor);
- mActivity.setListAdapter(adapter);
- cursor.setNotificationUri(mActivity.getContentResolver(),
- TrackerProvider.CONTENT_URI);
-
- }
-
- private class TrackerAdapter extends ResourceCursorAdapter {
-
- public TrackerAdapter(Context context, int layout, Cursor c) {
- super(context, layout, c);
- }
-
- @Override
- public void bindView(View view, Context context, Cursor cursor) {
- final TextView v = (TextView) view
- .findViewById(R.id.entrylist_item);
- String rowText = mFormatter.getOutput(TrackerEntry
- .createEntry(cursor));
- v.setText(rowText);
- }
- }
-}
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES 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.locationtracker.data;
+
+import android.app.ListActivity;
+import android.content.Context;
+import android.database.Cursor;
+import android.view.View;
+import android.widget.ResourceCursorAdapter;
+import android.widget.TextView;
+
+import com.android.locationtracker.R;
+
+/**
+ * Used to bind Tracker data to a list view UI
+ */
+public class TrackerListHelper extends TrackerDataHelper {
+
+ private ListActivity mActivity;
+
+ // sort entries by most recent first
+ private static final String SORT_ORDER = TrackerEntry.ID_COL + " DESC";
+
+ public TrackerListHelper(ListActivity activity) {
+ super(activity, TrackerDataHelper.CSV_FORMATTER);
+ mActivity = activity;
+ }
+
+ /**
+ * Helper method for binding the list activities UI to the tracker data
+ * Tracker data will be sorted in most-recent first order
+ * Will enable automatic UI changes as tracker data changes
+ *
+ * @param layout - layout to populate data
+ */
+ public void bindListUI(int layout) {
+ Cursor cursor = mActivity.managedQuery(TrackerProvider.CONTENT_URI,
+ TrackerEntry.ATTRIBUTES, null, null, SORT_ORDER);
+ // Used to map tracker entries from the database to views
+ TrackerAdapter adapter = new TrackerAdapter(mActivity, layout, cursor);
+ mActivity.setListAdapter(adapter);
+ cursor.setNotificationUri(mActivity.getContentResolver(),
+ TrackerProvider.CONTENT_URI);
+
+ }
+
+ private class TrackerAdapter extends ResourceCursorAdapter {
+
+ public TrackerAdapter(Context context, int layout, Cursor c) {
+ super(context, layout, c);
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ final TextView v = (TextView) view
+ .findViewById(R.id.entrylist_item);
+ String rowText = mFormatter.getOutput(TrackerEntry
+ .createEntry(cursor));
+ v.setText(rowText);
+ }
+ }
+}
diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java
index 4c12c2d..0412bc7 100644
--- a/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java
+++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java
@@ -65,4 +65,9 @@
}
return certs;
}
+
+ @Override
+ public void handleTrustStorageUpdate() {
+ // Nothing to do.
+ }
}
diff --git a/tests/SoundTriggerTestApp/AndroidManifest.xml b/tests/SoundTriggerTestApp/AndroidManifest.xml
index dc4cdb5..71d6001 100644
--- a/tests/SoundTriggerTestApp/AndroidManifest.xml
+++ b/tests/SoundTriggerTestApp/AndroidManifest.xml
@@ -2,7 +2,7 @@
package="com.android.test.soundtrigger">
<uses-permission android:name="android.permission.MANAGE_SOUND_TRIGGER" />
- <uses-permission android:name="android.permission.WAKE_LOCk" />
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
<application>
<activity
android:name="TestSoundTriggerActivity"
diff --git a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/stubhal/GenericSoundModelTest.java b/tests/SoundTriggerTests/src/android/hardware/soundtrigger/stubhal/GenericSoundModelTest.java
index ad02d2b..c0583ce 100644
--- a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/stubhal/GenericSoundModelTest.java
+++ b/tests/SoundTriggerTests/src/android/hardware/soundtrigger/stubhal/GenericSoundModelTest.java
@@ -18,6 +18,7 @@
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
@@ -40,6 +41,7 @@
import java.net.InetAddress;
import java.net.Socket;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.Random;
import java.util.UUID;
@@ -53,7 +55,7 @@
static final int MSG_GENERIC_TRIGGER = 4;
private Random random = new Random();
- private ArrayList<UUID> loadedModelUuids;
+ private HashSet<UUID> loadedModelUuids;
private ISoundTriggerService soundTriggerService;
private SoundTriggerManager soundTriggerManager;
@@ -68,7 +70,7 @@
soundTriggerManager = (SoundTriggerManager) context.getSystemService(
Context.SOUND_TRIGGER_SERVICE);
- loadedModelUuids = new ArrayList<UUID>();
+ loadedModelUuids = new HashSet<UUID>();
}
@Override
@@ -170,6 +172,101 @@
verify(spyCallback, timeout(100)).onGenericSoundTriggerDetected(any());
}
+ /**
+ * Tests a more complicated pattern of loading, unloading, triggering, starting and stopping
+ * recognition. Intended to find unexpected errors that occur in unexpected states.
+ */
+ @LargeTest
+ public void testFuzzGenericSoundModel() throws Exception {
+ int numModels = 2;
+
+ final int STATUS_UNLOADED = 0;
+ final int STATUS_LOADED = 1;
+ final int STATUS_STARTED = 2;
+
+ class ModelInfo {
+ int status;
+ GenericSoundModel model;
+
+ public ModelInfo(GenericSoundModel model, int status) {
+ this.status = status;
+ this.model = model;
+ }
+ }
+
+ Random predictableRandom = new Random(100);
+
+ ArrayList modelInfos = new ArrayList<ModelInfo>();
+ for(int i=0; i<numModels; i++) {
+ // Create sound model
+ byte[] data = new byte[1024];
+ predictableRandom.nextBytes(data);
+ UUID modelUuid = UUID.randomUUID();
+ UUID mVendorUuid = UUID.randomUUID();
+ GenericSoundModel model = new GenericSoundModel(modelUuid, mVendorUuid, data);
+ ModelInfo modelInfo = new ModelInfo(model, STATUS_UNLOADED);
+ modelInfos.add(modelInfo);
+ }
+
+ boolean captureTriggerAudio = true;
+ boolean allowMultipleTriggers = true;
+ RecognitionConfig config = new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
+ null, null);
+ TestRecognitionStatusCallback spyCallback = spy(new TestRecognitionStatusCallback());
+
+
+ int numOperationsToRun = 100;
+ for(int i=0; i<numOperationsToRun; i++) {
+ // Select a random model
+ int modelInfoIndex = predictableRandom.nextInt(modelInfos.size());
+ ModelInfo modelInfo = (ModelInfo) modelInfos.get(modelInfoIndex);
+
+ // Perform a random operation
+ int operation = predictableRandom.nextInt(5);
+
+ if (operation == 0 && modelInfo.status == STATUS_UNLOADED) {
+ // Update and start sound model
+ soundTriggerService.updateSoundModel(modelInfo.model);
+ loadedModelUuids.add(modelInfo.model.uuid);
+ modelInfo.status = STATUS_LOADED;
+ } else if (operation == 1 && modelInfo.status == STATUS_LOADED) {
+ // Start the sound model
+ int r = soundTriggerService.startRecognition(new ParcelUuid(modelInfo.model.uuid),
+ spyCallback, config);
+ assertEquals("Could Not Start Recognition with code: " + r,
+ android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
+ modelInfo.status = STATUS_STARTED;
+ } else if (operation == 2 && modelInfo.status == STATUS_STARTED) {
+ // Send trigger to stub HAL
+ Socket socket = new Socket(InetAddress.getLocalHost(), 14035);
+ DataOutputStream out = new DataOutputStream(socket.getOutputStream());
+ out.writeBytes("trig " + modelInfo.model.uuid + "\r\n");
+ out.flush();
+ socket.close();
+
+ // Verify trigger was received
+ verify(spyCallback, timeout(100)).onGenericSoundTriggerDetected(any());
+ reset(spyCallback);
+ } else if (operation == 3 && modelInfo.status == STATUS_STARTED) {
+ // Stop recognition
+ int r = soundTriggerService.stopRecognition(new ParcelUuid(modelInfo.model.uuid),
+ spyCallback);
+ assertEquals("Could Not Stop Recognition with code: " + r,
+ android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
+ modelInfo.status = STATUS_LOADED;
+ } else if (operation == 4 && modelInfo.status != STATUS_UNLOADED) {
+ // Delete sound model
+ soundTriggerService.deleteSoundModel(new ParcelUuid(modelInfo.model.uuid));
+ loadedModelUuids.remove(modelInfo.model.uuid);
+
+ // Confirm it was deleted
+ GenericSoundModel returnedModel =
+ soundTriggerService.getSoundModel(new ParcelUuid(modelInfo.model.uuid));
+ assertEquals(null, returnedModel);
+ modelInfo.status = STATUS_UNLOADED;
+ }
+ }
+ }
public class TestRecognitionStatusCallback extends IRecognitionStatusCallback.Stub {
@Override
diff --git a/tests/VectorDrawableTest/Android.mk b/tests/VectorDrawableTest/Android.mk
index 3d44e33..dd8a4d4 100644
--- a/tests/VectorDrawableTest/Android.mk
+++ b/tests/VectorDrawableTest/Android.mk
@@ -23,6 +23,4 @@
LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := current
-
include $(BUILD_PACKAGE)
diff --git a/tests/VectorDrawableTest/AndroidManifest.xml b/tests/VectorDrawableTest/AndroidManifest.xml
index e648897..7b3beb2 100644
--- a/tests/VectorDrawableTest/AndroidManifest.xml
+++ b/tests/VectorDrawableTest/AndroidManifest.xml
@@ -18,8 +18,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.test.dynamic" >
- <uses-sdk android:minSdkVersion="21" />
-
<application
android:hardwareAccelerated="true"
android:label="vector"
diff --git a/tests/VectorDrawableTest/res/drawable/btn_radio_on_to_off_bundle.xml b/tests/VectorDrawableTest/res/drawable/btn_radio_on_to_off_bundle.xml
new file mode 100644
index 0000000..4f05090
--- /dev/null
+++ b/tests/VectorDrawableTest/res/drawable/btn_radio_on_to_off_bundle.xml
@@ -0,0 +1,190 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<animated-vector xmlns:aapt="http://schemas.android.com/aapt"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="32dp"
+ android:viewportWidth="32"
+ android:height="32dp"
+ android:viewportHeight="32">
+ <group
+ android:name="btn_radio_to_off_mtrl_0"
+ android:translateX="16"
+ android:translateY="16">
+ <group
+ android:name="ring_outer">
+ <path
+ android:name="ring_outer_path"
+ android:strokeColor="#FF000000"
+ android:strokeWidth="2"
+ android:pathData="M 0.0,-9.0 c 4.9705627482,0.0 9.0,4.0294372518 9.0,9.0 c 0.0,4.9705627482 -4.0294372518,9.0 -9.0,9.0 c -4.9705627482,0.0 -9.0,-4.0294372518 -9.0,-9.0 c 0.0,-4.9705627482 4.0294372518,-9.0 9.0,-9.0 Z"/>
+ </group>
+ <group
+ android:name="dot_group">
+ <path
+ android:name="dot_path"
+ android:pathData="M 0.0,-5.0 c -2.7619934082,0.0 -5.0,2.2380065918 -5.0,5.0 c 0.0,2.7619934082 2.2380065918,5.0 5.0,5.0 c 2.7619934082,0.0 5.0,-2.2380065918 5.0,-5.0 c 0.0,-2.7619934082 -2.2380065918,-5.0 -5.0,-5.0 Z"
+ android:fillColor="#FF000000"/>
+ </group>
+ </group>
+ </vector>
+ </aapt:attr>
+ <target android:name="ring_outer">
+ <aapt:attr name="android:animation">
+ <set
+ xmlns:android="http://schemas.android.com/apk/res/android" >
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="183"
+ android:propertyName="scaleX"
+ android:valueFrom="1.0"
+ android:valueTo="0.9"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ <objectAnimator
+ android:duration="16"
+ android:propertyName="scaleX"
+ android:valueFrom="0.9"
+ android:valueTo="0.5"
+ android:valueType="floatType"
+ android:interpolator="@interpolator/btn_radio_to_off_mtrl_animation_interpolator_0" />
+ <objectAnimator
+ android:duration="300"
+ android:propertyName="scaleX"
+ android:valueFrom="0.5"
+ android:valueTo="1.0"
+ android:valueType="floatType"
+ android:interpolator="@interpolator/btn_radio_to_off_mtrl_animation_interpolator_0" />
+ </set>
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="183"
+ android:propertyName="scaleY"
+ android:valueFrom="1.0"
+ android:valueTo="0.9"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ <objectAnimator
+ android:duration="16"
+ android:propertyName="scaleY"
+ android:valueFrom="0.9"
+ android:valueTo="0.5"
+ android:valueType="floatType"
+ android:interpolator="@interpolator/btn_radio_to_off_mtrl_animation_interpolator_0" />
+ <objectAnimator
+ android:duration="300"
+ android:propertyName="scaleY"
+ android:valueFrom="0.5"
+ android:valueTo="1.0"
+ android:valueType="floatType"
+ android:interpolator="@interpolator/btn_radio_to_off_mtrl_animation_interpolator_0" />
+ </set>
+ </set>
+ </aapt:attr>
+ </target>
+
+ <target android:name="ring_outer_path">
+ <aapt:attr name="android:animation">
+ <set
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <set
+ android:ordering="sequentially">
+ <objectAnimator
+ android:duration="183"
+ android:propertyName="strokeWidth"
+ android:valueFrom="2.0"
+ android:valueTo="2.0"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in"/>
+ <objectAnimator
+ android:duration="16"
+ android:propertyName="strokeWidth"
+ android:valueFrom="2.0"
+ android:valueTo="18.0"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in"/>
+ <objectAnimator
+ android:duration="300"
+ android:propertyName="strokeWidth"
+ android:valueFrom="18.0"
+ android:valueTo="2.0"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in"/>
+ </set>
+
+ </set>
+ </aapt:attr>
+ </target>
+ <target
+ android:name="dot_group">
+ <aapt:attr name="android:animation">
+ <set
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <set
+ android:ordering="sequentially">
+ <objectAnimator
+ android:duration="183"
+ android:propertyName="scaleX"
+ android:valueFrom="1.0"
+ android:valueTo="1.4"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in"/>
+ <objectAnimator
+ android:duration="16"
+ android:propertyName="scaleX"
+ android:valueFrom="1.4"
+ android:valueTo="0.0"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in"/>
+ <objectAnimator
+ android:duration="300"
+ android:propertyName="scaleX"
+ android:valueFrom="0.0"
+ android:valueTo="0.0"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in"/>
+ </set>
+ <set
+ android:ordering="sequentially">
+ <objectAnimator
+ android:duration="183"
+ android:propertyName="scaleY"
+ android:valueFrom="1.0"
+ android:valueTo="1.4"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in"/>
+ <objectAnimator
+ android:duration="16"
+ android:propertyName="scaleY"
+ android:valueFrom="1.4"
+ android:valueTo="0.0"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in"/>
+ <objectAnimator
+ android:duration="300"
+ android:propertyName="scaleY"
+ android:valueFrom="0.0"
+ android:valueTo="0.0"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in"/>
+ </set>
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable_linear_progress_bar.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable_linear_progress_bar.xml
index 96fd70e..a6da114 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable_linear_progress_bar.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable_linear_progress_bar.xml
@@ -17,7 +17,7 @@
android:height="4dp"
android:viewportHeight="4"
android:viewportWidth="360"
- android:width="360dp" >
+ android:width="36dp" >
<group
android:name="linear_indeterminate"
diff --git a/tests/VectorDrawableTest/res/interpolator/btn_radio_to_off_mtrl_animation_interpolator_0.xml b/tests/VectorDrawableTest/res/interpolator/btn_radio_to_off_mtrl_animation_interpolator_0.xml
new file mode 100644
index 0000000..d3728c4
--- /dev/null
+++ b/tests/VectorDrawableTest/res/interpolator/btn_radio_to_off_mtrl_animation_interpolator_0.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<pathInterpolator
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:pathData="M 0.0,0.0 c 0.4,0.0 0.4,1.0 1.0,1.0" />
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java
index 087e68a..8f538ae 100644
--- a/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java
+++ b/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java
@@ -24,11 +24,13 @@
import android.widget.Button;
import android.widget.GridLayout;
import android.widget.ScrollView;
+import android.widget.TextView;
public class AnimatedVectorDrawableTest extends Activity implements View.OnClickListener {
private static final String LOGCAT = "AnimatedVectorDrawableTest";
protected int[] icon = {
+ R.drawable.btn_radio_on_to_off_bundle,
R.drawable.ic_rotate_2_portrait_v2_animation,
R.drawable.ic_signal_airplane_v2_animation,
R.drawable.ic_hourglass_animation,
@@ -43,33 +45,53 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
+ final int[] layerTypes = {View.LAYER_TYPE_SOFTWARE, View.LAYER_TYPE_HARDWARE};
+ final boolean[] forceOnUi = {false, true};
super.onCreate(savedInstanceState);
ScrollView scrollView = new ScrollView(this);
GridLayout container = new GridLayout(this);
scrollView.addView(container);
- container.setColumnCount(1);
+ container.setColumnCount(layerTypes.length * forceOnUi.length);
+ for (int j = 0; j < layerTypes.length; j++) {
+ for (int k = 0; k < forceOnUi.length; k++) {
+ TextView textView = new TextView(this);
+ String category = "Layer:"
+ + (layerTypes[j] == View.LAYER_TYPE_SOFTWARE ? "SW" : "HW")
+ + (forceOnUi[k] == true ? ",forceUI" : "");
+ textView.setText(category);
+ container.addView(textView);
+ }
+ }
for (int i = 0; i < icon.length; i++) {
- Button button = new Button(this);
- button.setWidth(400);
- button.setHeight(400);
- button.setBackgroundResource(icon[i]);
- AnimatedVectorDrawable d = (AnimatedVectorDrawable) button.getBackground();
- d.registerAnimationCallback(new Animatable2.AnimationCallback() {
- @Override
- public void onAnimationStart(Drawable drawable) {
- Log.v(LOGCAT, "Animator start");
- }
+ for (int j = 0; j < layerTypes.length; j++) {
+ for (int k = 0; k < forceOnUi.length; k++) {
+ Button button = new Button(this);
+ button.setWidth(300);
+ button.setHeight(300);
+ button.setLayerType(layerTypes[j], null);
+ button.setBackgroundResource(icon[i]);
+ AnimatedVectorDrawable d = (AnimatedVectorDrawable) button.getBackground();
+ if (forceOnUi[k] == true) {
+ d.forceAnimationOnUI();
+ }
+ d.registerAnimationCallback(new Animatable2.AnimationCallback() {
+ @Override
+ public void onAnimationStart(Drawable drawable) {
+ Log.v(LOGCAT, "Animator start");
+ }
- @Override
- public void onAnimationEnd(Drawable drawable) {
- Log.v(LOGCAT, "Animator end");
- }
- });
+ @Override
+ public void onAnimationEnd(Drawable drawable) {
+ Log.v(LOGCAT, "Animator end");
+ }
+ });
- container.addView(button);
- button.setOnClickListener(this);
+ container.addView(button);
+ button.setOnClickListener(this);
+ }
+ }
}
setContentView(scrollView);
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index cbd8480..c1cfd0b 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -388,10 +388,6 @@
if (script[0]) {
memcpy(out->localeScript, script, sizeof(out->localeScript));
- out->localeScriptWasComputed = false;
- } else {
- out->computeScript();
- out->localeScriptWasComputed = true;
}
if (variant[0]) {
diff --git a/tools/aapt/Package.cpp b/tools/aapt/Package.cpp
index 641c34b..d631f35 100644
--- a/tools/aapt/Package.cpp
+++ b/tools/aapt/Package.cpp
@@ -33,7 +33,7 @@
".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet",
".rtttl", ".imy", ".xmf", ".mp4", ".m4a",
".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2",
- ".amr", ".awb", ".wma", ".wmv", ".webm"
+ ".amr", ".awb", ".wma", ".wmv", ".webm", ".mkv"
};
/* fwd decls, so I can write this downward */
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index 85d22ff..ef11d66 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -54,6 +54,7 @@
Debug.cpp \
Flags.cpp \
java/AnnotationProcessor.cpp \
+ java/ClassDefinition.cpp \
java/JavaClassGenerator.cpp \
java/ManifestClassGenerator.cpp \
java/ProguardRules.cpp \
@@ -65,6 +66,7 @@
ResourceValues.cpp \
SdkConstants.cpp \
StringPool.cpp \
+ xml/XmlActionExecutor.cpp \
xml/XmlDom.cpp \
xml/XmlPullParser.cpp \
xml/XmlUtil.cpp
@@ -106,6 +108,7 @@
SdkConstants_test.cpp \
StringPool_test.cpp \
ValueVisitor_test.cpp \
+ xml/XmlActionExecutor_test.cpp \
xml/XmlDom_test.cpp \
xml/XmlPullParser_test.cpp \
xml/XmlUtil_test.cpp
@@ -139,7 +142,7 @@
endif
cFlags := -Wall -Werror -Wno-unused-parameter -UNDEBUG
-cppFlags := -std=c++11 -Wno-missing-field-initializers -fno-exceptions -fno-rtti
+cppFlags := -std=c++14 -Wno-missing-field-initializers -fno-exceptions -fno-rtti
protoIncludes := $(call generated-sources-dir-for,STATIC_LIBRARIES,libaapt2,HOST)
# ==========================================================
diff --git a/tools/aapt2/Diagnostics.h b/tools/aapt2/Diagnostics.h
index ab4d284..e86f2a8 100644
--- a/tools/aapt2/Diagnostics.h
+++ b/tools/aapt2/Diagnostics.h
@@ -21,6 +21,7 @@
#include "util/StringPiece.h"
#include "util/Util.h"
+#include <android-base/macros.h>
#include <iostream>
#include <sstream>
#include <string>
@@ -46,7 +47,11 @@
DiagMessage(const Source& src) : mSource(src) {
}
- template <typename T> DiagMessage& operator<<(const T& value) {
+ DiagMessage(size_t line) : mSource(Source().withLine(line)) {
+ }
+
+ template <typename T>
+ DiagMessage& operator<<(const T& value) {
mMessage << value;
return *this;
}
@@ -59,36 +64,82 @@
struct IDiagnostics {
virtual ~IDiagnostics() = default;
- virtual void error(const DiagMessage& message) = 0;
- virtual void warn(const DiagMessage& message) = 0;
- virtual void note(const DiagMessage& message) = 0;
+ enum class Level {
+ Note,
+ Warn,
+ Error
+ };
+
+ virtual void log(Level level, DiagMessageActual& actualMsg) = 0;
+
+ virtual void error(const DiagMessage& message) {
+ DiagMessageActual actual = message.build();
+ log(Level::Error, actual);
+ }
+
+ virtual void warn(const DiagMessage& message) {
+ DiagMessageActual actual = message.build();
+ log(Level::Warn, actual);
+ }
+
+ virtual void note(const DiagMessage& message) {
+ DiagMessageActual actual = message.build();
+ log(Level::Note, actual);
+ }
};
-struct StdErrDiagnostics : public IDiagnostics {
+class StdErrDiagnostics : public IDiagnostics {
+public:
+ StdErrDiagnostics() = default;
+
+ void log(Level level, DiagMessageActual& actualMsg) override {
+ const char* tag;
+
+ switch (level) {
+ case Level::Error:
+ mNumErrors++;
+ if (mNumErrors > 20) {
+ return;
+ }
+ tag = "error";
+ break;
+
+ case Level::Warn:
+ tag = "warn";
+ break;
+
+ case Level::Note:
+ tag = "note";
+ break;
+ }
+
+ if (!actualMsg.source.path.empty()) {
+ std::cerr << actualMsg.source << ": ";
+ }
+ std::cerr << tag << ": " << actualMsg.message << "." << std::endl;
+ }
+
+private:
size_t mNumErrors = 0;
- void emit(const DiagMessage& msg, const char* tag) {
- DiagMessageActual actual = msg.build();
- if (!actual.source.path.empty()) {
- std::cerr << actual.source << ": ";
- }
- std::cerr << tag << actual.message << "." << std::endl;
+ DISALLOW_COPY_AND_ASSIGN(StdErrDiagnostics);
+};
+
+class SourcePathDiagnostics : public IDiagnostics {
+public:
+ SourcePathDiagnostics(const Source& src, IDiagnostics* diag) : mSource(src), mDiag(diag) {
}
- void error(const DiagMessage& msg) override {
- if (mNumErrors < 20) {
- emit(msg, "error: ");
- }
- mNumErrors++;
+ void log(Level level, DiagMessageActual& actualMsg) override {
+ actualMsg.source.path = mSource.path;
+ mDiag->log(level, actualMsg);
}
- void warn(const DiagMessage& msg) override {
- emit(msg, "warn: ");
- }
+private:
+ Source mSource;
+ IDiagnostics* mDiag;
- void note(const DiagMessage& msg) override {
- emit(msg, "note: ");
- }
+ DISALLOW_COPY_AND_ASSIGN(SourcePathDiagnostics);
};
} // namespace aapt
diff --git a/tools/aapt2/Locale.cpp b/tools/aapt2/Locale.cpp
index 12f56fc..be57661 100644
--- a/tools/aapt2/Locale.cpp
+++ b/tools/aapt2/Locale.cpp
@@ -268,10 +268,6 @@
if (script[0]) {
memcpy(out->localeScript, script, sizeof(out->localeScript));
- out->localeScriptWasComputed = false;
- } else {
- out->computeScript();
- out->localeScriptWasComputed = true;
}
if (variant[0]) {
diff --git a/tools/aapt2/ResourceTable_test.cpp b/tools/aapt2/ResourceTable_test.cpp
index 180bd11..d6c52ab 100644
--- a/tools/aapt2/ResourceTable_test.cpp
+++ b/tools/aapt2/ResourceTable_test.cpp
@@ -28,33 +28,23 @@
namespace aapt {
-struct ResourceTableTest : public ::testing::Test {
- struct EmptyDiagnostics : public IDiagnostics {
- void error(const DiagMessage& msg) override {}
- void warn(const DiagMessage& msg) override {}
- void note(const DiagMessage& msg) override {}
- };
-
- EmptyDiagnostics mDiagnostics;
-};
-
-TEST_F(ResourceTableTest, FailToAddResourceWithBadName) {
+TEST(ResourceTableTest, FailToAddResourceWithBadName) {
ResourceTable table;
EXPECT_FALSE(table.addResource(
ResourceNameRef(u"android", ResourceType::kId, u"hey,there"),
ConfigDescription{}, "",
test::ValueBuilder<Id>().setSource("test.xml", 21u).build(),
- &mDiagnostics));
+ test::getDiagnostics()));
EXPECT_FALSE(table.addResource(
ResourceNameRef(u"android", ResourceType::kId, u"hey:there"),
ConfigDescription{}, "",
test::ValueBuilder<Id>().setSource("test.xml", 21u).build(),
- &mDiagnostics));
+ test::getDiagnostics()));
}
-TEST_F(ResourceTableTest, AddOneResource) {
+TEST(ResourceTableTest, AddOneResource) {
ResourceTable table;
EXPECT_TRUE(table.addResource(test::parseNameOrDie(u"@android:attr/id"),
@@ -62,12 +52,12 @@
"",
test::ValueBuilder<Id>()
.setSource("test/path/file.xml", 23u).build(),
- &mDiagnostics));
+ test::getDiagnostics()));
ASSERT_NE(nullptr, test::getValue<Id>(&table, u"@android:attr/id"));
}
-TEST_F(ResourceTableTest, AddMultipleResources) {
+TEST(ResourceTableTest, AddMultipleResources) {
ResourceTable table;
ConfigDescription config;
@@ -79,21 +69,21 @@
config,
"",
test::ValueBuilder<Id>().setSource("test/path/file.xml", 10u).build(),
- &mDiagnostics));
+ test::getDiagnostics()));
EXPECT_TRUE(table.addResource(
test::parseNameOrDie(u"@android:attr/id"),
config,
"",
test::ValueBuilder<Id>().setSource("test/path/file.xml", 12u).build(),
- &mDiagnostics));
+ test::getDiagnostics()));
EXPECT_TRUE(table.addResource(
test::parseNameOrDie(u"@android:string/ok"),
config,
"",
test::ValueBuilder<Id>().setSource("test/path/file.xml", 14u).build(),
- &mDiagnostics));
+ test::getDiagnostics()));
EXPECT_TRUE(table.addResource(
test::parseNameOrDie(u"@android:string/ok"),
@@ -102,7 +92,7 @@
test::ValueBuilder<BinaryPrimitive>(android::Res_value{})
.setSource("test/path/file.xml", 20u)
.build(),
- &mDiagnostics));
+ test::getDiagnostics()));
ASSERT_NE(nullptr, test::getValue<Id>(&table, u"@android:attr/layout_width"));
ASSERT_NE(nullptr, test::getValue<Id>(&table, u"@android:attr/id"));
@@ -111,37 +101,37 @@
languageConfig));
}
-TEST_F(ResourceTableTest, OverrideWeakResourceValue) {
+TEST(ResourceTableTest, OverrideWeakResourceValue) {
ResourceTable table;
ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:attr/foo"), ConfigDescription{},
- "", util::make_unique<Attribute>(true), &mDiagnostics));
+ "", util::make_unique<Attribute>(true), test::getDiagnostics()));
Attribute* attr = test::getValue<Attribute>(&table, u"@android:attr/foo");
ASSERT_NE(nullptr, attr);
EXPECT_TRUE(attr->isWeak());
ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:attr/foo"), ConfigDescription{},
- "", util::make_unique<Attribute>(false), &mDiagnostics));
+ "", util::make_unique<Attribute>(false), test::getDiagnostics()));
attr = test::getValue<Attribute>(&table, u"@android:attr/foo");
ASSERT_NE(nullptr, attr);
EXPECT_FALSE(attr->isWeak());
}
-TEST_F(ResourceTableTest, ProductVaryingValues) {
+TEST(ResourceTableTest, ProductVaryingValues) {
ResourceTable table;
EXPECT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/foo"),
test::parseConfigOrDie("land"),
"tablet",
util::make_unique<Id>(),
- &mDiagnostics));
+ test::getDiagnostics()));
EXPECT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/foo"),
test::parseConfigOrDie("land"),
"phone",
util::make_unique<Id>(),
- &mDiagnostics));
+ test::getDiagnostics()));
EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, u"@android:string/foo",
test::parseConfigOrDie("land"),
diff --git a/tools/aapt2/integration-tests/StaticLibOne/Android.mk b/tools/aapt2/integration-tests/StaticLibOne/Android.mk
index d59dc60..0b7129a 100644
--- a/tools/aapt2/integration-tests/StaticLibOne/Android.mk
+++ b/tools/aapt2/integration-tests/StaticLibOne/Android.mk
@@ -22,4 +22,7 @@
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under,src)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+# We need this to compile the Java sources of AaptTestStaticLibTwo using javac.
+LOCAL_JAR_EXCLUDE_FILES := none
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tools/aapt2/java/AnnotationProcessor.cpp b/tools/aapt2/java/AnnotationProcessor.cpp
index 496e92e..ba74439 100644
--- a/tools/aapt2/java/AnnotationProcessor.cpp
+++ b/tools/aapt2/java/AnnotationProcessor.cpp
@@ -64,7 +64,7 @@
mComment << "\n *";
}
-void AnnotationProcessor::writeToStream(std::ostream* out, const StringPiece& prefix) {
+void AnnotationProcessor::writeToStream(std::ostream* out, const StringPiece& prefix) const {
if (mHasComments) {
std::string result = mComment.str();
for (StringPiece line : util::tokenize<char>(result, '\n')) {
diff --git a/tools/aapt2/java/AnnotationProcessor.h b/tools/aapt2/java/AnnotationProcessor.h
index fadf584..0fc5b08 100644
--- a/tools/aapt2/java/AnnotationProcessor.h
+++ b/tools/aapt2/java/AnnotationProcessor.h
@@ -66,7 +66,7 @@
/**
* Writes the comments and annotations to the stream, with the given prefix before each line.
*/
- void writeToStream(std::ostream* out, const StringPiece& prefix);
+ void writeToStream(std::ostream* out, const StringPiece& prefix) const;
private:
enum : uint32_t {
diff --git a/tools/aapt2/java/ClassDefinition.cpp b/tools/aapt2/java/ClassDefinition.cpp
new file mode 100644
index 0000000..08f2c8b
--- /dev/null
+++ b/tools/aapt2/java/ClassDefinition.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "java/ClassDefinition.h"
+#include "util/StringPiece.h"
+
+#include <ostream>
+
+namespace aapt {
+
+bool ClassDefinition::empty() const {
+ for (const std::unique_ptr<ClassMember>& member : mMembers) {
+ if (!member->empty()) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void ClassDefinition::writeToStream(const StringPiece& prefix, bool final,
+ std::ostream* out) const {
+ if (mMembers.empty() && !mCreateIfEmpty) {
+ return;
+ }
+
+ ClassMember::writeToStream(prefix, final, out);
+
+ *out << prefix << "public ";
+ if (mQualifier == ClassQualifier::Static) {
+ *out << "static ";
+ }
+ *out << "final class " << mName << " {\n";
+
+ std::string newPrefix = prefix.toString();
+ newPrefix.append(kIndent);
+
+ for (const std::unique_ptr<ClassMember>& member : mMembers) {
+ member->writeToStream(newPrefix, final, out);
+ *out << "\n";
+ }
+
+ *out << prefix << "}";
+}
+
+constexpr static const char* sWarningHeader =
+ "/* AUTO-GENERATED FILE. DO NOT MODIFY.\n"
+ " *\n"
+ " * This class was automatically generated by the\n"
+ " * aapt tool from the resource data it found. It\n"
+ " * should not be modified by hand.\n"
+ " */\n\n";
+
+bool ClassDefinition::writeJavaFile(const ClassDefinition* def,
+ const StringPiece& package,
+ bool final,
+ std::ostream* out) {
+ *out << sWarningHeader << "package " << package << ";\n\n";
+ def->writeToStream("", final, out);
+ return bool(*out);
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/java/ClassDefinition.h b/tools/aapt2/java/ClassDefinition.h
new file mode 100644
index 0000000..53e0f6f
--- /dev/null
+++ b/tools/aapt2/java/ClassDefinition.h
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAPT_JAVA_CLASSDEFINITION_H
+#define AAPT_JAVA_CLASSDEFINITION_H
+
+#include "Resource.h"
+#include "java/AnnotationProcessor.h"
+#include "util/StringPiece.h"
+#include "util/Util.h"
+
+#include <android-base/macros.h>
+#include <sstream>
+#include <string>
+
+namespace aapt {
+
+// The number of attributes to emit per line in a Styleable array.
+constexpr static size_t kAttribsPerLine = 4;
+constexpr static const char* kIndent = " ";
+
+class ClassMember {
+public:
+ virtual ~ClassMember() = default;
+
+ AnnotationProcessor* getCommentBuilder() {
+ return &mProcessor;
+ }
+
+ virtual bool empty() const = 0;
+
+ virtual void writeToStream(const StringPiece& prefix, bool final, std::ostream* out) const {
+ mProcessor.writeToStream(out, prefix);
+ }
+
+private:
+ AnnotationProcessor mProcessor;
+};
+
+template <typename T>
+class PrimitiveMember : public ClassMember {
+public:
+ PrimitiveMember(const StringPiece& name, const T& val) :
+ mName(name.toString()), mVal(val) {
+ }
+
+ bool empty() const override {
+ return false;
+ }
+
+ void writeToStream(const StringPiece& prefix, bool final, std::ostream* out) const override {
+ ClassMember::writeToStream(prefix, final, out);
+
+ *out << prefix << "public static " << (final ? "final " : "")
+ << "int " << mName << "=" << mVal << ";";
+ }
+
+private:
+ std::string mName;
+ T mVal;
+
+ DISALLOW_COPY_AND_ASSIGN(PrimitiveMember);
+};
+
+/**
+ * Specialization for strings so they get the right type and are quoted with "".
+ */
+template <>
+class PrimitiveMember<std::string> : public ClassMember {
+public:
+ PrimitiveMember(const StringPiece& name, const std::string& val) :
+ mName(name.toString()), mVal(val) {
+ }
+
+ bool empty() const override {
+ return false;
+ }
+
+ void writeToStream(const StringPiece& prefix, bool final, std::ostream* out) const override {
+ ClassMember::writeToStream(prefix, final, out);
+
+ *out << prefix << "public static " << (final ? "final " : "")
+ << "String " << mName << "=\"" << mVal << "\";";
+ }
+
+private:
+ std::string mName;
+ std::string mVal;
+
+ DISALLOW_COPY_AND_ASSIGN(PrimitiveMember);
+};
+
+using IntMember = PrimitiveMember<uint32_t>;
+using ResourceMember = PrimitiveMember<ResourceId>;
+using StringMember = PrimitiveMember<std::string>;
+
+template <typename T>
+class PrimitiveArrayMember : public ClassMember {
+public:
+ PrimitiveArrayMember(const StringPiece& name) :
+ mName(name.toString()) {
+ }
+
+ void addElement(const T& val) {
+ mElements.push_back(val);
+ }
+
+ bool empty() const override {
+ return false;
+ }
+
+ void writeToStream(const StringPiece& prefix, bool final, std::ostream* out) const override {
+ ClassMember::writeToStream(prefix, final, out);
+
+ *out << "public static final int[] " << mName << "={";
+
+ const auto begin = mElements.begin();
+ const auto end = mElements.end();
+ for (auto current = begin; current != end; ++current) {
+ if (std::distance(begin, current) % kAttribsPerLine == 0) {
+ *out << "\n" << prefix << kIndent << kIndent;
+ }
+
+ *out << *current;
+ if (std::distance(current, end) > 1) {
+ *out << ", ";
+ }
+ }
+ *out << "\n" << prefix << kIndent <<"};";
+ }
+
+private:
+ std::string mName;
+ std::vector<T> mElements;
+
+ DISALLOW_COPY_AND_ASSIGN(PrimitiveArrayMember);
+};
+
+using ResourceArrayMember = PrimitiveArrayMember<ResourceId>;
+
+enum class ClassQualifier {
+ None,
+ Static
+};
+
+class ClassDefinition : public ClassMember {
+public:
+ static bool writeJavaFile(const ClassDefinition* def,
+ const StringPiece& package,
+ bool final,
+ std::ostream* out);
+
+ ClassDefinition(const StringPiece& name, ClassQualifier qualifier, bool createIfEmpty) :
+ mName(name.toString()), mQualifier(qualifier), mCreateIfEmpty(createIfEmpty) {
+ }
+
+ void addMember(std::unique_ptr<ClassMember> member) {
+ mMembers.push_back(std::move(member));
+ }
+
+ bool empty() const override;
+ void writeToStream(const StringPiece& prefix, bool final, std::ostream* out) const override;
+
+private:
+ std::string mName;
+ ClassQualifier mQualifier;
+ bool mCreateIfEmpty;
+ std::vector<std::unique_ptr<ClassMember>> mMembers;
+
+ DISALLOW_COPY_AND_ASSIGN(ClassDefinition);
+};
+
+} // namespace aapt
+
+#endif /* AAPT_JAVA_CLASSDEFINITION_H */
diff --git a/tools/aapt2/java/ClassDefinitionWriter.h b/tools/aapt2/java/ClassDefinitionWriter.h
deleted file mode 100644
index cf92c9a..0000000
--- a/tools/aapt2/java/ClassDefinitionWriter.h
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef AAPT_JAVA_CLASSDEFINITION_H
-#define AAPT_JAVA_CLASSDEFINITION_H
-
-#include "Resource.h"
-#include "java/AnnotationProcessor.h"
-#include "util/StringPiece.h"
-#include "util/Util.h"
-
-#include <sstream>
-#include <string>
-
-namespace aapt {
-
-struct ClassDefinitionWriterOptions {
- bool useFinalQualifier = false;
- bool forceCreationIfEmpty = false;
-};
-
-/**
- * Writes a class for use in R.java or Manifest.java.
- */
-class ClassDefinitionWriter {
-public:
- ClassDefinitionWriter(const StringPiece& name, const ClassDefinitionWriterOptions& options) :
- mName(name.toString()), mOptions(options), mStarted(false) {
- }
-
- ClassDefinitionWriter(const StringPiece16& name, const ClassDefinitionWriterOptions& options) :
- mName(util::utf16ToUtf8(name)), mOptions(options), mStarted(false) {
- }
-
- void addIntMember(const StringPiece& name, AnnotationProcessor* processor,
- const uint32_t val) {
- ensureClassDeclaration();
- if (processor) {
- processor->writeToStream(&mOut, kIndent);
- }
- mOut << kIndent << "public static " << (mOptions.useFinalQualifier ? "final " : "")
- << "int " << name << "=" << val << ";\n";
- }
-
- void addStringMember(const StringPiece16& name, AnnotationProcessor* processor,
- const StringPiece16& val) {
- ensureClassDeclaration();
- if (processor) {
- processor->writeToStream(&mOut, kIndent);
- }
- mOut << kIndent << "public static " << (mOptions.useFinalQualifier ? "final " : "")
- << "String " << name << "=\"" << val << "\";\n";
- }
-
- void addResourceMember(const StringPiece& name, AnnotationProcessor* processor,
- const ResourceId id) {
- ensureClassDeclaration();
- if (processor) {
- processor->writeToStream(&mOut, kIndent);
- }
- mOut << kIndent << "public static " << (mOptions.useFinalQualifier ? "final " : "")
- << "int " << name << "=" << id <<";\n";
- }
-
- template <typename Iterator, typename FieldAccessorFunc>
- void addArrayMember(const StringPiece& name, AnnotationProcessor* processor,
- const Iterator begin, const Iterator end, FieldAccessorFunc f) {
- ensureClassDeclaration();
- if (processor) {
- processor->writeToStream(&mOut, kIndent);
- }
- mOut << kIndent << "public static final int[] " << name << "={";
-
- for (Iterator current = begin; current != end; ++current) {
- if (std::distance(begin, current) % kAttribsPerLine == 0) {
- mOut << "\n" << kIndent << kIndent;
- }
-
- mOut << f(*current);
- if (std::distance(current, end) > 1) {
- mOut << ", ";
- }
- }
- mOut << "\n" << kIndent <<"};\n";
- }
-
- void writeToStream(std::ostream* out, const StringPiece& prefix,
- AnnotationProcessor* processor=nullptr) {
- if (mOptions.forceCreationIfEmpty) {
- ensureClassDeclaration();
- }
-
- if (!mStarted) {
- return;
- }
-
- if (processor) {
- processor->writeToStream(out, prefix);
- }
-
- std::string result = mOut.str();
- for (StringPiece line : util::tokenize<char>(result, '\n')) {
- *out << prefix << line << "\n";
- }
- *out << prefix << "}\n";
- }
-
-private:
- constexpr static const char* kIndent = " ";
-
- // The number of attributes to emit per line in a Styleable array.
- constexpr static size_t kAttribsPerLine = 4;
-
- void ensureClassDeclaration() {
- if (!mStarted) {
- mStarted = true;
- mOut << "public static final class " << mName << " {\n";
- }
- }
-
- std::stringstream mOut;
- std::string mName;
- ClassDefinitionWriterOptions mOptions;
- bool mStarted;
-};
-
-} // namespace aapt
-
-#endif /* AAPT_JAVA_CLASSDEFINITION_H */
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index 01330dc..092bab2 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -21,7 +21,7 @@
#include "ValueVisitor.h"
#include "java/AnnotationProcessor.h"
-#include "java/ClassDefinitionWriter.h"
+#include "java/ClassDefinition.h"
#include "java/JavaClassGenerator.h"
#include "process/SymbolTable.h"
#include "util/StringPiece.h"
@@ -39,16 +39,6 @@
mContext(context), mTable(table), mOptions(options) {
}
-static void generateHeader(const StringPiece16& packageNameToGenerate, std::ostream* out) {
- *out << "/* AUTO-GENERATED FILE. DO NOT MODIFY.\n"
- " *\n"
- " * This class was automatically generated by the\n"
- " * aapt tool from the resource data it found. It\n"
- " * should not be modified by hand.\n"
- " */\n\n"
- "package " << packageNameToGenerate << ";\n\n";
-}
-
static const std::set<StringPiece16> sJavaIdentifiers = {
u"abstract", u"assert", u"boolean", u"break", u"byte",
u"case", u"catch", u"char", u"class", u"const", u"continue",
@@ -110,15 +100,15 @@
if (typeMask & android::ResTable_map::TYPE_REFERENCE) {
processor->appendComment(
"<p>May be a reference to another resource, in the form\n"
- "\"<code>@[+][<i>package</i>:]<i>type</i>/<i>name</i></code>\" or a theme\n"
- "attribute in the form\n"
- "\"<code>?[<i>package</i>:]<i>type</i>/<i>name</i></code>\".");
+ "\"<code>@[+][<i>package</i>:]<i>type</i>/<i>name</i></code>\" or a theme\n"
+ "attribute in the form\n"
+ "\"<code>?[<i>package</i>:]<i>type</i>/<i>name</i></code>\".");
}
if (typeMask & android::ResTable_map::TYPE_STRING) {
processor->appendComment(
"<p>May be a string value, using '\\\\;' to escape characters such as\n"
- "'\\\\n' or '\\\\uxxxx' for a unicode character;");
+ "'\\\\n' or '\\\\uxxxx' for a unicode character;");
}
if (typeMask & android::ResTable_map::TYPE_INTEGER) {
@@ -128,14 +118,14 @@
if (typeMask & android::ResTable_map::TYPE_BOOLEAN) {
processor->appendComment(
"<p>May be a boolean value, such as \"<code>true</code>\" or\n"
- "\"<code>false</code>\".");
+ "\"<code>false</code>\".");
}
if (typeMask & android::ResTable_map::TYPE_COLOR) {
processor->appendComment(
"<p>May be a color value, in the form of \"<code>#<i>rgb</i></code>\",\n"
- "\"<code>#<i>argb</i></code>\", \"<code>#<i>rrggbb</i></code\", or \n"
- "\"<code>#<i>aarrggbb</i></code>\".");
+ "\"<code>#<i>argb</i></code>\", \"<code>#<i>rrggbb</i></code\", or \n"
+ "\"<code>#<i>aarrggbb</i></code>\".");
}
if (typeMask & android::ResTable_map::TYPE_FLOAT) {
@@ -146,33 +136,33 @@
if (typeMask & android::ResTable_map::TYPE_DIMENSION) {
processor->appendComment(
"<p>May be a dimension value, which is a floating point number appended with a\n"
- "unit such as \"<code>14.5sp</code>\".\n"
- "Available units are: px (pixels), dp (density-independent pixels),\n"
- "sp (scaled pixels based on preferred font size), in (inches), and\n"
- "mm (millimeters).");
+ "unit such as \"<code>14.5sp</code>\".\n"
+ "Available units are: px (pixels), dp (density-independent pixels),\n"
+ "sp (scaled pixels based on preferred font size), in (inches), and\n"
+ "mm (millimeters).");
}
if (typeMask & android::ResTable_map::TYPE_FRACTION) {
processor->appendComment(
"<p>May be a fractional value, which is a floating point number appended with\n"
- "either % or %p, such as \"<code>14.5%</code>\".\n"
- "The % suffix always means a percentage of the base size;\n"
- "the optional %p suffix provides a size relative to some parent container.");
+ "either % or %p, such as \"<code>14.5%</code>\".\n"
+ "The % suffix always means a percentage of the base size;\n"
+ "the optional %p suffix provides a size relative to some parent container.");
}
if (typeMask & (android::ResTable_map::TYPE_FLAGS | android::ResTable_map::TYPE_ENUM)) {
if (typeMask & android::ResTable_map::TYPE_FLAGS) {
processor->appendComment(
"<p>Must be one or more (separated by '|') of the following "
- "constant values.</p>");
+ "constant values.</p>");
} else {
processor->appendComment("<p>Must be one of the following constant values.</p>");
}
processor->appendComment("<table>\n<colgroup align=\"left\" />\n"
- "<colgroup align=\"left\" />\n"
- "<colgroup align=\"left\" />\n"
- "<tr><th>Constant</th><th>Value</th><th>Description</th></tr>\n");
+ "<colgroup align=\"left\" />\n"
+ "<colgroup align=\"left\" />\n"
+ "<tr><th>Constant</th><th>Value</th><th>Description</th></tr>\n");
for (const Attribute::Symbol& symbol : attr->symbols) {
std::stringstream line;
line << "<tr><td>" << symbol.symbol.name.value().entry << "</td>"
@@ -214,13 +204,15 @@
}
}
-void JavaClassGenerator::writeStyleableEntryForClass(ClassDefinitionWriter* outClassDef,
- AnnotationProcessor* processor,
- const StringPiece16& packageNameToGenerate,
- const std::u16string& entryName,
- const Styleable* styleable) {
+void JavaClassGenerator::addMembersToStyleableClass(const StringPiece16& packageNameToGenerate,
+ const std::u16string& entryName,
+ const Styleable* styleable,
+ ClassDefinition* outStyleableClassDef) {
const std::string className = transform(entryName);
+ std::unique_ptr<ResourceArrayMember> styleableArrayDef =
+ util::make_unique<ResourceArrayMember>(className);
+
// This must be sorted by resource ID.
std::vector<StyleableAttr> sortedAttributes;
sortedAttributes.reserve(styleable->entries.size());
@@ -230,6 +222,8 @@
assert((!mOptions.useFinal || attr.id) && "no ID set for Styleable entry");
assert(attr.name && "no name set for Styleable entry");
+ // We will need the unmangled, transformed name in the comments and the field,
+ // so create it once and cache it in this StyleableAttr data structure.
StyleableAttr styleableAttr = {};
styleableAttr.attrRef = &attr;
styleableAttr.fieldName = transformNestedAttr(attr.name.value(), className,
@@ -247,6 +241,8 @@
mangledReference.name = mangledName;
}
+ // Look up the symbol so that we can write out in the comments what are possible
+ // legal values for this attribute.
const SymbolTable::Symbol* symbol = mContext->getExternalSymbols()->findByReference(
mangledReference);
if (symbol) {
@@ -254,10 +250,11 @@
}
sortedAttributes.push_back(std::move(styleableAttr));
}
+
+ // Sort the attributes by ID.
std::sort(sortedAttributes.begin(), sortedAttributes.end(), lessStyleableAttr);
const size_t attrCount = sortedAttributes.size();
-
if (attrCount > 0) {
// Build the comment string for the Styleable. It includes details about the
// child attributes.
@@ -267,6 +264,7 @@
} else {
styleableComment << "Attributes that can be used with a " << className << ".\n";
}
+
styleableComment <<
"<p>Includes the following attributes:</p>\n"
"<table>\n"
@@ -274,7 +272,7 @@
"<colgroup align=\"left\" />\n"
"<tr><th>Attribute</th><th>Description</th></tr>\n";
- for (const auto& entry : sortedAttributes) {
+ for (const StyleableAttr& entry : sortedAttributes) {
const ResourceName& attrName = entry.attrRef->name.value();
styleableComment << "<tr><td>";
styleableComment << "<code>{@link #"
@@ -292,21 +290,22 @@
styleableComment << "</td></tr>\n";
}
styleableComment << "</table>\n";
- for (const auto& entry : sortedAttributes) {
+
+ for (const StyleableAttr& entry : sortedAttributes) {
styleableComment << "@see #" << entry.fieldName << "\n";
}
- processor->appendComment(styleableComment.str());
+
+ styleableArrayDef->getCommentBuilder()->appendComment(styleableComment.str());
}
- auto accessorFunc = [](const StyleableAttr& a) -> ResourceId {
- return a.attrRef->id ? a.attrRef->id.value() : ResourceId(0);
- };
+ // Add the ResourceIds to the array member.
+ for (const StyleableAttr& styleableAttr : sortedAttributes) {
+ styleableArrayDef->addElement(
+ styleableAttr.attrRef->id ? styleableAttr.attrRef->id.value() : ResourceId(0));
+ }
- // First we emit the array containing the IDs of each attribute.
- outClassDef->addArrayMember(className, processor,
- sortedAttributes.begin(),
- sortedAttributes.end(),
- accessorFunc);
+ // Add the Styleable array to the Styleable class.
+ outStyleableClassDef->addMember(std::move(styleableArrayDef));
// Now we emit the indices into the array.
for (size_t i = 0; i < attrCount; i++) {
@@ -318,7 +317,10 @@
packageName = mContext->getCompilationPackage();
}
- AnnotationProcessor attrProcessor;
+ std::unique_ptr<IntMember> indexMember = util::make_unique<IntMember>(
+ sortedAttributes[i].fieldName, i);
+
+ AnnotationProcessor* attrProcessor = indexMember->getCommentBuilder();
StringPiece16 comment = styleableAttr.attrRef->getComment();
if (styleableAttr.attribute && comment.empty()) {
@@ -326,8 +328,8 @@
}
if (!comment.empty()) {
- attrProcessor.appendComment("<p>\n@attr description");
- attrProcessor.appendComment(comment);
+ attrProcessor->appendComment("<p>\n@attr description");
+ attrProcessor->appendComment(comment);
} else {
std::stringstream defaultComment;
defaultComment
@@ -335,27 +337,29 @@
<< "{@link " << packageName << ".R.attr#" << transform(attrName.entry) << "}\n"
<< "attribute's value can be found in the "
<< "{@link #" << className << "} array.";
- attrProcessor.appendComment(defaultComment.str());
+ attrProcessor->appendComment(defaultComment.str());
}
- attrProcessor.appendNewLine();
+ attrProcessor->appendNewLine();
if (styleableAttr.attribute) {
- addAttributeFormatDoc(&attrProcessor, styleableAttr.attribute.get());
- attrProcessor.appendNewLine();
+ addAttributeFormatDoc(attrProcessor, styleableAttr.attribute.get());
+ attrProcessor->appendNewLine();
}
std::stringstream doclavaName;
doclavaName << "@attr name " << packageName << ":" << attrName.entry;;
- attrProcessor.appendComment(doclavaName.str());
- outClassDef->addIntMember(sortedAttributes[i].fieldName, &attrProcessor, i);
+ attrProcessor->appendComment(doclavaName.str());
+
+ outStyleableClassDef->addMember(std::move(indexMember));
}
}
-bool JavaClassGenerator::writeEntriesForClass(ClassDefinitionWriter* outClassDef,
- const StringPiece16& packageNameToGenerate,
- const ResourceTablePackage* package,
- const ResourceTableType* type) {
+bool JavaClassGenerator::addMembersToTypeClass(const StringPiece16& packageNameToGenerate,
+ const ResourceTablePackage* package,
+ const ResourceTableType* type,
+ ClassDefinition* outTypeClassDef) {
+
for (const auto& entry : type->entries) {
if (skipSymbol(entry->symbolStatus.state)) {
continue;
@@ -389,33 +393,41 @@
return false;
}
- // Build the comments and annotations for this entry.
-
- AnnotationProcessor processor;
- if (entry->symbolStatus.state != SymbolState::kUndefined) {
- processor.appendComment(entry->symbolStatus.comment);
- }
-
- for (const auto& configValue : entry->values) {
- processor.appendComment(configValue->value->getComment());
- }
-
- // If this is an Attribute, append the format Javadoc.
- if (!entry->values.empty()) {
- if (Attribute* attr = valueCast<Attribute>(entry->values.front()->value.get())) {
- // We list out the available values for the given attribute.
- addAttributeFormatDoc(&processor, attr);
- }
- }
-
if (type->type == ResourceType::kStyleable) {
assert(!entry->values.empty());
+
const Styleable* styleable = static_cast<const Styleable*>(
entry->values.front()->value.get());
- writeStyleableEntryForClass(outClassDef, &processor, packageNameToGenerate,
- unmangledName, styleable);
+
+ // Comments are handled within this method.
+ addMembersToStyleableClass(packageNameToGenerate, unmangledName, styleable,
+ outTypeClassDef);
} else {
- outClassDef->addResourceMember(transform(unmangledName), &processor, id);
+ std::unique_ptr<ResourceMember> resourceMember =
+ util::make_unique<ResourceMember>(transform(unmangledName), id);
+
+ // Build the comments and annotations for this entry.
+ AnnotationProcessor* processor = resourceMember->getCommentBuilder();
+
+ // Add the comments from any <public> tags.
+ if (entry->symbolStatus.state != SymbolState::kUndefined) {
+ processor->appendComment(entry->symbolStatus.comment);
+ }
+
+ // Add the comments from all configurations of this entry.
+ for (const auto& configValue : entry->values) {
+ processor->appendComment(configValue->value->getComment());
+ }
+
+ // If this is an Attribute, append the format Javadoc.
+ if (!entry->values.empty()) {
+ if (Attribute* attr = valueCast<Attribute>(entry->values.front()->value.get())) {
+ // We list out the available values for the given attribute.
+ addAttributeFormatDoc(processor, attr);
+ }
+ }
+
+ outTypeClassDef->addMember(std::move(resourceMember));
}
}
return true;
@@ -425,11 +437,19 @@
return generate(packageNameToGenerate, packageNameToGenerate, out);
}
+static void appendJavaDocAnnotations(const std::vector<std::string>& annotations,
+ AnnotationProcessor* processor) {
+ for (const std::string& annotation : annotations) {
+ std::string properAnnotation = "@";
+ properAnnotation += annotation;
+ processor->appendComment(properAnnotation);
+ }
+}
+
bool JavaClassGenerator::generate(const StringPiece16& packageNameToGenerate,
const StringPiece16& outPackageName, std::ostream* out) {
- generateHeader(outPackageName, out);
- *out << "public final class R {\n";
+ ClassDefinition rClass("R", ClassQualifier::None, true);
for (const auto& package : mTable->packages) {
for (const auto& type : package->types) {
@@ -437,13 +457,15 @@
continue;
}
- ClassDefinitionWriterOptions classOptions;
- classOptions.useFinalQualifier = mOptions.useFinal;
- classOptions.forceCreationIfEmpty =
+ const bool forceCreationIfEmpty =
(mOptions.types == JavaClassGeneratorOptions::SymbolTypes::kPublic);
- ClassDefinitionWriter classDef(toString(type->type), classOptions);
- bool result = writeEntriesForClass(&classDef, packageNameToGenerate,
- package.get(), type.get());
+
+ std::unique_ptr<ClassDefinition> classDef = util::make_unique<ClassDefinition>(
+ util::utf16ToUtf8(toString(type->type)), ClassQualifier::Static,
+ forceCreationIfEmpty);
+
+ bool result = addMembersToTypeClass(packageNameToGenerate, package.get(), type.get(),
+ classDef.get());
if (!result) {
return false;
}
@@ -452,30 +474,36 @@
// Also include private attributes in this same class.
ResourceTableType* privType = package->findType(ResourceType::kAttrPrivate);
if (privType) {
- result = writeEntriesForClass(&classDef, packageNameToGenerate,
- package.get(), privType);
+ result = addMembersToTypeClass(packageNameToGenerate, package.get(), privType,
+ classDef.get());
if (!result) {
return false;
}
}
}
- AnnotationProcessor processor;
if (type->type == ResourceType::kStyleable &&
mOptions.types == JavaClassGeneratorOptions::SymbolTypes::kPublic) {
// When generating a public R class, we don't want Styleable to be part of the API.
// It is only emitted for documentation purposes.
- processor.appendComment("@doconly");
+ classDef->getCommentBuilder()->appendComment("@doconly");
}
- classDef.writeToStream(out, " ", &processor);
+
+ appendJavaDocAnnotations(mOptions.javadocAnnotations, classDef->getCommentBuilder());
+
+ rClass.addMember(std::move(classDef));
}
}
- *out << "}\n";
+ appendJavaDocAnnotations(mOptions.javadocAnnotations, rClass.getCommentBuilder());
+
+ if (!ClassDefinition::writeJavaFile(&rClass, util::utf16ToUtf8(outPackageName),
+ mOptions.useFinal, out)) {
+ return false;
+ }
+
out->flush();
return true;
}
-
-
} // namespace aapt
diff --git a/tools/aapt2/java/JavaClassGenerator.h b/tools/aapt2/java/JavaClassGenerator.h
index 7e46f8c..77e0ed7 100644
--- a/tools/aapt2/java/JavaClassGenerator.h
+++ b/tools/aapt2/java/JavaClassGenerator.h
@@ -28,7 +28,7 @@
namespace aapt {
class AnnotationProcessor;
-class ClassDefinitionWriter;
+class ClassDefinition;
struct JavaClassGeneratorOptions {
/*
@@ -44,6 +44,11 @@
};
SymbolTypes types = SymbolTypes::kAll;
+
+ /**
+ * A list of JavaDoc annotations to add to the comments of all generated classes.
+ */
+ std::vector<std::string> javadocAnnotations;
};
/*
@@ -70,16 +75,15 @@
const std::string& getError() const;
private:
- bool writeEntriesForClass(ClassDefinitionWriter* outClassDef,
- const StringPiece16& packageNameToGenerate,
- const ResourceTablePackage* package,
- const ResourceTableType* type);
+ bool addMembersToTypeClass(const StringPiece16& packageNameToGenerate,
+ const ResourceTablePackage* package,
+ const ResourceTableType* type,
+ ClassDefinition* outTypeClassDef);
- void writeStyleableEntryForClass(ClassDefinitionWriter* outClassDef,
- AnnotationProcessor* processor,
- const StringPiece16& packageNameToGenerate,
- const std::u16string& entryName,
- const Styleable* styleable);
+ void addMembersToStyleableClass(const StringPiece16& packageNameToGenerate,
+ const std::u16string& entryName,
+ const Styleable* styleable,
+ ClassDefinition* outStyleableClassDef);
bool skipSymbol(SymbolState state);
diff --git a/tools/aapt2/java/ManifestClassGenerator.cpp b/tools/aapt2/java/ManifestClassGenerator.cpp
index a9b4c14..be8955e 100644
--- a/tools/aapt2/java/ManifestClassGenerator.cpp
+++ b/tools/aapt2/java/ManifestClassGenerator.cpp
@@ -16,7 +16,7 @@
#include "Source.h"
#include "java/AnnotationProcessor.h"
-#include "java/ClassDefinitionWriter.h"
+#include "java/ClassDefinition.h"
#include "java/ManifestClassGenerator.h"
#include "util/Maybe.h"
#include "xml/XmlDom.h"
@@ -58,8 +58,8 @@
return result;
}
-static bool writeSymbol(IDiagnostics* diag, ClassDefinitionWriter* outClassDef, const Source& source,
- xml::Element* el) {
+static bool writeSymbol(const Source& source, IDiagnostics* diag, xml::Element* el,
+ ClassDefinition* classDef) {
xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, u"name");
if (!attr) {
diag->error(DiagMessage(source) << "<" << el->name << "> must define 'android:name'");
@@ -72,54 +72,53 @@
return false;
}
- AnnotationProcessor processor;
- processor.appendComment(el->comment);
- outClassDef->addStringMember(result.value(), &processor, attr->value);
+ std::unique_ptr<StringMember> stringMember = util::make_unique<StringMember>(
+ util::utf16ToUtf8(result.value()), util::utf16ToUtf8(attr->value));
+ stringMember->getCommentBuilder()->appendComment(el->comment);
+
+ classDef->addMember(std::move(stringMember));
return true;
}
-bool ManifestClassGenerator::generate(IDiagnostics* diag, const StringPiece16& package,
- xml::XmlResource* res, std::ostream* out) {
+std::unique_ptr<ClassDefinition> generateManifestClass(IDiagnostics* diag, xml::XmlResource* res) {
xml::Element* el = xml::findRootElement(res->root.get());
if (!el) {
- return false;
+ diag->error(DiagMessage(res->file.source) << "no root tag defined");
+ return {};
}
if (el->name != u"manifest" && !el->namespaceUri.empty()) {
diag->error(DiagMessage(res->file.source) << "no <manifest> root tag defined");
- return false;
+ return {};
}
- *out << "package " << package << ";\n\n"
- << "public final class Manifest {\n";
+ std::unique_ptr<ClassDefinition> permissionClass =
+ util::make_unique<ClassDefinition>("permission", ClassQualifier::Static, false);
+ std::unique_ptr<ClassDefinition> permissionGroupClass =
+ util::make_unique<ClassDefinition>("permission_group", ClassQualifier::Static, false);
bool error = false;
+
std::vector<xml::Element*> children = el->getChildElements();
-
- ClassDefinitionWriterOptions classOptions;
- classOptions.useFinalQualifier = true;
- classOptions.forceCreationIfEmpty = false;
-
- // First write out permissions.
- ClassDefinitionWriter classDef("permission", classOptions);
for (xml::Element* childEl : children) {
- if (childEl->namespaceUri.empty() && childEl->name == u"permission") {
- error |= !writeSymbol(diag, &classDef, res->file.source, childEl);
+ if (childEl->namespaceUri.empty()) {
+ if (childEl->name == u"permission") {
+ error |= !writeSymbol(res->file.source, diag, childEl, permissionClass.get());
+ } else if (childEl->name == u"permission-group") {
+ error |= !writeSymbol(res->file.source, diag, childEl, permissionGroupClass.get());
+ }
}
}
- classDef.writeToStream(out, " ");
- // Next write out permission groups.
- classDef = ClassDefinitionWriter("permission_group", classOptions);
- for (xml::Element* childEl : children) {
- if (childEl->namespaceUri.empty() && childEl->name == u"permission-group") {
- error |= !writeSymbol(diag, &classDef, res->file.source, childEl);
- }
+ if (error) {
+ return {};
}
- classDef.writeToStream(out, " ");
- *out << "}\n";
- return !error;
+ std::unique_ptr<ClassDefinition> manifestClass =
+ util::make_unique<ClassDefinition>("Manifest", ClassQualifier::None, false);
+ manifestClass->addMember(std::move(permissionClass));
+ manifestClass->addMember(std::move(permissionGroupClass));
+ return manifestClass;
}
} // namespace aapt
diff --git a/tools/aapt2/java/ManifestClassGenerator.h b/tools/aapt2/java/ManifestClassGenerator.h
index 226ed23..f565289 100644
--- a/tools/aapt2/java/ManifestClassGenerator.h
+++ b/tools/aapt2/java/ManifestClassGenerator.h
@@ -18,6 +18,7 @@
#define AAPT_JAVA_MANIFESTCLASSGENERATOR_H
#include "Diagnostics.h"
+#include "java/ClassDefinition.h"
#include "util/StringPiece.h"
#include "xml/XmlDom.h"
@@ -25,10 +26,7 @@
namespace aapt {
-struct ManifestClassGenerator {
- bool generate(IDiagnostics* diag, const StringPiece16& package, xml::XmlResource* res,
- std::ostream* out);
-};
+std::unique_ptr<ClassDefinition> generateManifestClass(IDiagnostics* diag, xml::XmlResource* res);
} // namespace aapt
diff --git a/tools/aapt2/java/ManifestClassGenerator_test.cpp b/tools/aapt2/java/ManifestClassGenerator_test.cpp
index fc57ae6f..a9ec318 100644
--- a/tools/aapt2/java/ManifestClassGenerator_test.cpp
+++ b/tools/aapt2/java/ManifestClassGenerator_test.cpp
@@ -22,6 +22,23 @@
namespace aapt {
+static ::testing::AssertionResult getManifestClassText(IAaptContext* context, xml::XmlResource* res,
+ std::string* outStr) {
+ std::unique_ptr<ClassDefinition> manifestClass = generateManifestClass(
+ context->getDiagnostics(), res);
+ if (!manifestClass) {
+ return ::testing::AssertionFailure() << "manifestClass == nullptr";
+ }
+
+ std::stringstream out;
+ if (!manifestClass->writeJavaFile(manifestClass.get(), "android", true, &out)) {
+ return ::testing::AssertionFailure() << "failed to write java file";
+ }
+
+ *outStr = out.str();
+ return ::testing::AssertionSuccess();
+}
+
TEST(ManifestClassGeneratorTest, NameIsProperlyGeneratedFromSymbol) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
std::unique_ptr<xml::XmlResource> manifest = test::buildXmlDom(R"EOF(
@@ -32,11 +49,8 @@
<permission-group android:name="foo.bar.PERMISSION" />
</manifest>)EOF");
- std::stringstream out;
- ManifestClassGenerator generator;
- ASSERT_TRUE(generator.generate(context->getDiagnostics(), u"android", manifest.get(), &out));
-
- std::string actual = out.str();
+ std::string actual;
+ ASSERT_TRUE(getManifestClassText(context.get(), manifest.get(), &actual));
const size_t permissionClassPos = actual.find("public static final class permission {");
const size_t permissionGroupClassPos =
@@ -87,11 +101,8 @@
<permission android:name="android.permission.SECRET" />
</manifest>)EOF");
- std::stringstream out;
- ManifestClassGenerator generator;
- ASSERT_TRUE(generator.generate(context->getDiagnostics(), u"android", manifest.get(), &out));
-
- std::string actual = out.str();
+ std::string actual;
+ ASSERT_TRUE(getManifestClassText(context.get(), manifest.get(), &actual));
EXPECT_NE(std::string::npos, actual.find(
R"EOF( /**
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index b84074d..8c8bffa 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -66,6 +66,7 @@
bool staticLib = false;
bool noStaticLibPackages = false;
bool generateNonFinalIds = false;
+ std::vector<std::string> javadocAnnotations;
bool outputToDirectory = false;
bool autoAddOverlay = false;
bool doNotCompressAnything = false;
@@ -762,9 +763,31 @@
return true;
}
+ std::unique_ptr<ClassDefinition> manifestClass = generateManifestClass(
+ mContext->getDiagnostics(), manifestXml);
+
+ if (!manifestClass) {
+ // Something bad happened, but we already logged it, so exit.
+ return false;
+ }
+
+ if (manifestClass->empty()) {
+ // Empty Manifest class, no need to generate it.
+ return true;
+ }
+
+ // Add any JavaDoc annotations to the generated class.
+ for (const std::string& annotation : mOptions.javadocAnnotations) {
+ std::string properAnnotation = "@";
+ properAnnotation += annotation;
+ manifestClass->getCommentBuilder()->appendComment(properAnnotation);
+ }
+
+ const std::string packageUtf8 = util::utf16ToUtf8(mContext->getCompilationPackage());
+
std::string outPath = mOptions.generateJavaClassPath.value();
- file::appendPath(&outPath,
- file::packageToPath(util::utf16ToUtf8(mContext->getCompilationPackage())));
+ file::appendPath(&outPath, file::packageToPath(packageUtf8));
+
if (!file::mkdirs(outPath)) {
mContext->getDiagnostics()->error(
DiagMessage() << "failed to create directory '" << outPath << "'");
@@ -780,13 +803,7 @@
return false;
}
- ManifestClassGenerator generator;
- if (!generator.generate(mContext->getDiagnostics(), mContext->getCompilationPackage(),
- manifestXml, &fout)) {
- return false;
- }
-
- if (!fout) {
+ if (!ClassDefinition::writeJavaFile(manifestClass.get(), packageUtf8, true, &fout)) {
mContext->getDiagnostics()->error(
DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
return false;
@@ -1283,6 +1300,7 @@
if (mOptions.generateJavaClassPath) {
JavaClassGeneratorOptions options;
options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
+ options.javadocAnnotations = mOptions.javadocAnnotations;
if (mOptions.staticLib || mOptions.generateNonFinalIds) {
options.useFinal = false;
@@ -1423,6 +1441,8 @@
&customJavaPackage)
.optionalFlagList("--extra-packages", "Generate the same R.java but with different "
"package names", &extraJavaPackages)
+ .optionalFlagList("--add-javadoc-annotation", "Adds a JavaDoc annotation to all "
+ "generated Java classes", &options.javadocAnnotations)
.optionalSwitch("--auto-add-overlay", "Allows the addition of new resources in "
"overlays without <add-resource> tags", &options.autoAddOverlay)
.optionalFlag("--rename-manifest-package", "Renames the package in AndroidManifest.xml",
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 9baf1d8..3779638 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -17,66 +17,199 @@
#include "ResourceUtils.h"
#include "link/ManifestFixer.h"
#include "util/Util.h"
+#include "xml/XmlActionExecutor.h"
#include "xml/XmlDom.h"
namespace aapt {
-static bool verifyManifest(IAaptContext* context, const Source& source, xml::Element* manifestEl) {
- xml::Attribute* attr = manifestEl->findAttribute({}, u"package");
- if (!attr) {
- context->getDiagnostics()->error(DiagMessage(source.withLine(manifestEl->lineNumber))
- << "missing 'package' attribute");
- } else if (ResourceUtils::isReference(attr->value)) {
- context->getDiagnostics()->error(DiagMessage(source.withLine(manifestEl->lineNumber))
- << "value for attribute 'package' must not be a "
- "reference");
- } else if (!util::isJavaPackageName(attr->value)) {
- context->getDiagnostics()->error(DiagMessage(source.withLine(manifestEl->lineNumber))
- << "invalid package name '" << attr->value << "'");
- } else {
- return true;
+/**
+ * This is how PackageManager builds class names from AndroidManifest.xml entries.
+ */
+static bool nameIsJavaClassName(xml::Element* el, xml::Attribute* attr,
+ SourcePathDiagnostics* diag) {
+ std::u16string className = attr->value;
+ if (className.find(u'.') == std::u16string::npos) {
+ // There is no '.', so add one to the beginning.
+ className = u".";
+ className += attr->value;
}
+
+ // We allow unqualified class names (ie: .HelloActivity)
+ // Since we don't know the package name, we can just make a fake one here and
+ // the test will be identical as long as the real package name is valid too.
+ Maybe<std::u16string> fullyQualifiedClassName =
+ util::getFullyQualifiedClassName(u"a", className);
+
+ StringPiece16 qualifiedClassName = fullyQualifiedClassName
+ ? fullyQualifiedClassName.value() : className;
+ if (!util::isJavaClassName(qualifiedClassName)) {
+ diag->error(DiagMessage(el->lineNumber)
+ << "attribute 'android:name' in <"
+ << el->name << "> tag must be a valid Java class name");
+ return false;
+ }
+ return true;
+}
+
+static bool optionalNameIsJavaClassName(xml::Element* el, SourcePathDiagnostics* diag) {
+ if (xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, u"name")) {
+ return nameIsJavaClassName(el, attr, diag);
+ }
+ return true;
+}
+
+static bool requiredNameIsJavaClassName(xml::Element* el, SourcePathDiagnostics* diag) {
+ if (xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, u"name")) {
+ return nameIsJavaClassName(el, attr, diag);
+ }
+ diag->error(DiagMessage(el->lineNumber)
+ << "<" << el->name << "> is missing attribute 'android:name'");
return false;
}
-static bool includeVersionName(IAaptContext* context, const Source& source,
- const StringPiece16& versionName, xml::Element* manifestEl) {
- if (manifestEl->findAttribute(xml::kSchemaAndroid, u"versionName")) {
- return true;
+static bool verifyManifest(xml::Element* el, SourcePathDiagnostics* diag) {
+ xml::Attribute* attr = el->findAttribute({}, u"package");
+ if (!attr) {
+ diag->error(DiagMessage(el->lineNumber) << "<manifest> tag is missing 'package' attribute");
+ return false;
+ } else if (ResourceUtils::isReference(attr->value)) {
+ diag->error(DiagMessage(el->lineNumber)
+ << "attribute 'package' in <manifest> tag must not be a reference");
+ return false;
+ } else if (!util::isJavaPackageName(attr->value)) {
+ diag->error(DiagMessage(el->lineNumber)
+ << "attribute 'package' in <manifest> tag is not a valid Java package name: '"
+ << attr->value << "'");
+ return false;
}
-
- manifestEl->attributes.push_back(xml::Attribute{
- xml::kSchemaAndroid, u"versionName", versionName.toString() });
return true;
}
-static bool includeVersionCode(IAaptContext* context, const Source& source,
- const StringPiece16& versionCode, xml::Element* manifestEl) {
- if (manifestEl->findAttribute(xml::kSchemaAndroid, u"versionCode")) {
+bool ManifestFixer::buildRules(xml::XmlActionExecutor* executor, IDiagnostics* diag) {
+ // First verify some options.
+ if (mOptions.renameManifestPackage) {
+ if (!util::isJavaPackageName(mOptions.renameManifestPackage.value())) {
+ diag->error(DiagMessage() << "invalid manifest package override '"
+ << mOptions.renameManifestPackage.value() << "'");
+ return false;
+ }
+ }
+
+ if (mOptions.renameInstrumentationTargetPackage) {
+ if (!util::isJavaPackageName(mOptions.renameInstrumentationTargetPackage.value())) {
+ diag->error(DiagMessage() << "invalid instrumentation target package override '"
+ << mOptions.renameInstrumentationTargetPackage.value() << "'");
+ return false;
+ }
+ }
+
+ // Common intent-filter actions.
+ xml::XmlNodeAction intentFilterAction;
+ intentFilterAction[u"action"];
+ intentFilterAction[u"category"];
+ intentFilterAction[u"data"];
+
+ // Common meta-data actions.
+ xml::XmlNodeAction metaDataAction;
+
+ // Manifest actions.
+ xml::XmlNodeAction& manifestAction = (*executor)[u"manifest"];
+ manifestAction.action(verifyManifest);
+ manifestAction.action([&](xml::Element* el) -> bool {
+ if (mOptions.versionNameDefault) {
+ if (el->findAttribute(xml::kSchemaAndroid, u"versionName") == nullptr) {
+ el->attributes.push_back(xml::Attribute{
+ xml::kSchemaAndroid,
+ u"versionName",
+ mOptions.versionNameDefault.value() });
+ }
+ }
+
+ if (mOptions.versionCodeDefault) {
+ if (el->findAttribute(xml::kSchemaAndroid, u"versionCode") == nullptr) {
+ el->attributes.push_back(xml::Attribute{
+ xml::kSchemaAndroid,
+ u"versionCode",
+ mOptions.versionCodeDefault.value() });
+ }
+ }
return true;
- }
+ });
- manifestEl->attributes.push_back(xml::Attribute{
- xml::kSchemaAndroid, u"versionCode", versionCode.toString() });
- return true;
-}
+ // Uses-sdk actions.
+ manifestAction[u"uses-sdk"].action([&](xml::Element* el) -> bool {
+ if (mOptions.minSdkVersionDefault &&
+ el->findAttribute(xml::kSchemaAndroid, u"minSdkVersion") == nullptr) {
+ // There was no minSdkVersion defined and we have a default to assign.
+ el->attributes.push_back(xml::Attribute{
+ xml::kSchemaAndroid, u"minSdkVersion",
+ mOptions.minSdkVersionDefault.value() });
+ }
-static bool fixUsesSdk(IAaptContext* context, const Source& source, xml::Element* el,
- const ManifestFixerOptions& options) {
- if (options.minSdkVersionDefault &&
- el->findAttribute(xml::kSchemaAndroid, u"minSdkVersion") == nullptr) {
- // There was no minSdkVersion defined and we have a default to assign.
- el->attributes.push_back(xml::Attribute{
- xml::kSchemaAndroid, u"minSdkVersion", options.minSdkVersionDefault.value() });
- }
+ if (mOptions.targetSdkVersionDefault &&
+ el->findAttribute(xml::kSchemaAndroid, u"targetSdkVersion") == nullptr) {
+ // There was no targetSdkVersion defined and we have a default to assign.
+ el->attributes.push_back(xml::Attribute{
+ xml::kSchemaAndroid, u"targetSdkVersion",
+ mOptions.targetSdkVersionDefault.value() });
+ }
+ return true;
+ });
- if (options.targetSdkVersionDefault &&
- el->findAttribute(xml::kSchemaAndroid, u"targetSdkVersion") == nullptr) {
- // There was no targetSdkVersion defined and we have a default to assign.
- el->attributes.push_back(xml::Attribute{
- xml::kSchemaAndroid, u"targetSdkVersion",
- options.targetSdkVersionDefault.value() });
- }
+ // Instrumentation actions.
+ manifestAction[u"instrumentation"].action([&](xml::Element* el) -> bool {
+ if (!mOptions.renameInstrumentationTargetPackage) {
+ return true;
+ }
+
+ if (xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, u"targetPackage")) {
+ attr->value = mOptions.renameInstrumentationTargetPackage.value();
+ }
+ return true;
+ });
+
+ manifestAction[u"eat-comment"];
+ manifestAction[u"protected-broadcast"];
+ manifestAction[u"uses-permission"];
+ manifestAction[u"permission"];
+ manifestAction[u"permission-tree"];
+ manifestAction[u"permission-group"];
+
+ manifestAction[u"uses-configuration"];
+ manifestAction[u"uses-feature"];
+ manifestAction[u"uses-library"];
+ manifestAction[u"supports-screens"];
+ manifestAction[u"compatible-screens"];
+ manifestAction[u"supports-gl-texture"];
+
+ // Application actions.
+ xml::XmlNodeAction& applicationAction = (*executor)[u"manifest"][u"application"];
+ applicationAction.action(optionalNameIsJavaClassName);
+
+ // Activity actions.
+ applicationAction[u"activity"].action(requiredNameIsJavaClassName);
+ applicationAction[u"activity"][u"intent-filter"] = intentFilterAction;
+ applicationAction[u"activity"][u"meta-data"] = metaDataAction;
+
+ // Activity alias actions.
+ applicationAction[u"activity-alias"][u"intent-filter"] = intentFilterAction;
+ applicationAction[u"activity-alias"][u"meta-data"] = metaDataAction;
+
+ // Service actions.
+ applicationAction[u"service"].action(requiredNameIsJavaClassName);
+ applicationAction[u"service"][u"intent-filter"] = intentFilterAction;
+ applicationAction[u"service"][u"meta-data"] = metaDataAction;
+
+ // Receiver actions.
+ applicationAction[u"receiver"].action(requiredNameIsJavaClassName);
+ applicationAction[u"receiver"][u"intent-filter"] = intentFilterAction;
+ applicationAction[u"receiver"][u"meta-data"] = metaDataAction;
+
+ // Provider actions.
+ applicationAction[u"provider"].action(requiredNameIsJavaClassName);
+ applicationAction[u"provider"][u"grant-uri-permissions"];
+ applicationAction[u"provider"][u"meta-data"] = metaDataAction;
+ applicationAction[u"provider"][u"path-permissions"];
return true;
}
@@ -103,14 +236,7 @@
StringPiece16 mPackage;
};
-static bool renameManifestPackage(IAaptContext* context, const Source& source,
- const StringPiece16& packageOverride, xml::Element* manifestEl) {
- if (!util::isJavaPackageName(packageOverride)) {
- context->getDiagnostics()->error(DiagMessage() << "invalid manifest package override '"
- << packageOverride << "'");
- return false;
- }
-
+static bool renameManifestPackage(const StringPiece16& packageOverride, xml::Element* manifestEl) {
xml::Attribute* attr = manifestEl->findAttribute({}, u"package");
// We've already verified that the manifest element is present, with a package name specified.
@@ -124,32 +250,6 @@
return true;
}
-static bool renameInstrumentationTargetPackage(IAaptContext* context, const Source& source,
- const StringPiece16& packageOverride,
- xml::Element* manifestEl) {
- if (!util::isJavaPackageName(packageOverride)) {
- context->getDiagnostics()->error(DiagMessage()
- << "invalid instrumentation target package override '"
- << packageOverride << "'");
- return false;
- }
-
- xml::Element* instrumentationEl = manifestEl->findChild({}, u"instrumentation");
- if (!instrumentationEl) {
- // No error if there is no work to be done.
- return true;
- }
-
- xml::Attribute* attr = instrumentationEl->findAttribute(xml::kSchemaAndroid, u"targetPackage");
- if (!attr) {
- // No error if there is no work to be done.
- return true;
- }
-
- attr->value = packageOverride.toString();
- return true;
-}
-
bool ManifestFixer::consume(IAaptContext* context, xml::XmlResource* doc) {
xml::Element* root = xml::findRootElement(doc->root.get());
if (!root || !root->namespaceUri.empty() || root->name != u"manifest") {
@@ -158,59 +258,31 @@
return false;
}
- if (!verifyManifest(context, doc->file.source, root)) {
- return false;
- }
-
- if (mOptions.versionCodeDefault) {
- if (!includeVersionCode(context, doc->file.source, mOptions.versionCodeDefault.value(),
- root)) {
- return false;
- }
- }
-
- if (mOptions.versionNameDefault) {
- if (!includeVersionName(context, doc->file.source, mOptions.versionNameDefault.value(),
- root)) {
- return false;
- }
- }
-
- if (mOptions.renameManifestPackage) {
- // Rename manifest package.
- if (!renameManifestPackage(context, doc->file.source,
- mOptions.renameManifestPackage.value(), root)) {
- return false;
- }
- }
-
- if (mOptions.renameInstrumentationTargetPackage) {
- if (!renameInstrumentationTargetPackage(context, doc->file.source,
- mOptions.renameInstrumentationTargetPackage.value(),
- root)) {
- return false;
- }
- }
-
- bool foundUsesSdk = false;
- for (xml::Element* el : root->getChildElements()) {
- if (!el->namespaceUri.empty()) {
- continue;
- }
-
- if (el->name == u"uses-sdk") {
- foundUsesSdk = true;
- fixUsesSdk(context, doc->file.source, el, mOptions);
- }
- }
-
- if (!foundUsesSdk && (mOptions.minSdkVersionDefault || mOptions.targetSdkVersionDefault)) {
+ if ((mOptions.minSdkVersionDefault || mOptions.targetSdkVersionDefault)
+ && root->findChild({}, u"uses-sdk") == nullptr) {
+ // Auto insert a <uses-sdk> element.
std::unique_ptr<xml::Element> usesSdk = util::make_unique<xml::Element>();
usesSdk->name = u"uses-sdk";
- fixUsesSdk(context, doc->file.source, usesSdk.get(), mOptions);
root->addChild(std::move(usesSdk));
}
+ xml::XmlActionExecutor executor;
+ if (!buildRules(&executor, context->getDiagnostics())) {
+ return false;
+ }
+
+ if (!executor.execute(xml::XmlActionExecutorPolicy::Whitelist, context->getDiagnostics(),
+ doc)) {
+ return false;
+ }
+
+ if (mOptions.renameManifestPackage) {
+ // Rename manifest package outside of the XmlActionExecutor.
+ // We need to extract the old package name and FullyQualify all class names.
+ if (!renameManifestPackage(mOptions.renameManifestPackage.value(), root)) {
+ return false;
+ }
+ }
return true;
}
diff --git a/tools/aapt2/link/ManifestFixer.h b/tools/aapt2/link/ManifestFixer.h
index b8d9c83..4d9356a 100644
--- a/tools/aapt2/link/ManifestFixer.h
+++ b/tools/aapt2/link/ManifestFixer.h
@@ -19,6 +19,7 @@
#include "process/IResourceTableConsumer.h"
#include "util/Maybe.h"
+#include "xml/XmlActionExecutor.h"
#include "xml/XmlDom.h"
#include <string>
@@ -38,13 +39,17 @@
* Verifies that the manifest is correctly formed and inserts defaults
* where specified with ManifestFixerOptions.
*/
-struct ManifestFixer : public IXmlResourceConsumer {
- ManifestFixerOptions mOptions;
-
+class ManifestFixer : public IXmlResourceConsumer {
+public:
ManifestFixer(const ManifestFixerOptions& options) : mOptions(options) {
}
bool consume(IAaptContext* context, xml::XmlResource* doc) override;
+
+private:
+ bool buildRules(xml::XmlActionExecutor* executor, IDiagnostics* diag);
+
+ ManifestFixerOptions mOptions;
};
} // namespace aapt
diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp
index 18c47df..f993720 100644
--- a/tools/aapt2/link/ManifestFixer_test.cpp
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -166,9 +166,9 @@
std::unique_ptr<xml::XmlResource> doc = verifyWithOptions(R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android">
- <application name=".MainApplication" text="hello">
- <activity name=".activity.Start" />
- <receiver name="com.google.android.Receiver" />
+ <application android:name=".MainApplication" text="hello">
+ <activity android:name=".activity.Start" />
+ <receiver android:name="com.google.android.Receiver" />
</application>
</manifest>)EOF", options);
ASSERT_NE(nullptr, doc);
@@ -185,7 +185,7 @@
xml::Element* applicationEl = manifestEl->findChild({}, u"application");
ASSERT_NE(nullptr, applicationEl);
- attr = applicationEl->findAttribute({}, u"name");
+ attr = applicationEl->findAttribute(xml::kSchemaAndroid, u"name");
ASSERT_NE(nullptr, attr);
EXPECT_EQ(std::u16string(u"android.MainApplication"), attr->value);
@@ -197,14 +197,14 @@
el = applicationEl->findChild({}, u"activity");
ASSERT_NE(nullptr, el);
- attr = el->findAttribute({}, u"name");
+ attr = el->findAttribute(xml::kSchemaAndroid, u"name");
ASSERT_NE(nullptr, el);
EXPECT_EQ(std::u16string(u"android.activity.Start"), attr->value);
el = applicationEl->findChild({}, u"receiver");
ASSERT_NE(nullptr, el);
- attr = el->findAttribute({}, u"name");
+ attr = el->findAttribute(xml::kSchemaAndroid, u"name");
ASSERT_NE(nullptr, el);
EXPECT_EQ(std::u16string(u"com.google.android.Receiver"), attr->value);
}
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index 8c56ebc..8eb4bc8 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -238,7 +238,7 @@
std::stringstream in;
in << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" << str;
StdErrDiagnostics diag;
- std::unique_ptr<xml::XmlResource> doc = xml::inflate(&in, &diag, {});
+ std::unique_ptr<xml::XmlResource> doc = xml::inflate(&in, &diag, Source("test.xml"));
assert(doc);
return doc;
}
diff --git a/tools/aapt2/test/Common.h b/tools/aapt2/test/Common.h
index 348c32a..faccd477 100644
--- a/tools/aapt2/test/Common.h
+++ b/tools/aapt2/test/Common.h
@@ -41,15 +41,20 @@
namespace test {
struct DummyDiagnosticsImpl : public IDiagnostics {
- void error(const DiagMessage& message) override {
- DiagMessageActual actual = message.build();
- std::cerr << actual.source << ": error: " << actual.message << "." << std::endl;
+ void log(Level level, DiagMessageActual& actualMsg) override {
+ switch (level) {
+ case Level::Note:
+ return;
+
+ case Level::Warn:
+ std::cerr << actualMsg.source << ": warn: " << actualMsg.message << "." << std::endl;
+ break;
+
+ case Level::Error:
+ std::cerr << actualMsg.source << ": error: " << actualMsg.message << "." << std::endl;
+ break;
+ }
}
- void warn(const DiagMessage& message) override {
- DiagMessageActual actual = message.build();
- std::cerr << actual.source << ": warn: " << actual.message << "." << std::endl;
- }
- void note(const DiagMessage& message) override {}
};
inline IDiagnostics* getDiagnostics() {
diff --git a/tools/aapt2/xml/XmlActionExecutor.cpp b/tools/aapt2/xml/XmlActionExecutor.cpp
new file mode 100644
index 0000000..0ef67ea
--- /dev/null
+++ b/tools/aapt2/xml/XmlActionExecutor.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "xml/XmlActionExecutor.h"
+
+namespace aapt {
+namespace xml {
+
+static bool wrapperOne(XmlNodeAction::ActionFunc& f, Element* el, SourcePathDiagnostics*) {
+ return f(el);
+}
+
+static bool wrapperTwo(XmlNodeAction::ActionFuncWithDiag& f, Element* el,
+ SourcePathDiagnostics* diag) {
+ return f(el, diag);
+}
+
+void XmlNodeAction::action(XmlNodeAction::ActionFunc f) {
+ mActions.emplace_back(std::bind(wrapperOne, std::move(f),
+ std::placeholders::_1,
+ std::placeholders::_2));
+}
+
+void XmlNodeAction::action(XmlNodeAction::ActionFuncWithDiag f) {
+ mActions.emplace_back(std::bind(wrapperTwo, std::move(f),
+ std::placeholders::_1,
+ std::placeholders::_2));
+}
+
+static void printElementToDiagMessage(const Element* el, DiagMessage* msg) {
+ *msg << "<";
+ if (!el->namespaceUri.empty()) {
+ *msg << el->namespaceUri << ":";
+ }
+ *msg << el->name << ">";
+}
+
+bool XmlNodeAction::execute(XmlActionExecutorPolicy policy, SourcePathDiagnostics* diag,
+ Element* el) const {
+ bool error = false;
+ for (const ActionFuncWithDiag& action : mActions) {
+ error |= !action(el, diag);
+ }
+
+ for (Element* childEl : el->getChildElements()) {
+ if (childEl->namespaceUri.empty()) {
+ std::map<std::u16string, XmlNodeAction>::const_iterator iter =
+ mMap.find(childEl->name);
+ if (iter != mMap.end()) {
+ error |= !iter->second.execute(policy, diag, childEl);
+ continue;
+ }
+ }
+
+ if (policy == XmlActionExecutorPolicy::Whitelist) {
+ DiagMessage errorMsg(childEl->lineNumber);
+ errorMsg << "unknown element ";
+ printElementToDiagMessage(childEl, &errorMsg);
+ errorMsg << " found";
+ diag->error(errorMsg);
+ error = true;
+ }
+ }
+ return !error;
+}
+
+bool XmlActionExecutor::execute(XmlActionExecutorPolicy policy, IDiagnostics* diag,
+ XmlResource* doc) const {
+ SourcePathDiagnostics sourceDiag(doc->file.source, diag);
+
+ Element* el = findRootElement(doc);
+ if (!el) {
+ if (policy == XmlActionExecutorPolicy::Whitelist) {
+ sourceDiag.error(DiagMessage() << "no root XML tag found");
+ return false;
+ }
+ return true;
+ }
+
+ if (el->namespaceUri.empty()) {
+ std::map<std::u16string, XmlNodeAction>::const_iterator iter = mMap.find(el->name);
+ if (iter != mMap.end()) {
+ return iter->second.execute(policy, &sourceDiag, el);
+ }
+ }
+
+ if (policy == XmlActionExecutorPolicy::Whitelist) {
+ DiagMessage errorMsg(el->lineNumber);
+ errorMsg << "unknown element ";
+ printElementToDiagMessage(el, &errorMsg);
+ errorMsg << " found";
+ sourceDiag.error(errorMsg);
+ return false;
+ }
+ return true;
+}
+
+} // namespace xml
+} // namespace aapt
diff --git a/tools/aapt2/xml/XmlActionExecutor.h b/tools/aapt2/xml/XmlActionExecutor.h
new file mode 100644
index 0000000..36b94db
--- /dev/null
+++ b/tools/aapt2/xml/XmlActionExecutor.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAPT_XML_XMLPATTERN_H
+#define AAPT_XML_XMLPATTERN_H
+
+#include "Diagnostics.h"
+#include "xml/XmlDom.h"
+
+#include <android-base/macros.h>
+#include <functional>
+#include <map>
+#include <string>
+#include <vector>
+
+namespace aapt {
+namespace xml {
+
+enum class XmlActionExecutorPolicy {
+ /**
+ * Actions on run if elements are matched, errors occur only when actions return false.
+ */
+ None,
+
+ /**
+ * The actions defined must match and run. If an element is found that does not match
+ * an action, an error occurs.
+ */
+ Whitelist,
+};
+
+/**
+ * Contains the actions to perform at this XML node. This is a recursive data structure that
+ * holds XmlNodeActions for child XML nodes.
+ */
+class XmlNodeAction {
+public:
+ using ActionFuncWithDiag = std::function<bool(Element*, SourcePathDiagnostics*)>;
+ using ActionFunc = std::function<bool(Element*)>;
+
+ /**
+ * Find or create a child XmlNodeAction that will be performed for the child element
+ * with the name `name`.
+ */
+ XmlNodeAction& operator[](const std::u16string& name) {
+ return mMap[name];
+ }
+
+ /**
+ * Add an action to be performed at this XmlNodeAction.
+ */
+ void action(ActionFunc f);
+ void action(ActionFuncWithDiag);
+
+private:
+ friend class XmlActionExecutor;
+
+ bool execute(XmlActionExecutorPolicy policy, SourcePathDiagnostics* diag, Element* el) const;
+
+ std::map<std::u16string, XmlNodeAction> mMap;
+ std::vector<ActionFuncWithDiag> mActions;
+};
+
+/**
+ * Allows the definition of actions to execute at specific XML elements defined by their
+ * hierarchy.
+ */
+class XmlActionExecutor {
+public:
+ XmlActionExecutor() = default;
+
+ /**
+ * Find or create a root XmlNodeAction that will be performed for the root XML element
+ * with the name `name`.
+ */
+ XmlNodeAction& operator[](const std::u16string& name) {
+ return mMap[name];
+ }
+
+ /**
+ * Execute the defined actions for this XmlResource.
+ * Returns true if all actions return true, otherwise returns false.
+ */
+ bool execute(XmlActionExecutorPolicy policy, IDiagnostics* diag, XmlResource* doc) const;
+
+private:
+ std::map<std::u16string, XmlNodeAction> mMap;
+
+ DISALLOW_COPY_AND_ASSIGN(XmlActionExecutor);
+};
+
+} // namespace xml
+} // namespace aapt
+
+#endif /* AAPT_XML_XMLPATTERN_H */
diff --git a/tools/aapt2/xml/XmlActionExecutor_test.cpp b/tools/aapt2/xml/XmlActionExecutor_test.cpp
new file mode 100644
index 0000000..ebf287a
--- /dev/null
+++ b/tools/aapt2/xml/XmlActionExecutor_test.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "test/Test.h"
+#include "xml/XmlActionExecutor.h"
+
+namespace aapt {
+namespace xml {
+
+TEST(XmlActionExecutorTest, BuildsAccessibleNestedPattern) {
+ XmlActionExecutor executor;
+ XmlNodeAction& manifestAction = executor[u"manifest"];
+ XmlNodeAction& applicationAction = manifestAction[u"application"];
+
+ Element* manifestEl = nullptr;
+ manifestAction.action([&](Element* manifest) -> bool {
+ manifestEl = manifest;
+ return true;
+ });
+
+ Element* applicationEl = nullptr;
+ applicationAction.action([&](Element* application) -> bool {
+ applicationEl = application;
+ return true;
+ });
+
+ std::unique_ptr<XmlResource> doc = test::buildXmlDom("<manifest><application /></manifest>");
+
+ StdErrDiagnostics diag;
+ ASSERT_TRUE(executor.execute(XmlActionExecutorPolicy::None, &diag, doc.get()));
+ ASSERT_NE(nullptr, manifestEl);
+ EXPECT_EQ(std::u16string(u"manifest"), manifestEl->name);
+
+ ASSERT_NE(nullptr, applicationEl);
+ EXPECT_EQ(std::u16string(u"application"), applicationEl->name);
+}
+
+TEST(XmlActionExecutorTest, FailsWhenUndefinedHierarchyExists) {
+ XmlActionExecutor executor;
+ executor[u"manifest"][u"application"];
+
+ std::unique_ptr<XmlResource> doc = test::buildXmlDom(
+ "<manifest><application /><activity /></manifest>");
+ StdErrDiagnostics diag;
+ ASSERT_FALSE(executor.execute(XmlActionExecutorPolicy::Whitelist, &diag, doc.get()));
+}
+
+} // namespace xml
+} // namespace aapt
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index a8a8c5c..ca2d2e7 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -980,6 +980,16 @@
warn(clazz, m, "M10", "Methods accepting File should also accept FileDescriptor or streams")
+def verify_manager_list(clazz):
+ """Verifies that managers return List<? extends Parcelable> instead of arrays."""
+
+ if not clazz.name.endswith("Manager"): return
+
+ for m in clazz.methods:
+ if m.typ.startswith("android.") and m.typ.endswith("[]"):
+ warn(clazz, m, None, "Methods should return List<? extends Parcelable> instead of Parcelable[] to support ParceledListSlice under the hood")
+
+
def examine_clazz(clazz):
"""Find all style issues in the given class."""
if clazz.pkg.name.startswith("java"): return
@@ -1025,6 +1035,7 @@
verify_listener_last(clazz)
verify_resource_names(clazz)
verify_files(clazz)
+ verify_manager_list(clazz)
def examine_stream(stream):
diff --git a/tools/fonts/fontchain_lint.py b/tools/fonts/fontchain_lint.py
index a848346..b5ed1b5 100755
--- a/tools/fonts/fontchain_lint.py
+++ b/tools/fonts/fontchain_lint.py
@@ -9,17 +9,37 @@
from fontTools import ttLib
LANG_TO_SCRIPT = {
+ 'as': 'Beng',
+ 'bn': 'Beng',
+ 'cy': 'Latn',
+ 'da': 'Latn',
'de': 'Latn',
'en': 'Latn',
'es': 'Latn',
+ 'et': 'Latn',
'eu': 'Latn',
- 'ja': 'Jpan',
- 'ko': 'Kore',
+ 'fr': 'Latn',
+ 'ga': 'Latn',
+ 'gu': 'Gujr',
+ 'hi': 'Deva',
+ 'hr': 'Latn',
'hu': 'Latn',
'hy': 'Armn',
+ 'ja': 'Jpan',
+ 'kn': 'Knda',
+ 'ko': 'Kore',
+ 'ml': 'Mlym',
+ 'mn': 'Cyrl',
+ 'mr': 'Deva',
'nb': 'Latn',
'nn': 'Latn',
+ 'or': 'Orya',
+ 'pa': 'Guru',
'pt': 'Latn',
+ 'sl': 'Latn',
+ 'ta': 'Taml',
+ 'te': 'Telu',
+ 'tk': 'Latn',
}
def lang_to_script(lang_code):
@@ -157,9 +177,10 @@
def check_emoji_availability():
emoji_fonts = [font[5] for font in _fallback_chain if 'Zsye' in font[1]]
+ assert len(emoji_fonts) == 1, 'There are %d emoji fonts.' % len(emoji_fonts)
+ emoji_font = emoji_fonts[0]
emoji_chars = _emoji_properties['Emoji']
- for emoji_font in emoji_fonts:
- assert_font_supports_all_of_chars(emoji_font, emoji_chars)
+ assert_font_supports_all_of_chars(emoji_font, emoji_chars)
def check_emoji_defaults():
@@ -253,11 +274,12 @@
hyphens_dir = path.join(target_out, 'usr', 'hyphen-data')
check_hyphens(hyphens_dir)
- ucd_path = sys.argv[2]
- parse_ucd(ucd_path)
- # Temporarily disable emoji checks for Bug 27785690
- # check_emoji_availability()
- # check_emoji_defaults()
+ check_emoji = sys.argv[2]
+ if check_emoji == 'true':
+ ucd_path = sys.argv[3]
+ parse_ucd(ucd_path)
+ check_emoji_availability()
+ check_emoji_defaults()
if __name__ == '__main__':
diff --git a/tools/layoutlib/.idea/codeStyleSettings.xml b/tools/layoutlib/.idea/codeStyleSettings.xml
index 89f7b34..ac90d1e 100644
--- a/tools/layoutlib/.idea/codeStyleSettings.xml
+++ b/tools/layoutlib/.idea/codeStyleSettings.xml
@@ -40,6 +40,7 @@
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
</XML>
<codeStyleSettings language="JAVA">
+ <option name="KEEP_LINE_BREAKS" value="false" />
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
<option name="CALL_PARAMETERS_WRAP" value="1" />
<option name="METHOD_PARAMETERS_WRAP" value="1" />
@@ -55,6 +56,7 @@
<option name="DOWHILE_BRACE_FORCE" value="3" />
<option name="WHILE_BRACE_FORCE" value="3" />
<option name="FOR_BRACE_FORCE" value="3" />
+ <option name="WRAP_LONG_LINES" value="true" />
<arrangement>
<groups>
<group>
diff --git a/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java b/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java
index 985dd5a..ea320c7 100644
--- a/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java
@@ -21,6 +21,7 @@
import com.android.ide.common.rendering.api.DensityBasedResourceValue;
import com.android.ide.common.rendering.api.LayoutLog;
import com.android.ide.common.rendering.api.LayoutlibCallback;
+import com.android.ide.common.rendering.api.RenderResources;
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.BridgeConstants;
@@ -84,7 +85,7 @@
return new BridgeTypedArray(resources, resources.mContext, numEntries, platformFile);
}
- private static Pair<String, ResourceValue> getResourceValue(Resources resources, int id,
+ private static Pair<ResourceType, String> getResourceInfo(Resources resources, int id,
boolean[] platformResFlag_out) {
// first get the String related to this id in the framework
Pair<ResourceType, String> resourceInfo = Bridge.resolveResourceId(id);
@@ -97,11 +98,7 @@
if (resourceInfo != null) {
platformResFlag_out[0] = true;
- String attributeName = resourceInfo.getSecond();
-
- return Pair.of(attributeName,
- resources.mContext.getRenderResources().getFrameworkResource(
- resourceInfo.getFirst(), attributeName));
+ return resourceInfo;
}
// didn't find a match in the framework? look in the project.
@@ -110,13 +107,24 @@
if (resourceInfo != null) {
platformResFlag_out[0] = false;
- String attributeName = resourceInfo.getSecond();
-
- return Pair.of(attributeName,
- resources.mContext.getRenderResources().getProjectResource(
- resourceInfo.getFirst(), attributeName));
+ return resourceInfo;
}
}
+ return null;
+ }
+
+ private static Pair<String, ResourceValue> getResourceValue(Resources resources, int id,
+ boolean[] platformResFlag_out) {
+ Pair<ResourceType, String> resourceInfo =
+ getResourceInfo(resources, id, platformResFlag_out);
+
+ if (resourceInfo != null) {
+ String attributeName = resourceInfo.getSecond();
+ RenderResources renderResources = resources.mContext.getRenderResources();
+ return Pair.of(attributeName, platformResFlag_out[0] ?
+ renderResources.getFrameworkResource(resourceInfo.getFirst(), attributeName) :
+ renderResources.getProjectResource(resourceInfo.getFirst(), attributeName));
+ }
return null;
}
@@ -626,17 +634,57 @@
@LayoutlibDelegate
static String getResourceEntryName(Resources resources, int resid) throws NotFoundException {
- throw new UnsupportedOperationException();
+ Pair<ResourceType, String> resourceInfo = getResourceInfo(resources, resid, new boolean[1]);
+ if (resourceInfo != null) {
+ return resourceInfo.getSecond();
+ }
+ throwException(resid, null);
+ return null;
+
}
@LayoutlibDelegate
static String getResourceName(Resources resources, int resid) throws NotFoundException {
- throw new UnsupportedOperationException();
+ boolean[] platformOut = new boolean[1];
+ Pair<ResourceType, String> resourceInfo = getResourceInfo(resources, resid, platformOut);
+ String packageName;
+ if (resourceInfo != null) {
+ if (platformOut[0]) {
+ packageName = SdkConstants.ANDROID_NS_NAME;
+ } else {
+ packageName = resources.mContext.getPackageName();
+ packageName = packageName == null ? SdkConstants.APP_PREFIX : packageName;
+ }
+ return packageName + ':' + resourceInfo.getFirst().getName() + '/' +
+ resourceInfo.getSecond();
+ }
+ throwException(resid, null);
+ return null;
+ }
+
+ @LayoutlibDelegate
+ static String getResourcePackageName(Resources resources, int resid) throws NotFoundException {
+ boolean[] platformOut = new boolean[1];
+ Pair<ResourceType, String> resourceInfo = getResourceInfo(resources, resid, platformOut);
+ if (resourceInfo != null) {
+ if (platformOut[0]) {
+ return SdkConstants.ANDROID_NS_NAME;
+ }
+ String packageName = resources.mContext.getPackageName();
+ return packageName == null ? SdkConstants.APP_PREFIX : packageName;
+ }
+ throwException(resid, null);
+ return null;
}
@LayoutlibDelegate
static String getResourceTypeName(Resources resources, int resid) throws NotFoundException {
- throw new UnsupportedOperationException();
+ Pair<ResourceType, String> resourceInfo = getResourceInfo(resources, resid, new boolean[1]);
+ if (resourceInfo != null) {
+ return resourceInfo.getFirst().getName();
+ }
+ throwException(resid, null);
+ return null;
}
@LayoutlibDelegate
@@ -849,22 +897,17 @@
* @throws NotFoundException
*/
private static void throwException(Resources resources, int id) throws NotFoundException {
- // first get the String related to this id in the framework
- Pair<ResourceType, String> resourceInfo = Bridge.resolveResourceId(id);
+ throwException(id, getResourceInfo(resources, id, new boolean[1]));
+ }
- // if the name is unknown in the framework, get it from the custom view loader.
- if (resourceInfo == null && resources.mLayoutlibCallback != null) {
- resourceInfo = resources.mLayoutlibCallback.resolveResourceId(id);
- }
-
+ private static void throwException(int id, @Nullable Pair<ResourceType, String> resourceInfo) {
String message;
if (resourceInfo != null) {
message = String.format(
"Could not find %1$s resource matching value 0x%2$X (resolved name: %3$s) in current configuration.",
resourceInfo.getFirst(), id, resourceInfo.getSecond());
} else {
- message = String.format(
- "Could not resolve resource value: 0x%1$X.", id);
+ message = String.format("Could not resolve resource value: 0x%1$X.", id);
}
throw new NotFoundException(message);
diff --git a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
index f5938cf..265ebd1 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
@@ -423,21 +423,13 @@
}
@LayoutlibDelegate
- /*package*/ static void native_offset(long nPath, float dx, float dy, long dst_path) {
+ /*package*/ static void native_offset(long nPath, float dx, float dy) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
return;
}
- // could be null if the int is 0;
- Path_Delegate dstDelegate = sManager.getDelegate(dst_path);
-
- pathDelegate.offset(dx, dy, dstDelegate);
- }
-
- @LayoutlibDelegate
- /*package*/ static void native_offset(long nPath, float dx, float dy) {
- native_offset(nPath, dx, dy, 0);
+ pathDelegate.offset(dx, dy);
}
@LayoutlibDelegate
@@ -860,21 +852,14 @@
*
* @param dx The amount in the X direction to offset the entire path
* @param dy The amount in the Y direction to offset the entire path
- * @param dst The translated path is written here. If this is null, then
- * the original path is modified.
*/
- public void offset(float dx, float dy, Path_Delegate dst) {
+ public void offset(float dx, float dy) {
GeneralPath newPath = new GeneralPath();
PathIterator iterator = mPath.getPathIterator(new AffineTransform(0, 0, dx, 0, 0, dy));
newPath.append(iterator, false /*connect*/);
-
- if (dst != null) {
- dst.mPath = newPath;
- } else {
- mPath = newPath;
- }
+ mPath = newPath;
}
/**
diff --git a/tools/layoutlib/bridge/src/android/view/Choreographer_Delegate.java b/tools/layoutlib/bridge/src/android/view/Choreographer_Delegate.java
index 01af669..381eb1f 100644
--- a/tools/layoutlib/bridge/src/android/view/Choreographer_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/view/Choreographer_Delegate.java
@@ -15,8 +15,11 @@
*/
package android.view;
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.layoutlib.bridge.Bridge;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+import java.lang.reflect.Field;
import java.util.concurrent.atomic.AtomicReference;
/**
@@ -64,4 +67,18 @@
thisChoreographer.doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
}
+
+ public static void dispose() {
+ try {
+ Field threadInstanceField = Choreographer.class.getDeclaredField("sThreadInstance");
+ threadInstanceField.setAccessible(true);
+ @SuppressWarnings("unchecked") ThreadLocal<Choreographer> threadInstance =
+ (ThreadLocal<Choreographer>) threadInstanceField.get(null);
+ threadInstance.remove();
+ } catch (ReflectiveOperationException e) {
+ assert false;
+ Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+ "Unable to clear Choreographer memory.", e, null);
+ }
+ }
}
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 309c1b8..f9e008e 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -403,8 +403,15 @@
}
@Override
- public void setNewConfiguration(Configuration arg0) throws RemoteException {
+ public int[] setNewConfiguration(Configuration arg0) throws RemoteException {
// TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Rect getBoundsForNewConfiguration(int stackId) throws RemoteException {
+ // TODO Auto-generated method stub
+ return null;
}
@Override
@@ -575,4 +582,10 @@
@Override
public void registerShortcutKey(long shortcutCode, IShortcutService service)
throws RemoteException {}
+
+ @Override
+ public void createWallpaperInputConsumer(InputChannel inputChannel) throws RemoteException {}
+
+ @Override
+ public void removeWallpaperInputConsumer() throws RemoteException {}
}
diff --git a/tools/layoutlib/bridge/src/android/view/RenderNode_Delegate.java b/tools/layoutlib/bridge/src/android/view/RenderNode_Delegate.java
index 1465f50..24f7887 100644
--- a/tools/layoutlib/bridge/src/android/view/RenderNode_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/view/RenderNode_Delegate.java
@@ -55,7 +55,7 @@
private String mName;
@LayoutlibDelegate
- /*package*/ static long nCreate(String name) {
+ /*package*/ static long nCreate(RenderNode thisRenderNode, String name) {
RenderNode_Delegate renderNodeDelegate = new RenderNode_Delegate();
renderNodeDelegate.mName = name;
return sManager.addNewDelegate(renderNodeDelegate);
diff --git a/tools/layoutlib/bridge/src/com/android/internal/util/VirtualRefBasePtr_Delegate.java b/tools/layoutlib/bridge/src/com/android/internal/util/VirtualRefBasePtr_Delegate.java
new file mode 100644
index 0000000..01fe45d
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/internal/util/VirtualRefBasePtr_Delegate.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.util.LongSparseLongArray;
+
+/**
+ * Delegate used to provide new implementation the native methods of {@link VirtualRefBasePtr}
+ *
+ * Through the layoutlib_create tool, the original native methods of VirtualRefBasePtr have been
+ * replaced by calls to methods of the same name in this delegate class.
+ *
+ */
+@SuppressWarnings("unused")
+public class VirtualRefBasePtr_Delegate {
+ private static final DelegateManager<Object> sManager = new DelegateManager<>(Object.class);
+ private static final LongSparseLongArray sRefCount = new LongSparseLongArray();
+
+ @LayoutlibDelegate
+ /*package*/ static synchronized void nIncStrong(long ptr) {
+ long counter = sRefCount.get(ptr);
+ sRefCount.put(ptr, ++counter);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static synchronized void nDecStrong(long ptr) {
+ long counter = sRefCount.get(ptr);
+
+ if (counter > 1) {
+ sRefCount.put(ptr, --counter);
+ } else {
+ sRefCount.delete(ptr);
+ sManager.removeJavaReferenceFor(ptr);
+ }
+ }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index c8e3d03..9e50ee8 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -24,6 +24,7 @@
import com.android.ide.common.rendering.api.Result;
import com.android.ide.common.rendering.api.Result.Status;
import com.android.ide.common.rendering.api.SessionParams;
+import com.android.layoutlib.bridge.android.RenderParamsFlags;
import com.android.layoutlib.bridge.impl.RenderDrawable;
import com.android.layoutlib.bridge.impl.RenderSessionImpl;
import com.android.layoutlib.bridge.util.DynamicIdMap;
@@ -408,7 +409,9 @@
/**
* Starts a layout session by inflating and rendering it. The method returns a
* {@link RenderSession} on which further actions can be taken.
- *
+ * <p/>
+ * If {@link SessionParams} includes the {@link RenderParamsFlags#FLAG_DO_NOT_RENDER_ON_CREATE},
+ * this method will only inflate the layout but will NOT render it.
* @param params the {@link SessionParams} object with all the information necessary to create
* the scene.
* @return a new {@link RenderSession} object that contains the result of the layout.
@@ -424,7 +427,10 @@
lastResult = scene.init(params.getTimeout());
if (lastResult.isSuccess()) {
lastResult = scene.inflate();
- if (lastResult.isSuccess()) {
+
+ boolean doNotRenderOnCreate = Boolean.TRUE.equals(
+ params.getFlag(RenderParamsFlags.FLAG_DO_NOT_RENDER_ON_CREATE));
+ if (lastResult.isSuccess() && !doNotRenderOnCreate) {
lastResult = scene.render(true /*freshRender*/);
}
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 4161307..2399b3a 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -110,13 +110,13 @@
public final class BridgeContext extends Context {
/** The map adds cookies to each view so that IDE can link xml tags to views. */
- private final HashMap<View, Object> mViewKeyMap = new HashMap<View, Object>();
+ private final HashMap<View, Object> mViewKeyMap = new HashMap<>();
/**
* In some cases, when inflating an xml, some objects are created. Then later, the objects are
* converted to views. This map stores the mapping from objects to cookies which can then be
* used to populate the mViewKeyMap.
*/
- private final HashMap<Object, Object> mViewKeyHelpMap = new HashMap<Object, Object>();
+ private final HashMap<Object, Object> mViewKeyHelpMap = new HashMap<>();
private final BridgeAssetManager mAssets;
private Resources mSystemResources;
private final Object mProjectKey;
@@ -132,8 +132,7 @@
private Resources.Theme mTheme;
- private final Map<Object, Map<String, String>> mDefaultPropMaps =
- new IdentityHashMap<Object, Map<String,String>>();
+ private final Map<Object, PropertiesMap> mDefaultPropMaps = new IdentityHashMap<>();
// maps for dynamically generated id representing style objects (StyleResourceValue)
@Nullable
@@ -142,13 +141,12 @@
private int mDynamicIdGenerator = 0x02030000; // Base id for R.style in custom namespace
// cache for TypedArray generated from StyleResourceValue object
- private Map<int[], Map<List<StyleResourceValue>, Map<Integer, BridgeTypedArray>>>
- mTypedArrayCache;
+ private TypedArrayCache mTypedArrayCache;
private BridgeInflater mBridgeInflater;
private BridgeContentResolver mContentResolver;
- private final Stack<BridgeXmlBlockParser> mParserStack = new Stack<BridgeXmlBlockParser>();
+ private final Stack<BridgeXmlBlockParser> mParserStack = new Stack<>();
private SharedPreferences mSharedPreferences;
private ClassLoader mClassLoader;
private IBinder mBinder;
@@ -162,7 +160,7 @@
* This a map from value to attribute name. Warning for missing references shouldn't be logged
* if value and attr name pair is the same as an entry in this map.
*/
- private static Map<String, String> RTL_ATTRS = new HashMap<String, String>(10);
+ private static Map<String, String> RTL_ATTRS = new HashMap<>(10);
static {
RTL_ATTRS.put("?android:attr/paddingLeft", "paddingStart");
@@ -325,11 +323,11 @@
return mParserStack.get(mParserStack.size() - 2);
}
- public boolean resolveThemeAttribute(int resid, TypedValue outValue, boolean resolveRefs) {
- Pair<ResourceType, String> resourceInfo = Bridge.resolveResourceId(resid);
+ public boolean resolveThemeAttribute(int resId, TypedValue outValue, boolean resolveRefs) {
+ Pair<ResourceType, String> resourceInfo = Bridge.resolveResourceId(resId);
boolean isFrameworkRes = true;
if (resourceInfo == null) {
- resourceInfo = mLayoutlibCallback.resolveResourceId(resid);
+ resourceInfo = mLayoutlibCallback.resolveResourceId(resId);
isFrameworkRes = false;
}
@@ -602,23 +600,20 @@
@Override
public final BridgeTypedArray obtainStyledAttributes(int[] attrs) {
- // No style is specified here, so create the typed array based on the default theme
- // and the styles already applied to it. A null value of style indicates that the default
- // theme should be used.
- return createStyleBasedTypedArray(null, attrs);
+ return obtainStyledAttributes(0, attrs);
}
@Override
- public final BridgeTypedArray obtainStyledAttributes(int resid, int[] attrs)
+ public final BridgeTypedArray obtainStyledAttributes(int resId, int[] attrs)
throws Resources.NotFoundException {
StyleResourceValue style = null;
// get the StyleResourceValue based on the resId;
- if (resid != 0) {
- style = getStyleByDynamicId(resid);
+ if (resId != 0) {
+ style = getStyleByDynamicId(resId);
if (style == null) {
// In some cases, style may not be a dynamic id, so we do a full search.
- ResourceReference ref = resolveId(resid);
+ ResourceReference ref = resolveId(resId);
if (ref != null) {
style = mRenderResources.getStyle(ref.getName(), ref.isFramework());
}
@@ -629,41 +624,33 @@
}
}
- // The map is from
- // attrs (int[]) -> context's current themes (List<StyleRV>) -> resid (int) -> typed array.
if (mTypedArrayCache == null) {
- mTypedArrayCache = new IdentityHashMap<int[],
- Map<List<StyleResourceValue>, Map<Integer, BridgeTypedArray>>>();
+ mTypedArrayCache = new TypedArrayCache();
}
- // get the 2nd map
- Map<List<StyleResourceValue>, Map<Integer, BridgeTypedArray>> map2 =
- mTypedArrayCache.get(attrs);
- if (map2 == null) {
- map2 = new HashMap<List<StyleResourceValue>, Map<Integer, BridgeTypedArray>>();
- mTypedArrayCache.put(attrs, map2);
- }
-
- // get the 3rd map
List<StyleResourceValue> currentThemes = mRenderResources.getAllThemes();
- Map<Integer, BridgeTypedArray> map3 = map2.get(currentThemes);
- if (map3 == null) {
- map3 = new HashMap<Integer, BridgeTypedArray>();
- // Create a copy of the list before adding it to the map. This allows reusing the
- // existing list.
- currentThemes = new ArrayList<StyleResourceValue>(currentThemes);
- map2.put(currentThemes, map3);
+
+ Pair<BridgeTypedArray, PropertiesMap> typeArrayAndPropertiesPair =
+ mTypedArrayCache.get(attrs, currentThemes, resId);
+
+ if (typeArrayAndPropertiesPair == null) {
+ typeArrayAndPropertiesPair = createStyleBasedTypedArray(style, attrs);
+ mTypedArrayCache.put(attrs, currentThemes, resId, typeArrayAndPropertiesPair);
}
-
- // get the array from the 3rd map
- BridgeTypedArray ta = map3.get(resid);
-
- if (ta == null) {
- ta = createStyleBasedTypedArray(style, attrs);
- map3.put(resid, ta);
+ // Add value to defaultPropsMap if needed
+ if (typeArrayAndPropertiesPair.getSecond() != null) {
+ Object key = getCurrentParser().getViewCookie();
+ if (key != null) {
+ PropertiesMap defaultPropMap = mDefaultPropMaps.get(key);
+ if (defaultPropMap == null) {
+ defaultPropMap = typeArrayAndPropertiesPair.getSecond();
+ mDefaultPropMaps.put(key, defaultPropMap);
+ } else {
+ defaultPropMap.putAll(typeArrayAndPropertiesPair.getSecond());
+ }
+ }
}
-
- return ta;
+ return typeArrayAndPropertiesPair.getFirst();
}
@Override
@@ -675,7 +662,7 @@
public BridgeTypedArray obtainStyledAttributes(AttributeSet set, int[] attrs,
int defStyleAttr, int defStyleRes) {
- Map<String, String> defaultPropMap = null;
+ PropertiesMap defaultPropMap = null;
boolean isPlatformFile = true;
// Hint: for XmlPullParser, attach source //DEVICE_SRC/dalvik/libcore/xml/src/java
@@ -689,7 +676,7 @@
if (key != null) {
defaultPropMap = mDefaultPropMaps.get(key);
if (defaultPropMap == null) {
- defaultPropMap = new HashMap<String, String>();
+ defaultPropMap = new PropertiesMap();
mDefaultPropMaps.put(key, defaultPropMap);
}
}
@@ -937,32 +924,33 @@
*
* @see #obtainStyledAttributes(int, int[])
*/
- private BridgeTypedArray createStyleBasedTypedArray(@Nullable StyleResourceValue style,
- int[] attrs) throws Resources.NotFoundException {
-
+ private Pair<BridgeTypedArray, PropertiesMap> createStyleBasedTypedArray(
+ @Nullable StyleResourceValue style, int[] attrs) throws Resources.NotFoundException {
List<Pair<String, Boolean>> attributes = searchAttrs(attrs);
- BridgeTypedArray ta = Resources_Delegate.newTypeArray(mSystemResources, attrs.length,
- false);
+ BridgeTypedArray ta = Resources_Delegate.newTypeArray(mSystemResources, attrs.length, false);
+ PropertiesMap defaultPropMap = new PropertiesMap();
// for each attribute, get its name so that we can search it in the style
- for (int i = 0 ; i < attrs.length ; i++) {
+ for (int i = 0; i < attrs.length; i++) {
Pair<String, Boolean> attribute = attributes.get(i);
if (attribute != null) {
// look for the value in the given style
ResourceValue resValue;
+ String attrName = attribute.getFirst();
if (style != null) {
- resValue = mRenderResources.findItemInStyle(style, attribute.getFirst(),
+ resValue = mRenderResources.findItemInStyle(style, attrName,
attribute.getSecond());
} else {
- resValue = mRenderResources.findItemInTheme(attribute.getFirst(),
- attribute.getSecond());
+ resValue = mRenderResources.findItemInTheme(attrName, attribute.getSecond());
}
if (resValue != null) {
+ // Add it to defaultPropMap before resolving
+ defaultPropMap.put(attrName, resValue.getValue());
// resolve it to make sure there are no references left.
- ta.bridgeSetValue(i, attribute.getFirst(), attribute.getSecond(),
+ ta.bridgeSetValue(i, attrName, attribute.getSecond(),
mRenderResources.resolveResValue(resValue));
}
}
@@ -970,7 +958,7 @@
ta.sealArray();
- return ta;
+ return Pair.of(ta, defaultPropMap);
}
/**
@@ -982,7 +970,7 @@
* @return List of attribute information.
*/
private List<Pair<String, Boolean>> searchAttrs(int[] attrs) {
- List<Pair<String, Boolean>> results = new ArrayList<Pair<String, Boolean>>(attrs.length);
+ List<Pair<String, Boolean>> results = new ArrayList<>(attrs.length);
// for each attribute, get its name so that we can search it in the style
for (int attr : attrs) {
@@ -1011,7 +999,7 @@
* @return A (name, isFramework) pair describing the attribute if found. Returns null
* if nothing is found.
*/
- public Pair<String, Boolean> searchAttr(int attr) {
+ private Pair<String, Boolean> searchAttr(int attr) {
Pair<ResourceType, String> info = Bridge.resolveResourceId(attr);
if (info != null) {
return Pair.of(info.getSecond(), Boolean.TRUE);
@@ -1028,8 +1016,8 @@
public int getDynamicIdByStyle(StyleResourceValue resValue) {
if (mDynamicIdToStyleMap == null) {
// create the maps.
- mDynamicIdToStyleMap = new HashMap<Integer, StyleResourceValue>();
- mStyleToDynamicIdMap = new HashMap<StyleResourceValue, Integer>();
+ mDynamicIdToStyleMap = new HashMap<>();
+ mStyleToDynamicIdMap = new HashMap<>();
}
// look for an existing id
@@ -1868,4 +1856,69 @@
public boolean isCredentialProtectedStorage() {
return false;
}
+
+
+ /**
+ * The cached value depends on
+ * <ol>
+ * <li>{@code int[]}: the attributes for which TypedArray is created </li>
+ * <li>{@code List<StyleResourceValue>}: the themes set on the context at the time of
+ * creation of the TypedArray</li>
+ * <li>{@code Integer}: the default style used at the time of creation</li>
+ * </ol>
+ *
+ * The class is created by using nested maps resolving one dependency at a time.
+ * <p/>
+ * The final value of the nested maps is a pair of the typed array and a map of properties
+ * that should be added to {@link #mDefaultPropMaps}, if needed.
+ */
+ private static class TypedArrayCache {
+
+ private Map<int[],
+ Map<List<StyleResourceValue>,
+ Map<Integer, Pair<BridgeTypedArray, PropertiesMap>>>> mCache;
+
+ public TypedArrayCache() {
+ mCache = new IdentityHashMap<>();
+ }
+
+ public Pair<BridgeTypedArray, PropertiesMap> get(int[] attrs,
+ List<StyleResourceValue> themes, int resId) {
+ Map<List<StyleResourceValue>, Map<Integer, Pair<BridgeTypedArray, PropertiesMap>>>
+ cacheFromThemes = mCache.get(attrs);
+ if (cacheFromThemes != null) {
+ Map<Integer, Pair<BridgeTypedArray, PropertiesMap>> cacheFromResId =
+ cacheFromThemes.get(themes);
+ if (cacheFromResId != null) {
+ return cacheFromResId.get(resId);
+ }
+ }
+ return null;
+ }
+
+ public void put(int[] attrs, List<StyleResourceValue> themes, int resId,
+ Pair<BridgeTypedArray, PropertiesMap> value) {
+ Map<List<StyleResourceValue>, Map<Integer, Pair<BridgeTypedArray, PropertiesMap>>>
+ cacheFromThemes = mCache.get(attrs);
+ if (cacheFromThemes == null) {
+ cacheFromThemes = new HashMap<>();
+ mCache.put(attrs, cacheFromThemes);
+ }
+ Map<Integer, Pair<BridgeTypedArray, PropertiesMap>> cacheFromResId =
+ cacheFromThemes.get(themes);
+ if (cacheFromResId == null) {
+ cacheFromResId = new HashMap<>();
+ cacheFromThemes.put(themes, cacheFromResId);
+ }
+ cacheFromResId.put(resId, value);
+ }
+
+ }
+
+ /**
+ * An alias used for the value in {@code {@link #mDefaultPropMaps}}
+ */
+ private static class PropertiesMap extends HashMap<String, String> {
+ }
+
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
index 53adb41..5a6a00f 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
@@ -97,7 +97,6 @@
@Override
public void repositionChild(IWindow window, int left, int top, int right, int bottom,
- int requestedWidth, int requestedHeight,
long deferTransactionUntilFrame, Rect outFrame) {
// pass for now.
return;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java
index bd17a2f..051de90 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java
@@ -53,6 +53,12 @@
*/
public static final Key<Boolean> FLAG_KEY_XML_FILE_PARSER_SUPPORT =
new Key<Boolean>("xmlFileParser", Boolean.class);
+ /**
+ * To tell LayoutLib to not render when creating a new session. This allows controlling when the first
+ * layout rendering will happen.
+ */
+ public static final Key<Boolean> FLAG_DO_NOT_RENDER_ON_CREATE =
+ new Key<Boolean>("doNotRenderOnCreate", Boolean.class);
// Disallow instances.
private RenderParamsFlags() {}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBarWrapper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBarWrapper.java
index af6ba24..2cdc647 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBarWrapper.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBarWrapper.java
@@ -43,6 +43,7 @@
import android.view.ViewGroup;
import android.view.WindowCallback;
import android.widget.ActionMenuPresenter;
+import android.widget.ActionMenuView;
import android.widget.Toolbar;
import android.widget.Toolbar_Accessor;
@@ -196,11 +197,16 @@
@Override
protected void inflateMenus() {
super.inflateMenus();
- // Inflating the menus doesn't initialize the ActionMenuPresenter. Setting a fake menu
- // and then setting it back does the trick.
+ // Inflating the menus isn't enough. ActionMenuPresenter needs to be initialized too.
MenuBuilder menu = getMenuBuilder();
DecorToolbar decorToolbar = getDecorToolbar();
+ // Setting a menu different from the above initializes the presenter.
decorToolbar.setMenu(new MenuBuilder(getActionMenuContext()), null);
+ // ActionMenuView needs to be recreated to be able to set the menu back.
+ ActionMenuPresenter presenter = getActionMenuPresenter();
+ if (presenter != null) {
+ presenter.setMenuView(new ActionMenuView(getPopupContext()));
+ }
decorToolbar.setMenu(menu, null);
}
@@ -255,7 +261,7 @@
@NonNull ActionBarView actionBarView) {
super(context, callback, new WindowDecorActionBar(decorContentRoot));
mActionBarView = actionBarView;
- mActionBar = ((WindowDecorActionBar) super.mActionBar);
+ mActionBar = (WindowDecorActionBar) super.mActionBar;
mDecorContentRoot = decorContentRoot;
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java
index baf2e2e..c59b1a6 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java
@@ -22,9 +22,11 @@
import android.annotation.Nullable;
import android.util.SparseArray;
+import java.io.PrintStream;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
/**
* Manages native delegates.
@@ -73,14 +75,14 @@
public final class DelegateManager<T> {
@SuppressWarnings("FieldCanBeLocal")
private final Class<T> mClass;
- private final SparseWeakArray<T> mDelegates = new SparseWeakArray<T>();
+ private static final SparseWeakArray<Object> sDelegates = new SparseWeakArray<>();
/** list used to store delegates when their main object holds a reference to them.
* This is to ensure that the WeakReference in the SparseWeakArray doesn't get GC'ed
* @see #addNewDelegate(Object)
* @see #removeJavaReferenceFor(long)
*/
- private final List<T> mJavaReferences = new ArrayList<T>();
- private int mDelegateCounter = 0;
+ private static final List<Object> sJavaReferences = new ArrayList<>();
+ private static final AtomicLong sDelegateCounter = new AtomicLong(1);
public DelegateManager(Class<T> theClass) {
mClass = theClass;
@@ -97,9 +99,12 @@
* @return the delegate or null if not found.
*/
@Nullable
- public synchronized T getDelegate(long native_object) {
+ public T getDelegate(long native_object) {
if (native_object > 0) {
- T delegate = mDelegates.get(native_object);
+ Object delegate;
+ synchronized (DelegateManager.class) {
+ delegate = sDelegates.get(native_object);
+ }
if (Debug.DEBUG) {
if (delegate == null) {
@@ -109,7 +114,8 @@
}
assert delegate != null;
- return delegate;
+ //noinspection unchecked
+ return (T)delegate;
}
return null;
}
@@ -119,12 +125,13 @@
* @param newDelegate the delegate to add
* @return a unique native int to identify the delegate
*/
- public synchronized long addNewDelegate(T newDelegate) {
- long native_object = ++mDelegateCounter;
-
- mDelegates.put(native_object, newDelegate);
- assert !mJavaReferences.contains(newDelegate);
- mJavaReferences.add(newDelegate);
+ public long addNewDelegate(T newDelegate) {
+ long native_object = sDelegateCounter.getAndIncrement();
+ synchronized (DelegateManager.class) {
+ sDelegates.put(native_object, newDelegate);
+ assert !sJavaReferences.contains(newDelegate);
+ sJavaReferences.add(newDelegate);
+ }
if (Debug.DEBUG) {
System.out.println(
@@ -140,14 +147,23 @@
* Removes the main reference on the given delegate.
* @param native_object the native integer representing the delegate.
*/
- public synchronized void removeJavaReferenceFor(long native_object) {
- T delegate = getDelegate(native_object);
+ public void removeJavaReferenceFor(long native_object) {
+ synchronized (DelegateManager.class) {
+ T delegate = getDelegate(native_object);
- if (Debug.DEBUG) {
- System.out.println("Removing main Java ref on " + mClass.getSimpleName() +
- " with int " + native_object);
+ if (Debug.DEBUG) {
+ System.out.println("Removing main Java ref on " + mClass.getSimpleName() +
+ " with int " + native_object);
+ }
+
+ sJavaReferences.remove(delegate);
}
+ }
- mJavaReferences.remove(delegate);
+ public synchronized static void dump(PrintStream out) {
+ for (Object reference : sJavaReferences) {
+ int idx = sDelegates.indexOfValue(reference);
+ out.printf("[%d] %s\n", sDelegates.keyAt(idx), reference.getClass().getSimpleName());
+ }
}
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
index 4e4fcd0..0c53753 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
@@ -122,7 +122,7 @@
// build the context
mContext = new BridgeContext(mParams.getProjectKey(), metrics, resources,
- mParams.getAssets(), mParams.getLayoutlibCallback(), getConfiguration(),
+ mParams.getAssets(), mParams.getLayoutlibCallback(), getConfiguration(mParams),
mParams.getTargetSdkVersion(), mParams.isRtlSupported());
setUp();
@@ -130,7 +130,6 @@
return SUCCESS.createResult();
}
-
/**
* Prepares the scene for action.
* <p>
@@ -320,10 +319,11 @@
}
}
- private Configuration getConfiguration() {
+ // VisibleForTesting
+ public static Configuration getConfiguration(RenderParams params) {
Configuration config = new Configuration();
- HardwareConfig hardwareConfig = mParams.getHardwareConfig();
+ HardwareConfig hardwareConfig = params.getHardwareConfig();
ScreenSize screenSize = hardwareConfig.getScreenSize();
if (screenSize != null) {
@@ -392,7 +392,7 @@
} else {
config.screenLayout |= Configuration.SCREENLAYOUT_ROUND_UNDEFINED;
}
- String locale = getParams().getLocale();
+ String locale = params.getLocale();
if (locale != null && !locale.isEmpty()) config.locale = new Locale(locale);
// TODO: fill in more config info.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index 016825a..866b248 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -267,6 +267,34 @@
}
/**
+ * Renders the given view hierarchy to the passed canvas and returns the result of the render
+ * operation.
+ * @param canvas an optional canvas to render the views to. If null, only the measure and
+ * layout steps will be executed.
+ */
+ private static Result render(@NonNull BridgeContext context, @NonNull ViewGroup viewRoot,
+ @Nullable Canvas canvas, int width, int height) {
+ // measure again with the size we need
+ // This must always be done before the call to layout
+ measureView(viewRoot, null /*measuredView*/,
+ width, MeasureSpec.EXACTLY,
+ height, MeasureSpec.EXACTLY);
+
+ // now do the layout.
+ viewRoot.layout(0, 0, width, height);
+ handleScrolling(context, viewRoot);
+
+ if (canvas == null) {
+ return SUCCESS.createResult();
+ }
+
+ AttachInfo_Accessor.dispatchOnPreDraw(viewRoot);
+ viewRoot.draw(canvas);
+
+ return SUCCESS.createResult();
+ }
+
+ /**
* Renders the scene.
* <p>
* {@link #acquire(long)} must have been called before this.
@@ -367,24 +395,12 @@
}
}
- // measure again with the size we need
- // This must always be done before the call to layout
- measureView(mViewRoot, null /*measuredView*/,
- mMeasuredScreenWidth, MeasureSpec.EXACTLY,
- mMeasuredScreenHeight, MeasureSpec.EXACTLY);
-
- // now do the layout.
- mViewRoot.layout(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight);
-
- handleScrolling(mViewRoot);
-
+ Result renderResult = SUCCESS.createResult();
if (params.isLayoutOnly()) {
// delete the canvas and image to reset them on the next full rendering
mImage = null;
mCanvas = null;
} else {
- AttachInfo_Accessor.dispatchOnPreDraw(mViewRoot);
-
// draw the views
// create the BufferedImage into which the layout will be rendered.
boolean newImage = false;
@@ -446,6 +462,9 @@
if (mElapsedFrameTimeNanos >= 0) {
long initialTime = System_Delegate.nanoTime();
if (!mFirstFrameExecuted) {
+ // We need to run an initial draw call to initialize the animations
+ render(getContext(), mViewRoot, mCanvas, 0, 0);
+
// The first frame will initialize the animations
Choreographer_Delegate.doFrame(initialTime);
mFirstFrameExecuted = true;
@@ -453,14 +472,15 @@
// Second frame will move the animations
Choreographer_Delegate.doFrame(initialTime + mElapsedFrameTimeNanos);
}
- mViewRoot.draw(mCanvas);
+ renderResult = render(getContext(), mViewRoot, mCanvas, mMeasuredScreenWidth,
+ mMeasuredScreenHeight);
}
mSystemViewInfoList = visitAllChildren(mViewRoot, 0, params.getExtendedViewInfoMode(),
false);
// success!
- return SUCCESS.createResult();
+ return renderResult;
} catch (Throwable e) {
// get the real cause of the exception.
Throwable t = e;
@@ -488,7 +508,7 @@
* @return the measured width/height if measuredView is non-null, null otherwise.
*/
@SuppressWarnings("deprecation") // For the use of Pair
- private Pair<Integer, Integer> measureView(ViewGroup viewToMeasure, View measuredView,
+ private static Pair<Integer, Integer> measureView(ViewGroup viewToMeasure, View measuredView,
int width, int widthMode, int height, int heightMode) {
int w_spec = MeasureSpec.makeMeasureSpec(width, widthMode);
int h_spec = MeasureSpec.makeMeasureSpec(height, heightMode);
@@ -1061,8 +1081,7 @@
* the component supports nested scrolling attempt that first, then use the unconsumed scroll
* part to scroll the content in the component.
*/
- private void handleScrolling(View view) {
- BridgeContext context = getContext();
+ private static void handleScrolling(BridgeContext context, View view) {
int scrollPosX = context.getScrollXPos(view);
int scrollPosY = context.getScrollYPos(view);
if (scrollPosX != 0 || scrollPosY != 0) {
@@ -1080,7 +1099,7 @@
}
}
if (scrollPosX != 0 || scrollPosY != 0) {
- view.scrollBy(scrollPosX, scrollPosY);
+ view.scrollTo(scrollPosX, scrollPosY);
}
}
@@ -1090,7 +1109,7 @@
ViewGroup group = (ViewGroup) view;
for (int i = 0; i < group.getChildCount(); i++) {
View child = group.getChildAt(i);
- handleScrolling(child);
+ handleScrolling(context, child);
}
}
@@ -1434,6 +1453,7 @@
if (createdLooper) {
Bridge.cleanupThread();
+ Choreographer_Delegate.dispose();
}
}
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/DynamicIdMap.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/DynamicIdMap.java
index 08a8faf..161bf41 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/DynamicIdMap.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/DynamicIdMap.java
@@ -27,8 +27,8 @@
public class DynamicIdMap {
- private final Map<Pair<ResourceType, String>, Integer> mDynamicIds = new HashMap<Pair<ResourceType, String>, Integer>();
- private final SparseArray<Pair<ResourceType, String>> mRevDynamicIds = new SparseArray<Pair<ResourceType, String>>();
+ private final Map<Pair<ResourceType, String>, Integer> mDynamicIds = new HashMap<>();
+ private final SparseArray<Pair<ResourceType, String>> mRevDynamicIds = new SparseArray<>();
private int mDynamicSeed;
public DynamicIdMap(int seed) {
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/scrolled.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/scrolled.xml
index a5ebc2e..a07498c 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/scrolled.xml
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/scrolled.xml
@@ -2,8 +2,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
- android:scrollX="10px"
- android:scrollY="30px">
+ android:scrollX="30px"
+ android:scrollY="90px">
<LinearLayout
android:layout_width="60dp"
android:layout_height="60dp"
@@ -29,8 +29,8 @@
android:layout_width="200dp"
android:layout_height="400dp"
android:orientation="vertical"
- android:scrollX="-30px"
- android:scrollY="150px">
+ android:scrollX="-90px"
+ android:scrollY="450px">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="60dp"
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
index c2f06e8..034c8b2 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
@@ -29,11 +29,16 @@
import com.android.ide.common.resources.configuration.FolderConfiguration;
import com.android.io.FolderWrapper;
import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.android.RenderParamsFlags;
+import com.android.layoutlib.bridge.impl.RenderAction;
+import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.layoutlib.bridge.intensive.setup.ConfigGenerator;
import com.android.layoutlib.bridge.intensive.setup.LayoutLibTestCallback;
import com.android.layoutlib.bridge.intensive.setup.LayoutPullParser;
import com.android.resources.Density;
import com.android.resources.Navigation;
+import com.android.resources.ResourceType;
import com.android.utils.ILogger;
import org.junit.AfterClass;
@@ -42,13 +47,16 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.res.AssetManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.util.DisplayMetrics;
import java.io.File;
-import java.io.FileFilter;
import java.io.IOException;
+import java.lang.ref.WeakReference;
import java.net.URL;
import java.util.Arrays;
-import java.util.Comparator;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertEquals;
@@ -160,13 +168,8 @@
if (!host.isDirectory()) {
return null;
}
- File[] hosts = host.listFiles(new FileFilter() {
- @Override
- public boolean accept(File path) {
- return path.isDirectory() && (path.getName().startsWith("linux-") || path.getName()
- .startsWith("darwin-"));
- }
- });
+ File[] hosts = host.listFiles(path -> path.isDirectory() &&
+ (path.getName().startsWith("linux-") || path.getName().startsWith("darwin-")));
for (File hostOut : hosts) {
String platformDir = getPlatformDirFromHostOut(hostOut);
if (platformDir != null) {
@@ -184,12 +187,9 @@
if (!sdkDir.isDirectory()) {
return null;
}
- File[] sdkDirs = sdkDir.listFiles(new FileFilter() {
- @Override
- public boolean accept(File path) {
- // We need to search for $TARGET_PRODUCT (usually, sdk_phone_armv7)
- return path.isDirectory() && path.getName().startsWith("sdk");
- }
+ File[] sdkDirs = sdkDir.listFiles(path -> {
+ // We need to search for $TARGET_PRODUCT (usually, sdk_phone_armv7)
+ return path.isDirectory() && path.getName().startsWith("sdk");
});
for (File dir : sdkDirs) {
String platformDir = getPlatformDirFromHostOutSdkSdk(dir);
@@ -201,46 +201,34 @@
}
private static String getPlatformDirFromHostOutSdkSdk(File sdkDir) {
- File[] possibleSdks = sdkDir.listFiles(new FileFilter() {
- @Override
- public boolean accept(File path) {
- return path.isDirectory() && path.getName().contains("android-sdk");
- }
- });
+ File[] possibleSdks = sdkDir.listFiles(
+ path -> path.isDirectory() && path.getName().contains("android-sdk"));
for (File possibleSdk : possibleSdks) {
File platformsDir = new File(possibleSdk, "platforms");
- File[] platforms = platformsDir.listFiles(new FileFilter() {
- @Override
- public boolean accept(File path) {
- return path.isDirectory() && path.getName().startsWith("android-");
- }
- });
+ File[] platforms = platformsDir.listFiles(
+ path -> path.isDirectory() && path.getName().startsWith("android-"));
if (platforms == null || platforms.length == 0) {
continue;
}
- Arrays.sort(platforms, new Comparator<File>() {
- // Codenames before ints. Higher APIs precede lower.
- @Override
- public int compare(File o1, File o2) {
- final int MAX_VALUE = 1000;
- String suffix1 = o1.getName().substring("android-".length());
- String suffix2 = o2.getName().substring("android-".length());
- int suff1, suff2;
- try {
- suff1 = Integer.parseInt(suffix1);
- } catch (NumberFormatException e) {
- suff1 = MAX_VALUE;
- }
- try {
- suff2 = Integer.parseInt(suffix2);
- } catch (NumberFormatException e) {
- suff2 = MAX_VALUE;
- }
- if (suff1 != MAX_VALUE || suff2 != MAX_VALUE) {
- return suff2 - suff1;
- }
- return suffix2.compareTo(suffix1);
+ Arrays.sort(platforms, (o1, o2) -> {
+ final int MAX_VALUE = 1000;
+ String suffix1 = o1.getName().substring("android-".length());
+ String suffix2 = o2.getName().substring("android-".length());
+ int suff1, suff2;
+ try {
+ suff1 = Integer.parseInt(suffix1);
+ } catch (NumberFormatException e) {
+ suff1 = MAX_VALUE;
}
+ try {
+ suff2 = Integer.parseInt(suffix2);
+ } catch (NumberFormatException e) {
+ suff2 = MAX_VALUE;
+ }
+ if (suff1 != MAX_VALUE || suff2 != MAX_VALUE) {
+ return suff2 - suff1;
+ }
+ return suffix2.compareTo(suffix1);
});
return platforms[0].getAbsolutePath();
}
@@ -261,6 +249,7 @@
return null;
}
}
+
/**
* Initialize the bridge and the resource maps.
*/
@@ -312,6 +301,16 @@
renderAndVerify("allwidgets.xml", "allwidgets_tab.png", ConfigGenerator.NEXUS_7_2012);
}
+ private static void gc() {
+ // See RuntimeUtil#gc in jlibs (http://jlibs.in/)
+ Object obj = new Object();
+ WeakReference ref = new WeakReference<Object>(obj);
+ obj = null;
+ while(ref.get() != null) {
+ System.gc();
+ }
+ }
+
@AfterClass
public static void tearDown() {
sLayoutLibLog = null;
@@ -319,14 +318,18 @@
sProjectResources = null;
sLogger = null;
sBridge = null;
+
+ gc();
+
+ System.out.println("Objects still linked from the DelegateManager:");
+ DelegateManager.dump(System.out);
}
/** Test expand_layout.xml */
@Test
public void testExpand() throws ClassNotFoundException {
// Create the layout pull parser.
- LayoutPullParser parser = new LayoutPullParser(APP_TEST_RES + "/layout/" +
- "expand_vert_layout.xml");
+ LayoutPullParser parser = createLayoutPullParser("expand_vert_layout.xml");
// Create LayoutLibCallback.
LayoutLibTestCallback layoutLibCallback = new LayoutLibTestCallback(getLogger());
layoutLibCallback.initResources();
@@ -348,8 +351,7 @@
.setScreenHeight(300)
.setDensity(Density.XHIGH)
.setNavigation(Navigation.NONAV);
- parser = new LayoutPullParser(APP_TEST_RES + "/layout/" +
- "expand_horz_layout.xml");
+ parser = createLayoutPullParser("expand_horz_layout.xml");
params = getSessionParams(parser, customConfigGenerator,
layoutLibCallback, "Theme.Material.Light.NoActionBar.Fullscreen", false,
RenderingMode.H_SCROLL, 22);
@@ -361,8 +363,7 @@
@Test
public void testVectorAnimation() throws ClassNotFoundException {
// Create the layout pull parser.
- LayoutPullParser parser = new LayoutPullParser(APP_TEST_RES + "/layout/" +
- "indeterminate_progressbar.xml");
+ LayoutPullParser parser = createLayoutPullParser("indeterminate_progressbar.xml");
// Create LayoutLibCallback.
LayoutLibTestCallback layoutLibCallback = new LayoutLibTestCallback(getLogger());
layoutLibCallback.initResources();
@@ -373,8 +374,7 @@
renderAndVerify(params, "animated_vector.png", TimeUnit.SECONDS.toNanos(2));
- parser = new LayoutPullParser(APP_TEST_RES + "/layout/" +
- "indeterminate_progressbar.xml");
+ parser = createLayoutPullParser("indeterminate_progressbar.xml");
params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false,
RenderingMode.V_SCROLL, 22);
@@ -388,8 +388,7 @@
@Test
public void testVectorDrawable() throws ClassNotFoundException {
// Create the layout pull parser.
- LayoutPullParser parser = new LayoutPullParser(APP_TEST_RES + "/layout/" +
- "vector_drawable.xml");
+ LayoutPullParser parser = createLayoutPullParser("vector_drawable.xml");
// Create LayoutLibCallback.
LayoutLibTestCallback layoutLibCallback = new LayoutLibTestCallback(getLogger());
layoutLibCallback.initResources();
@@ -405,8 +404,7 @@
@Test
public void testScrolling() throws ClassNotFoundException {
// Create the layout pull parser.
- LayoutPullParser parser = new LayoutPullParser(APP_TEST_RES + "/layout/" +
- "scrolled.xml");
+ LayoutPullParser parser = createLayoutPullParser("scrolled.xml");
// Create LayoutLibCallback.
LayoutLibTestCallback layoutLibCallback = new LayoutLibTestCallback(getLogger());
layoutLibCallback.initResources();
@@ -435,6 +433,39 @@
assertEquals(690, rootLayout.getChildren().get(5).getChildren().get(0).getRight());
}
+ @Test
+ public void testGetResourceNameVariants() throws Exception {
+ // Setup
+ SessionParams params = createSessionParams("", ConfigGenerator.NEXUS_4);
+ AssetManager assetManager = AssetManager.getSystem();
+ DisplayMetrics metrics = new DisplayMetrics();
+ Configuration configuration = RenderAction.getConfiguration(params);
+ Resources resources = new Resources(assetManager, metrics, configuration);
+ resources.mLayoutlibCallback = params.getLayoutlibCallback();
+ resources.mContext =
+ new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
+ params.getAssets(), params.getLayoutlibCallback(), configuration,
+ params.getTargetSdkVersion(), params.isRtlSupported());
+ // Test
+ assertEquals("android:style/ButtonBar",
+ resources.getResourceName(android.R.style.ButtonBar));
+ assertEquals("android", resources.getResourcePackageName(android.R.style.ButtonBar));
+ assertEquals("ButtonBar", resources.getResourceEntryName(android.R.style.ButtonBar));
+ assertEquals("style", resources.getResourceTypeName(android.R.style.ButtonBar));
+ int id = resources.mLayoutlibCallback.getResourceId(ResourceType.STRING, "app_name");
+ assertEquals("com.android.layoutlib.test.myapplication:string/app_name",
+ resources.getResourceName(id));
+ assertEquals("com.android.layoutlib.test.myapplication",
+ resources.getResourcePackageName(id));
+ assertEquals("string", resources.getResourceTypeName(id));
+ assertEquals("app_name", resources.getResourceEntryName(id));
+ }
+
+ @NonNull
+ private LayoutPullParser createLayoutPullParser(String layoutPath) {
+ return new LayoutPullParser(APP_TEST_RES + "/layout/" + layoutPath);
+ }
+
/**
* Create a new rendering session and test that rendering the given layout doesn't throw any
* exceptions and matches the provided image.
@@ -505,16 +536,21 @@
private RenderResult renderAndVerify(String layoutFileName, String goldenFileName,
ConfigGenerator deviceConfig)
throws ClassNotFoundException {
+ SessionParams params = createSessionParams(layoutFileName, deviceConfig);
+ return renderAndVerify(params, goldenFileName);
+ }
+
+ private SessionParams createSessionParams(String layoutFileName, ConfigGenerator deviceConfig)
+ throws ClassNotFoundException {
// Create the layout pull parser.
- LayoutPullParser parser = new LayoutPullParser(APP_TEST_RES + "/layout/" + layoutFileName);
+ LayoutPullParser parser = createLayoutPullParser(layoutFileName);
// Create LayoutLibCallback.
LayoutLibTestCallback layoutLibCallback = new LayoutLibTestCallback(getLogger());
layoutLibCallback.initResources();
// TODO: Set up action bar handler properly to test menu rendering.
// Create session params.
- SessionParams params = getSessionParams(parser, deviceConfig,
+ return getSessionParams(parser, deviceConfig,
layoutLibCallback, "AppTheme", true, RenderingMode.NORMAL, 22);
- return renderAndVerify(params, goldenFileName);
}
/**
@@ -529,7 +565,7 @@
sFrameworkRepo.getConfiguredResources(config),
themeName, isProjectTheme);
- return new SessionParams(
+ SessionParams sessionParams = new SessionParams(
layoutParser,
renderingMode,
null /*used for caching*/,
@@ -539,6 +575,8 @@
0,
targetSdk,
getLayoutLog());
+ sessionParams.setFlag(RenderParamsFlags.FLAG_DO_NOT_RENDER_ON_CREATE, true);
+ return sessionParams;
}
private static LayoutLog getLayoutLog() {
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java
index 6c16ed0..96ae523 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java
@@ -24,7 +24,9 @@
import com.android.ide.common.rendering.api.ParserFactory;
import com.android.ide.common.rendering.api.ResourceReference;
import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.rendering.api.SessionParams.Key;
import com.android.ide.common.resources.IntArrayWrapper;
+import com.android.layoutlib.bridge.android.RenderParamsFlags;
import com.android.resources.ResourceType;
import com.android.util.Pair;
import com.android.utils.ILogger;
@@ -176,4 +178,12 @@
}
};
}
+
+ @Override
+ public <T> T getFlag(Key<T> key) {
+ if (key.equals(RenderParamsFlags.FLAG_KEY_APPLICATION_PACKAGE)) {
+ return (T) PACKAGE_NAME;
+ }
+ return null;
+ }
}
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutPullParser.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutPullParser.java
index c79b662..1110494 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutPullParser.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutPullParser.java
@@ -56,9 +56,7 @@
public LayoutPullParser(File layoutFile) {
try {
init(new FileInputStream(layoutFile));
- } catch (XmlPullParserException e) {
- throw new IOError(e);
- } catch (FileNotFoundException e) {
+ } catch (XmlPullParserException | FileNotFoundException e) {
throw new IOError(e);
}
}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index f32bb76..483bddc 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -168,6 +168,7 @@
"android.content.res.Resources#getLayout",
"android.content.res.Resources#getResourceEntryName",
"android.content.res.Resources#getResourceName",
+ "android.content.res.Resources#getResourcePackageName",
"android.content.res.Resources#getResourceTypeName",
"android.content.res.Resources#getString",
"android.content.res.Resources#getStringArray",
@@ -301,6 +302,7 @@
"android.text.StaticLayout",
"android.util.PathParser",
"android.view.Display",
+ "com.android.internal.util.VirtualRefBasePtr",
"com.android.internal.view.animation.NativeInterpolatorFactoryHelper",
"libcore.icu.ICU",
};
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 1c7a308..6ca4393 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -149,9 +149,6 @@
void setAllowScansWithTraffic(int enabled);
int getAllowScansWithTraffic();
- void setHalBasedAutojoinOffload(int enabled);
- int getHalBasedAutojoinOffload();
-
boolean enableAutoJoinWhenAssociated(boolean enabled);
boolean getEnableAutoJoinWhenAssociated();
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index a9259fa..67cf107 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -56,6 +56,11 @@
*/
public int anqpDomainId;
+ /*
+ * This field is equivalent to the |flags|, rather than the |capabilities| field
+ * of the per-BSS scan results returned by WPA supplicant. See the definition of
+ * |struct wpa_bss| in wpa_supplicant/bss.h for more details.
+ */
/**
* Describes the authentication, key management, and encryption schemes
* supported by the access point.
@@ -211,6 +216,10 @@
/** {@hide} */
public static final long FLAG_80211mc_RESPONDER = 0x0000000000000002;
+ /*
+ * These flags are specific to the ScanResult class, and are not related to the |flags|
+ * field of the per-BSS scan results from WPA supplicant.
+ */
/**
* Defines flags; such as {@link #FLAG_PASSPOINT_NETWORK}.
* {@hide}
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java b/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
index 035317e..4c38c9b 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
+++ b/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
@@ -19,6 +19,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Arrays;
+
/**
* Record of energy and activity information from controller and
* underlying wifi stack state. Timestamp the record with elapsed
@@ -44,6 +46,11 @@
/**
* @hide
*/
+ public long[] mControllerTxTimePerLevelMs;
+
+ /**
+ * @hide
+ */
public long mControllerRxTimeMs;
/**
@@ -62,10 +69,12 @@
public static final int STACK_STATE_STATE_IDLE = 3;
public WifiActivityEnergyInfo(long timestamp, int stackState,
- long txTime, long rxTime, long idleTime, long energyUsed) {
+ long txTime, long[] txTimePerLevel, long rxTime, long idleTime,
+ long energyUsed) {
mTimestamp = timestamp;
mStackState = stackState;
mControllerTxTimeMs = txTime;
+ mControllerTxTimePerLevelMs = txTimePerLevel;
mControllerRxTimeMs = rxTime;
mControllerIdleTimeMs = idleTime;
mControllerEnergyUsed = energyUsed;
@@ -77,6 +86,7 @@
+ " timestamp=" + mTimestamp
+ " mStackState=" + mStackState
+ " mControllerTxTimeMs=" + mControllerTxTimeMs
+ + " mControllerTxTimePerLevelMs=" + Arrays.toString(mControllerTxTimePerLevelMs)
+ " mControllerRxTimeMs=" + mControllerRxTimeMs
+ " mControllerIdleTimeMs=" + mControllerIdleTimeMs
+ " mControllerEnergyUsed=" + mControllerEnergyUsed
@@ -89,11 +99,12 @@
long timestamp = in.readLong();
int stackState = in.readInt();
long txTime = in.readLong();
+ long[] txTimePerLevel = in.createLongArray();
long rxTime = in.readLong();
long idleTime = in.readLong();
long energyUsed = in.readLong();
return new WifiActivityEnergyInfo(timestamp, stackState,
- txTime, rxTime, idleTime, energyUsed);
+ txTime, txTimePerLevel, rxTime, idleTime, energyUsed);
}
public WifiActivityEnergyInfo[] newArray(int size) {
return new WifiActivityEnergyInfo[size];
@@ -104,6 +115,7 @@
out.writeLong(mTimestamp);
out.writeInt(mStackState);
out.writeLong(mControllerTxTimeMs);
+ out.writeLongArray(mControllerTxTimePerLevelMs);
out.writeLong(mControllerRxTimeMs);
out.writeLong(mControllerIdleTimeMs);
out.writeLong(mControllerEnergyUsed);
@@ -128,6 +140,16 @@
}
/**
+ * @return tx time at power level provided in ms
+ */
+ public long getControllerTxTimeMillisAtLevel(int level) {
+ if (level < mControllerTxTimePerLevelMs.length) {
+ return mControllerTxTimePerLevelMs[level];
+ }
+ return 0;
+ }
+
+ /**
* @return rx time in ms
*/
public long getControllerRxTimeMillis() {
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 7dc8049..fb2bdd4 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -684,6 +684,13 @@
/**
* @hide
+ * A hint about whether or not the network represented by this WifiConfiguration
+ * is metered.
+ */
+ public boolean meteredHint;
+
+ /**
+ * @hide
* Number of time the scorer overrode a the priority based choice, when comparing two
* WifiConfigurations, note that since comparing WifiConfiguration happens very often
* potentially at every scan, this number might become very large, even on an idle
@@ -931,6 +938,15 @@
private boolean mSeenInLastQualifiedNetworkSelection;
/**
+ * Boolean indicating if we have ever successfully connected to this network.
+ *
+ * This value will be set to true upon a successful connection.
+ * This value will be set to false if a previous value was not stored in the config or if
+ * the credentials are updated (ex. a password change).
+ */
+ private boolean mHasEverConnected;
+
+ /**
* set whether this network is visible in latest Qualified Network Selection
* @param seen value set to candidate
*/
@@ -1020,7 +1036,18 @@
return QUALITY_NETWORK_SELECTION_STATUS[mStatus];
}
- private NetworkSelectionStatus() {};
+ public void setHasEverConnected(boolean value) {
+ mHasEverConnected = value;
+ }
+
+ public boolean getHasEverConnected() {
+ return mHasEverConnected;
+ }
+
+ private NetworkSelectionStatus() {
+ // previously stored configs will not have this parameter, so we default to false.
+ mHasEverConnected = false;
+ };
/**
* @param reason specific error reason
@@ -1219,6 +1246,7 @@
mNetworkSelectionBSSID = source.mNetworkSelectionBSSID;
setConnectChoice(source.getConnectChoice());
setConnectChoiceTimestamp(source.getConnectChoiceTimestamp());
+ setHasEverConnected(source.getHasEverConnected());
}
public void writeToParcel(Parcel dest) {
@@ -1237,6 +1265,7 @@
} else {
dest.writeInt(CONNECT_CHOICE_NOT_EXISTS);
}
+ dest.writeInt(getHasEverConnected() ? 1 : 0);
}
public void readFromParcel(Parcel in) {
@@ -1255,6 +1284,7 @@
setConnectChoice(null);
setConnectChoiceTimestamp(INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP);
}
+ setHasEverConnected(in.readInt() != 0);
}
}
@@ -1302,6 +1332,7 @@
selfAdded = false;
didSelfAdd = false;
ephemeral = false;
+ meteredHint = false;
validatedInternetAccess = false;
mIpConfiguration = new IpConfiguration();
lastUpdateUid = -1;
@@ -1381,6 +1412,8 @@
sbuf.append(" connect choice set time: ").append(mNetworkSelectionStatus
.getConnectChoiceTimestamp());
}
+ sbuf.append(" hasEverConnected: ")
+ .append(mNetworkSelectionStatus.getHasEverConnected()).append("\n");
if (this.numAssociation > 0) {
sbuf.append(" numAssociation ").append(this.numAssociation).append("\n");
@@ -1399,7 +1432,9 @@
if (this.selfAdded) sbuf.append(" selfAdded");
if (this.validatedInternetAccess) sbuf.append(" validatedInternetAccess");
if (this.ephemeral) sbuf.append(" ephemeral");
- if (this.didSelfAdd || this.selfAdded || this.validatedInternetAccess || this.ephemeral) {
+ if (this.meteredHint) sbuf.append(" meteredHint");
+ if (this.didSelfAdd || this.selfAdded || this.validatedInternetAccess
+ || this.ephemeral || this.meteredHint) {
sbuf.append("\n");
}
sbuf.append(" KeyMgmt:");
@@ -1546,18 +1581,6 @@
return sbuf.toString();
}
- /**
- * Construct a WifiConfiguration from a scanned network
- * @param scannedAP the scan result used to construct the config entry
- * TODO: figure out whether this is a useful way to construct a new entry.
- *
- public WifiConfiguration(ScanResult scannedAP) {
- networkId = -1;
- SSID = scannedAP.SSID;
- BSSID = scannedAP.BSSID;
- }
- */
-
/** {@hide} */
public String getPrintableSsid() {
if (SSID == null) return "";
@@ -1832,6 +1855,7 @@
selfAdded = source.selfAdded;
validatedInternetAccess = source.validatedInternetAccess;
ephemeral = source.ephemeral;
+ meteredHint = source.meteredHint;
if (source.visibility != null) {
visibility = new Visibility(source.visibility);
}
@@ -1870,11 +1894,6 @@
}
}
- /** {@hide} */
- //public static final int NOTHING_TAG = 0;
- /** {@hide} */
- //public static final int SCAN_CACHE_TAG = 1;
-
/** Implement the Parcelable interface {@hide} */
@Override
public void writeToParcel(Parcel dest, int flags) {
@@ -1916,6 +1935,7 @@
dest.writeInt(didSelfAdd ? 1 : 0);
dest.writeInt(validatedInternetAccess ? 1 : 0);
dest.writeInt(ephemeral ? 1 : 0);
+ dest.writeInt(meteredHint ? 1 : 0);
dest.writeInt(creatorUid);
dest.writeInt(lastConnectUid);
dest.writeInt(lastUpdateUid);
@@ -1985,6 +2005,7 @@
config.didSelfAdd = in.readInt() != 0;
config.validatedInternetAccess = in.readInt() != 0;
config.ephemeral = in.readInt() != 0;
+ config.meteredHint = in.readInt() != 0;
config.creatorUid = in.readInt();
config.lastConnectUid = in.readInt();
config.lastUpdateUid = in.readInt();
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index 9f8af6e..8d5efba 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -320,7 +320,8 @@
if (!TextUtils.isEmpty(unicode)) {
return "\"" + unicode + "\"";
} else {
- return mWifiSsid.getHexString();
+ String hex = mWifiSsid.getHexString();
+ return (hex != null) ? hex : WifiSsid.NONE;
}
}
return WifiSsid.NONE;
diff --git a/wifi/java/android/net/wifi/WifiLinkLayerStats.java b/wifi/java/android/net/wifi/WifiLinkLayerStats.java
index 1de4fd8..edd400b 100644
--- a/wifi/java/android/net/wifi/WifiLinkLayerStats.java
+++ b/wifi/java/android/net/wifi/WifiLinkLayerStats.java
@@ -19,6 +19,8 @@
import android.os.Parcelable;
import android.os.Parcel;
+import java.util.Arrays;
+
/**
* A class representing link layer statistics collected over a Wifi Interface.
*/
@@ -101,6 +103,8 @@
/** {@hide} */
public int tx_time;
/** {@hide} */
+ public int[] tx_time_per_level;
+ /** {@hide} */
public int rx_time;
/** {@hide} */
public int on_time_scan;
@@ -141,9 +145,10 @@
.append(" lost=").append(Long.toString(this.lostmpdu_vo))
.append(" retries=").append(Long.toString(this.retries_vo)).append('\n');
sbuf.append(" on_time : ").append(Integer.toString(this.on_time))
- .append(" tx_time=").append(Integer.toString(this.tx_time))
.append(" rx_time=").append(Integer.toString(this.rx_time))
- .append(" scan_time=").append(Integer.toString(this.on_time_scan)).append('\n');
+ .append(" scan_time=").append(Integer.toString(this.on_time_scan)).append('\n')
+ .append(" tx_time=").append(Integer.toString(this.tx_time))
+ .append(" tx_time_per_level=" + Arrays.toString(tx_time_per_level));
return sbuf.toString();
}
@@ -179,6 +184,7 @@
dest.writeString(BSSID);
dest.writeInt(on_time);
dest.writeInt(tx_time);
+ dest.writeIntArray(tx_time_per_level);
dest.writeInt(rx_time);
dest.writeInt(on_time_scan);
}
@@ -192,6 +198,7 @@
stats.BSSID = in.readString();
stats.on_time = in.readInt();
stats.tx_time = in.readInt();
+ stats.tx_time_per_level = in.createIntArray();
stats.rx_time = in.readInt();
stats.on_time_scan = in.readInt();
return stats;
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 8c1fbc3..6653a8d 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -28,7 +28,6 @@
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
@@ -43,7 +42,6 @@
import com.android.server.net.NetworkPinner;
import java.net.InetAddress;
-import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
@@ -2721,27 +2719,4 @@
throw e.rethrowFromSystemServer();
}
}
- /**
- * Set setting for enabling autojoin Offload thru Wifi HAL layer
- * @hide
- */
- public void setHalBasedAutojoinOffload(int enabled) {
- try {
- mService.setHalBasedAutojoinOffload(enabled);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Get setting for enabling autojoin Offload thru Wifi HAL layer
- * @hide
- */
- public int getHalBasedAutojoinOffload() {
- try {
- return mService.getHalBasedAutojoinOffload();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
}
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index c5e7bff..73ddbbc 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -27,6 +27,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
+import android.os.WorkSource;
import android.util.Log;
import android.util.SparseArray;
@@ -160,6 +161,11 @@
*/
public static final int REPORT_EVENT_NO_BATCH = (1 << 2);
+
+ /** {@hide} */
+ public static final String SCAN_PARAMS_SCAN_SETTINGS_KEY = "ScanSettings";
+ /** {@hide} */
+ public static final String SCAN_PARAMS_WORK_SOURCE_KEY = "WorkSource";
/**
* scan configuration parameters to be sent to {@link #startBackgroundScan}
*/
@@ -277,6 +283,12 @@
* non-zero => scan was truncated, so results may not be complete
*/
private int mFlags;
+ /**
+ * Indicates the buckets that were scanned to generate these results.
+ * This is not relevant to WifiScanner API users and is used internally.
+ * {@hide}
+ */
+ private int mBucketsScanned;
/** all scan results discovered in this scan, sorted by timestamp in ascending order */
private ScanResult mResults[];
@@ -288,9 +300,18 @@
mResults = results;
}
+ /** {@hide} */
+ public ScanData(int id, int flags, int bucketsScanned, ScanResult[] results) {
+ mId = id;
+ mFlags = flags;
+ mBucketsScanned = bucketsScanned;
+ mResults = results;
+ }
+
public ScanData(ScanData s) {
mId = s.mId;
mFlags = s.mFlags;
+ mBucketsScanned = s.mBucketsScanned;
mResults = new ScanResult[s.mResults.length];
for (int i = 0; i < s.mResults.length; i++) {
ScanResult result = s.mResults[i];
@@ -307,6 +328,11 @@
return mFlags;
}
+ /** {@hide} */
+ public int getBucketsScanned() {
+ return mBucketsScanned;
+ }
+
public ScanResult[] getResults() {
return mResults;
}
@@ -321,6 +347,7 @@
if (mResults != null) {
dest.writeInt(mId);
dest.writeInt(mFlags);
+ dest.writeInt(mBucketsScanned);
dest.writeInt(mResults.length);
for (int i = 0; i < mResults.length; i++) {
ScanResult result = mResults[i];
@@ -337,12 +364,13 @@
public ScanData createFromParcel(Parcel in) {
int id = in.readInt();
int flags = in.readInt();
+ int bucketsScanned = in.readInt();
int n = in.readInt();
ScanResult results[] = new ScanResult[n];
for (int i = 0; i < n; i++) {
results[i] = ScanResult.CREATOR.createFromParcel(in);
}
- return new ScanData(id, flags, results);
+ return new ScanData(id, flags, bucketsScanned, results);
}
public ScanData[] newArray(int size) {
@@ -639,12 +667,29 @@
* scans should also not share this object.
*/
public void startBackgroundScan(ScanSettings settings, ScanListener listener) {
+ startBackgroundScan(settings, listener, null);
+ }
+
+ /** start wifi scan in background
+ * @param settings specifies various parameters for the scan; for more information look at
+ * {@link ScanSettings}
+ * @param workSource WorkSource to blame for power usage
+ * @param listener specifies the object to report events to. This object is also treated as a
+ * key for this scan, and must also be specified to cancel the scan. Multiple
+ * scans should also not share this object.
+ */
+ public void startBackgroundScan(ScanSettings settings, ScanListener listener,
+ WorkSource workSource) {
Preconditions.checkNotNull(listener, "listener cannot be null");
int key = addListener(listener);
if (key == INVALID_KEY) return;
validateChannel();
- sAsyncChannel.sendMessage(CMD_START_BACKGROUND_SCAN, 0, key, settings);
+ Bundle scanParams = new Bundle();
+ scanParams.putParcelable(SCAN_PARAMS_SCAN_SETTINGS_KEY, settings);
+ scanParams.putParcelable(SCAN_PARAMS_WORK_SOURCE_KEY, workSource);
+ sAsyncChannel.sendMessage(CMD_START_BACKGROUND_SCAN, 0, key, scanParams);
}
+
/**
* stop an ongoing wifi scan
* @param listener specifies which scan to cancel; must be same object as passed in {@link
@@ -676,11 +721,27 @@
* scans should also not share this object.
*/
public void startScan(ScanSettings settings, ScanListener listener) {
+ startScan(settings, listener, null);
+ }
+
+ /**
+ * starts a single scan and reports results asynchronously
+ * @param settings specifies various parameters for the scan; for more information look at
+ * {@link ScanSettings}
+ * @param workSource WorkSource to blame for power usage
+ * @param listener specifies the object to report events to. This object is also treated as a
+ * key for this scan, and must also be specified to cancel the scan. Multiple
+ * scans should also not share this object.
+ */
+ public void startScan(ScanSettings settings, ScanListener listener, WorkSource workSource) {
Preconditions.checkNotNull(listener, "listener cannot be null");
int key = addListener(listener);
if (key == INVALID_KEY) return;
validateChannel();
- sAsyncChannel.sendMessage(CMD_START_SINGLE_SCAN, 0, key, settings);
+ Bundle scanParams = new Bundle();
+ scanParams.putParcelable(SCAN_PARAMS_SCAN_SETTINGS_KEY, settings);
+ scanParams.putParcelable(SCAN_PARAMS_WORK_SOURCE_KEY, workSource);
+ sAsyncChannel.sendMessage(CMD_START_SINGLE_SCAN, 0, key, scanParams);
}
/**
@@ -699,7 +760,6 @@
private void startPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings, int key) {
// Bundle up both the settings and send it across.
Bundle pnoParams = new Bundle();
- if (pnoParams == null) return;
// Set the PNO scan flag.
scanSettings.isPnoScan = true;
pnoParams.putParcelable(PNO_PARAMS_SCAN_SETTINGS_KEY, scanSettings);
diff --git a/wifi/java/android/net/wifi/WifiSsid.java b/wifi/java/android/net/wifi/WifiSsid.java
index f8ba95d..c53cd3c 100644
--- a/wifi/java/android/net/wifi/WifiSsid.java
+++ b/wifi/java/android/net/wifi/WifiSsid.java
@@ -205,7 +205,7 @@
for (int i = 0; i < octets.size(); i++) {
out += String.format(Locale.US, "%02x", ssidbytes[i]);
}
- return out;
+ return (octets.size() > 0) ? out : null;
}
/** Implement the Parcelable interface {@hide} */